2009-05-09 17 views
16

Después de leer muchos blogs, entradas en el foro y varios documentos de Apple, Todavía no sé si hacer una subclasificación exhaustiva en Objective-C es una buena idea o no.¿Subclases en Objective-C son una mala práctica?

Tomemos como ejemplo el siguiente caso:

decir que estoy desarrollando un juego de puzzles que tiene una gran cantidad de elementos. Todos esos elementos comparten una cierta cantidad del mismo comportamiento . Luego, dentro de mi colección de elementos, diferentes grupos de elementos comparten iguales comportamiento , grupos distintivos de grupos, etc ...

Así que, después de determinar lo que hereda de lo , decidí subclase cabo del olvido. ¿Y por qué no debería? Teniendo en cuenta la facilidad de ajuste general comportamiento toma con este modelo, I creo que logré algo que OOP es destinado para.

Pero, - y esta es la fuente de mi pregunta - Apple menciona usando delegados, métodos de origen de datos y protocolos informales a favor de la creación de subclases. Realmente me da vueltas la cabeza ¿por qué?

Parece que hay dos campos. Los que están a favor de la subclasificación, aquellos en fafor de no. Depende del gusto personal aparentemente. Me pregunto cuáles son los pros y los contras de la subclasificación masiva y no la subclasificación masiva?

Para concluir, mi pregunta es simple: ¿Estoy en lo cierto? ¿Y porqué o porqué no?

+10

Kriem, Apple está hablando sobre el uso de cacao en general, nada que ver con Objective-C. Para cada proyecto individual, tendrá que decidir cómo configurar mejor su aplicación y base de código. En el caso de Apple, han configurado Cocoa (especialmente AppKit/UIKit) usando paradigmas MVC e IoC, por lo que sugieren que no subclases cosas como NSControl, etc. usted mismo cuando podría usar delegados en su lugar. En resumen: esta advertencia es específicamente para el marco de Cocoa, no para Objecive-C en general. –

+0

@Jason: Esta es una muy buena respuesta, ¿por qué dejarlo como un simple comentario? – mouviciel

+0

@mouviciel - Supongo que no pensé en eso. Ahora hay una serie de respuestas que básicamente dicen lo mismo, por lo que no parece que valga la pena agregar más ruido :) –

Respuesta

10

Personalmente, sigo esta regla: puedo crear una subclase si respeta el Liskov substitution principle.

+0

Buena información. Creo que se aplica a mi caso. Pero, y esta es la fuente de mi pregunta, Apple menciona el uso de delegados, métodos de fuentes de datos y protocolos informales a favor de la creación de subclases. Realmente me da vueltas la cabeza ¿por qué? - Editaré la pregunta para enfatizar esto. – Kriem

+1

Sospecho que es mucho más simple usar delegados en la mayoría de los casos, de ahí la recomendación de Apple. Las subclases pueden ser apropiadas, pero sería más difícil hacerlo bien. Solo es mi opinión. –

+1

Creo que esa definición es terriblemente genérica. Si lees el mathese, lo que básicamente está diciendo es que cualquier cosa que use la superclase ya debería poder usar la subclase sin cambios.Por lo tanto, en lugar de ayudar a comprender cuándo tener una subclase, simplemente se establece que debe evitar que las subclases rompan una clase. Esa definición también elimina por completo categorías comunes enteras de subclases, como superclases abstractas que nunca deben usarse directamente, solo subclasificadas ... –

4

En general, debe evitar la subclasificación de clases API si existen delegados, etc. que logren lo que desea hacer. En su propio código, la creación de subclases suele ser más agradable, pero realmente depende de sus objetivos, por ej. si proporciona una API, debe proporcionar una API basada en delegado en lugar de asumir la creación de subclases.

Al tratar con las API, la subclasificación tiene más errores potenciales, por ejemplo. si cualquier clase en la jerarquía de clases obtiene un nuevo método que tiene el mismo nombre que tu adición, haces cosas de interrupción. 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 creando una subclase, y que podría ser más eficiente, etc. pero su anulación lo ocultará.

3

Realmente creo que depende de lo que estás tratando de hacer. Si el juego de rompecabezas que describes en el ejemplo realmente tiene un conjunto de elementos únicos que comparten atributos comunes, y no hay clases proporcionadas, por ejemplo, "NSPuzzlePiece", que se ajusten a tus necesidades, entonces no veo un problema con subclases extensamente.

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 utiliza una tabla.Hay (y hablo aquí del SDK de iPhone, ya que ahí es donde tengo experiencia) una clase UITableView que hace todas las pequeñas sutilezas 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 para subclasificar completamente UITableView y redefinir o ampliar sus métodos para personalizar su comportamiento.

Conceptos similares para delegados y protocolos. Si puede adaptar sus ideas a las clases de Apple, generalmente es más fácil (y funcionará mejor) hacerlo y usar fuentes de datos, delegados y protocolos que crear sus propias subclases. Le ayuda a evitar trabajo extra y a perder 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 depuradoras; cuanto más pueda trabajar con ellos, menos errores tendrá su programa a largo plazo.

6

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 tocó 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 ha tenido problemas para extender una clase no trivial en un marco complejo, sabe que siempre hay una curva de aprendizaje. Además, los detalles de implementación de la superclase suelen "filtrarse" a la subclase, lo cual es un gran problema en el @ $ & para constructores de marcos.

Apple optó por utilizar la delegación en muchos casos para resolver estos problemas; 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 sencilla y una forma más flexible para agregar un comportamiento específico de la aplicación. Rara vez es necesario extender la aplicación UIA directamente.

En su caso, para su código específico de aplicación, utilice las técnicas con las que se sienta cómodo y que funcionen mejor para su diseño. La herencia es una gran herramienta cuando se usa apropiadamente.

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

7

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

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 los sigues (a veces el contrato no documentado). Además, considero que las jerarquías de clase andante con herencia de implementación son difíciles porque los métodos pueden anularse o implementarse en cualquier nivel. Finalmente, al crear subclases, solo puede ampliar una interfaz, no puede restringirla. Esto conduce 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 le permite al consumidor correr alrededor de la interfaz.

Otros han mencionado el Principio de sustitución Liskov. Creo que el uso de eso habría aclarado el problema java.util.Stack, pero también puede conducir a jerarquías de clase muy profundas con el fin de garantizar que las clases obtengan solo los métodos que se supone que deben tener.

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

Al final, sin embargo, en realidad no importa qué camino tomar. Ambos son perfectamente aceptables. En realidad, es más una cuestión de con qué te sientes más cómodo y de qué fomentan los marcos con los que trabajas. Como dice el viejo refrán: "Cuando estén en Roma hagan lo mismo que los romanos".

12

La delegación es un medio de utilizar la técnica de composición para reemplazar algunos aspectos de la codificación por los que de otra manera se establecería una subclase. Como tal, se reduce a la vieja pregunta de la tarea que se está realizando, que necesita una gran cosa que sabe hacer mucho, o si se tiene una red 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 los delegados pueden hacer) proporciona una gran flexibilidad de comportamiento y facilidad de codificación, volviendo al principio de sustitución de Liskov, cuando se subclasifica hay que tener cuidado de no hacer nada que un usuario de toda la clase encuentre inesperado. Pero si simplemente está haciendo un objeto de delegado, tiene mucho menos de lo que hacerse responsable, solo que los métodos de delegado que implementa hacen lo que exige ese protocolo, más allá de eso no le importa.

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

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

2

mi impresión del énfasis de ADC 'contra' 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 aka os9) cuando C++ era la interfaz principal de la mayoría de mac toolbox, subclassing era el estándar de facto para que un programador modificara el comportamiento de las características comunes del sistema operativo (y esto era a veces un dolor en el cuello y significaba que había que tener mucho cuidado de que todas las modificaciones se comportaran de manera predecible y no rompió ningún comportamiento estándar).

esto se dice, mi impresión de énfasis de ADC en contra de subclases es no poner adelante un caso para el diseño de jerarquía de clases de una aplicación sin herencia, pero en lugar señalar que en el nueva manera de hacer las cosas (es decir, OSX) hay en la mayoría de los casos medios más apropiados para personalizar el comportamiento estándar sin necesidad de subclases.

¡Así que, por supuesto, diseñe la arquitectura de su programa de rompecabezas de la forma más robusta posible, aprovechando la herencia como mejor le parezca!

¡esperamos ver su nueva y genial aplicación de rompecabezas!

| K <

+0

Esta es una idea interesante, pero de hecho los principales principios de diseño de Cocoa se establecieron mucho antes de que C++ se convirtiera en un lenguaje de programación común para Mac OS. Cocoa obtiene su énfasis en delegación y composición de NEXTSTEP, que fue diseñado de esa manera porque la gente a cargo pensó que la composición era una forma más confiable de construir sistemas de software grandes con componentes reutilizables. Y esa idea realmente se remonta a Smalltalk y al patrón de diseño Model-View-Conroller. –

+0

También hay algunos elementos de las lecciones dolorosas de las clases base frágiles que se aprendieron en el proyecto Taligent. Ese fue un proyecto conjunto de Apple-IBM que, en parte, intentó crear un marco general para el desarrollo de aplicaciones. Su metáfora de Gente, Lugares y Cosas vale la pena estudiar solo para conocer el diseño del marco. Ese proyecto fue probablemente la mayor demostración de cómo falla C++ como un lenguaje para el diseño del marco fundamental (lo digo como desarrollador profesional de C++). –

+0

Corrección secundaria, C++ nunca fue la interfaz principal para la mayoría de los MacOS Toolbox. Siempre fue C y Pascal. –

4

Por favor lea la documentación de Apple "Adding behavior to a Cocoa program"!. Debajo de "Heredando de una clase Cocoa" sección, vea el 2do párrafo.Apple menciona claramente que la Subclasificación es la forma principal de agregar comportamiento específico de la aplicación al marco (tenga en cuenta MARCO).
El patrón MVC no desaproba completamente el uso de subclases o subtipos. Al menos no he visto esta recomendación de Apple u otros (si me he perdido, no dude en dirigirme a la fuente correcta de información sobre esto). Si está subclasificando clases de api solo dentro de su aplicación, por favor, adelante, nadie lo detiene pero tenga cuidado de que no rompa el comportamiento de la clase/api como un todo. La creación de subclases es una excelente forma de ampliar la funcionalidad de la API de Framework. También vemos muchas subclases dentro de las API de Apple IOS framework. Como desarrollador, hay que tener cuidado de que la implementación esté bien documentada y no haya sido duplicada accidentalmente por otro desarrollador. Es otro juego de pelota si tu aplicación es un conjunto de clases API que planeas distribuir como componente reutilizable.

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

0

Apple parece desaconsejar pasivamente la creación de subclases con Objective-C.

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

Cuestiones relacionadas