Pregunta

Después de leer muchos blogs, entradas de foros y varios documentos de Apple, todavía no sé si una subclasificación extensa en Objective-C es una buena opción o no .

Tome por ejemplo el siguiente caso:

  

Digamos que estoy desarrollando un juego de rompecabezas que   tiene muchos elementos Todos esos   los elementos comparten una cierta cantidad de   mismo comportamiento Entonces, dentro de mi   colección de elementos, diferentes   grupos de elementos comparten igual   comportamiento, distinguiendo grupos de   grupos, etc ...

     

Entonces, después de determinar qué hereda   de qué, decidí subclasificar   del olvido ¿Y por qué no debería?   Teniendo en cuenta la facilidad de ajuste general   comportamiento lleva con este modelo, yo   creo que logré algo OOP es   hecho para.

Pero, y esta es la fuente de mi pregunta, Apple menciona el uso de delegados, métodos de fuente de datos y protocolos informales a favor de la subclasificación . Realmente me aturde la mente ¿por qué?

Parece que hay dos campos. Los que están a favor de la subclase, los que están a favor o no. Depende del gusto personal aparentemente. Me pregunto cuáles son los pros y los contras de subclasificar masivamente y no subclasificar masivamente

Para concluir, mi pregunta es simple: ¿Tengo razón? ¿Y por qué o por qué no?

¿Fue útil?

Solución

Personalmente, sigo esta regla: puedo crear una subclase si respeta el Principio de sustitución de Liskov .

Otros consejos

La delegación es un medio de usar la técnica de composición para reemplazar algunos aspectos de la codificación para los que de otro modo se subclasificaría. Como tal, se reduce a la antigua cuestión de la tarea en cuestión que necesita una gran cosa que sepa hacer mucho, o si tiene una red suelta de objetos especializados (un tipo de modelo de responsabilidad muy UNIX).

El uso de una combinación de delegados y protocolos (para definir lo que se supone que deben hacer los delegados) proporciona una gran flexibilidad de comportamiento y facilidad de codificación, volviendo al principio de sustitución de Liskov, cuando subclases tienes para tener cuidado de no hacer nada que un usuario de toda la clase encuentre inesperado. Pero si simplemente está haciendo un objeto delegado, entonces tiene mucho menos de qué ser responsable, solo que los métodos de delegado que implemente hacen lo que ese protocolo exige, más allá de eso no le importa.

Todavía hay muchas buenas razones para usar subclases, si realmente ha compartido comportamientos y variables entre varias clases, puede tener mucho sentido subclasificar. Pero si puede aprovechar el concepto de delegado, a menudo hará que sus clases sean más fáciles de extender o usar de formas que el diseñador puede no haber esperado.

Tiendo a ser más fanático de los protocolos formales que de los informales, porque no solo los protocolos formales se aseguran de que tengas los métodos que una clase te trata como un delegado, sino también porque la definición del protocolo es un lugar natural para documentar lo que espera de un delegado que implementa esos métodos.

La subclasificación tiene sus beneficios, pero también tiene algunos inconvenientes. Como regla general, trato de evitar la herencia de implementación y en su lugar uso la herencia y la delegación de interfaz.

Una de las razones por las que hago esto es porque cuando heredas la implementación, puedes terminar con problemas si anulas los métodos pero no te adhieres a ellos (a veces un contrato no documentado). Además, encuentro que las jerarquías de clases ambulantes con herencia de implementación son difíciles porque los métodos se pueden anular o implementar en cualquier nivel. Finalmente, cuando subclases solo puedes ampliar una interfaz, no puedes reducirla. Esto lleva a abstracciones con fugas. Un buen ejemplo de esto es java.util.Stack que extiende java.util.Vector. No debería poder tratar una pila como un Vector. Hacerlo solo permite al consumidor correr alrededor de la interfaz.

Otros han mencionado el Principio de sustitución de Liskov. Creo que usar eso ciertamente habría solucionado el problema java.util.Stack, pero también puede conducir a jerarquías de clase muy profundas para garantizar que las clases obtengan solo los métodos que se supone que tienen.

En cambio, con la herencia de interfaz no existe esencialmente una jerarquía de clases porque las interfaces rara vez necesitan extenderse entre sí. Las clases simplemente implementan las interfaces que necesitan y, por lo tanto, el consumidor puede tratarlas de la manera correcta. Además, debido a que no hay herencia de implementación, los consumidores de estas clases no inferirán su comportamiento debido a la experiencia previa con una clase principal.

Al final, sin embargo, realmente no importa en qué dirección vayas. Ambos son perfectamente aceptables. Realmente es más una cuestión de con qué se siente más cómodo y cuáles son los marcos con los que está trabajando. Como dice el viejo refrán: & Quot; Cuando en Roma haz lo que hacen los romanos. & Quot;

No hay nada de malo en usar la herencia en Objective-C. Apple lo usa bastante. Por ejemplo, en Cocoa-touch, el árbol de herencia de UIButton es UIControl: UIView: UIResponder: NSObject.

Creo que Martin dio en un punto importante al mencionar el principio de sustitución de Liskov. Además, el uso adecuado de la herencia requiere que el implementador de la subclase tenga un conocimiento profundo de la superclase. Si alguna vez has tenido problemas para extender una clase no trivial en un marco complejo, sabes que siempre hay una curva de aprendizaje. Además, los detalles de implementación de la superclase a menudo & Quot; se filtran a través de & Quot; a la subclase, que es un gran dolor en el amplificador @ $ &; para creadores de marcos.

Apple eligió usar la delegación en muchos casos para abordar estos problemas; Las clases no triviales como UIApplication exponen puntos de extensión comunes a través de un objeto delegado, por lo que la mayoría de los desarrolladores tienen una curva de aprendizaje más fácil y una forma más flexible de agregar un comportamiento específico de la aplicación; rara vez es necesario extender UIApplication directamente.

En su caso, para el código específico de su aplicación, use cualquier técnica con la que se sienta cómodo y funcione mejor para su diseño. La herencia es una gran herramienta cuando se usa apropiadamente.

Frecuentemente veo que los programadores de aplicaciones extraen lecciones de diseños de marcos e intentan aplicarlas a su código de aplicación (esto es común en los mundos Java, C ++ y Python, así como en Objective-C). Si bien es bueno pensar y comprender las elecciones que hicieron los diseñadores de marcos, esas lecciones no siempre se aplican al código de la aplicación.

En general, debe evitar subclasificar las clases de API si existen delegados, etc., que logran lo que desea hacer. En su propio código, la subclasificación a menudo es más agradable, pero realmente depende de sus objetivos, por ejemplo. si proporciona una API, debe proporcionar una API basada en delegado en lugar de asumir una subclase.

Cuando se trata de subclases de API tiene más errores potenciales, por ejemplo. si alguna clase en la jerarquía de clases obtiene un nuevo método que tiene el mismo nombre que tu adición, haces cosas de descanso. Y también, si proporciona una función de tipo útil / auxiliar, existe la posibilidad de que en el futuro se agregue algo similar a la clase real que estaba subclasificando, y que podría ser más eficiente, etc., pero su anulación lo ocultará.

Lea la documentación de Apple quot; Agregar comportamiento a un programa Cocoa " !. En la sección & Quot; Heredando de una clase de Cacao & Quot; , vea el segundo párrafo. Apple menciona claramente que la Subclasificación es la forma principal de agregar un comportamiento específico de la aplicación al marco (tenga en cuenta, MARCO ).
El patrón MVC no prohíbe completamente el uso de subclases o subtipos. Por lo menos, no he visto esta recomendación de Apple ni de otros (si me la he perdido, siéntase libre de señalarme la fuente correcta de información sobre esto). Si está subclasificando las clases de API solo dentro de su aplicación, continúe, nadie lo detendrá, pero tenga cuidado de que no interrumpa el comportamiento de la clase / API en su conjunto. La subclasificación es una excelente manera de extender la funcionalidad de la API del framework. También vemos muchas subclases dentro de las API de marco Apple IOS. Como desarrollador, uno debe tener cuidado de que la implementación esté bien documentada y no sea duplicada accidentalmente por otro desarrollador. Es otro juego de pelota por completo si su aplicación es un conjunto de clases de API que planea distribuir como componente reutilizable.

En mi humilde opinión, en lugar de preguntar cuál es la mejor práctica, primero lea detenidamente la documentación relacionada, impleméntela y pruébela. Haz tu propio juicio. Sabes mejor sobre lo que estás haciendo. Es fácil para otros (como yo y tantos otros) simplemente leer cosas de diferentes fuentes en la red y arrojar términos. Sé tu propio juez, me ha funcionado hasta ahora.

Realmente creo que depende de lo que intentes hacer. Si el juego de rompecabezas que describe en el ejemplo realmente tiene un conjunto de elementos únicos que comparten atributos comunes, y no hay clases proporcionadas, por ejemplo, & Quot; NSPuzzlePiece & Quot; - que se ajuste a sus necesidades, entonces no veo un problema con la subclasificación extensiva.

En mi experiencia, los delegados, los métodos de fuente de datos y los protocolos informales son mucho más útiles cuando Apple ha proporcionado una clase que ya hace algo parecido a lo que usted quiere que haga.

Por ejemplo, supongamos que está creando una aplicación que usa una tabla. Existe (y hablo aquí del iPhone SDK, ya que es allí donde tengo experiencia) una clase UITableView que hace todas las pequeñas ventajas de crear una tabla para la interacción con el usuario, y es mucho más eficiente definir una fuente de datos para un instancia de UITableView de lo que es subclasificar completamente UITableView y redefinir o extender sus métodos para personalizar su comportamiento.

Conceptos similares se aplican a los delegados y protocolos. Si puede adaptar sus ideas a las clases de Apple, entonces generalmente es más fácil (y funcionará sin problemas) hacerlo y usar una fuente de datos, delegados y protocolos que crear sus propias subclases. Le ayuda a evitar el trabajo extra y la pérdida de tiempo, y generalmente es menos propenso a errores. Las clases de Apple se han ocupado del negocio de hacer que las funciones sean eficientes y de depuración; cuanto más pueda trabajar con ellos, menos errores tendrá su programa a largo plazo.

mi impresión del énfasis de ADC en contra de la subclasificación tiene más que ver con el legado de cómo ha evolucionado el sistema operativo ... en el día (Mac Classic también conocido como os9) cuando c ++ era la interfaz principal para la mayoría de las mac caja de herramientas, la subclase era el estándar de facto para que un programador modificara el comportamiento de las características comunes del sistema operativo (y esto a veces era un dolor de cabeza y significaba que uno tenía que tener mucho cuidado de que todas las modificaciones se comportaran de manera predecible y no rompió ningún comportamiento estándar).

Dicho esto, MI IMPRESIÓN del énfasis de ADC contra las subclases es no presentar un caso para diseñar la jerarquía de clases de una aplicación sin herencia, PERO EN LUGAR para señalar que en la nueva forma de hacer las cosas (es decir, OSX) en la mayoría de los casos hay medios más apropiados para personalizar el comportamiento estándar sin necesidad de subclase.

¡Así que, por supuesto, diseña la arquitectura de tu programa de rompecabezas de la manera más sólida posible, aprovechando la herencia como mejor te parezca!

¡espero ver tu nueva y genial aplicación de rompecabezas!

| K <

Apple de hecho parece desalentar pasivamente la subclase con Objective-C.

Es un axioma del diseño OOP para favorecer la composición sobre la implementación.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top