tag style ejemplo atributos class-design code-complete

class design - style - Clases para evitar(código completo)



html h2 h3 (10)

Aquí hay una versión clásica de ''verbo vs sustantivo'' en OO:

http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-nouns.html

Estoy algo confundido acerca de un párrafo en el libro completo del código.

En la sección "Clases para evitar" dice:

"Evite las clases con nombres de verbos Una clase que solo tiene comportamiento pero no contiene datos generalmente no es realmente una clase. Considere convertir una clase como DatabaseInitialization () o StringBuilder () en una rutina en alguna otra clase"

Mi código consiste principalmente en clases de verbos sin datos. Hay lectores de facturas, calculadores de precios, generadores de mensajes, etc. Hago esto para concentrar las clases en una tarea cada una. Luego agrego dependencias a otras clases para otras funcionalidades.

Si entiendo el párrafo correctamente debería usar código como

class Webservice : IInvoiceReader, IArticleReader { public IList<Invoice> GetInvoices(); public IList<Article> GetArticles(); }

más bien que

class InvoiceReader : IInvoiceReader { public InvoiceReader(IDataProvider dataProvider); public IList<Invoice> GetInvoices(); } class ArticleReader : IArticleReader { public ArticleReader(IDataProvider dataProvider); public IList<Article> GetArticles(); }

Editar Gracias por todas las respuestas.

Mi conclusión es que mi código actual es más SRP que OO, pero que también sufre del "modelo de dominio anémico".

Estoy seguro de que estas ideas me ayudarán en el futuro.


Céntrese menos en el nombre. La regla sobre el nombre es solo un indicador de regla general para una mala práctica. El punto importante es este:

Una clase que solo tiene comportamiento pero no tiene datos generalmente no es realmente una clase

En su caso, parece que sus clases tienen datos y comportamiento, y que también pueden llamarse "Factura" y "Artículo".


Creo que el libro sugiere un diseño como el siguiente:

class Article : IIArticleReader { // Article data... public IList<Article> GetArticles(); }


Depende. Existen muchas clases con el nombre de verbos de lectura y escritura porque las clases también crean, mantienen y representan una conexión con la fuente de datos desde la que están leyendo o escribiendo. Si tus clases lo hacen, probablemente sea mejor mantenerlos separados.

Si los objetos Reader solo contienen lógica de análisis, entonces convertir las clases en métodos de utilidad es el camino a seguir. Me gustaría ir con un nombre más descriptivo que el servicio web.


El enunciado Una clase que solo tiene comportamiento pero no tiene datos generalmente no es realmente una clase. es completamente incorrecto

Extraer el comportamiento en una clase separada es algo bueno y común de hacer en la refactorización. Puede tener estado, pero tampoco necesita tener uno. Necesita tener interfaces limpias e implementarlas de cualquier forma que considere necesaria.

Además, las clases sin estado son excelentes para los cálculos que necesita solo por un corto período de tiempo. Los crea (o solicita una Fábrica de algún tipo para obtenerlos), hace los cálculos necesarios y luego los tira a la basura. Puede tener la "versión" adecuada de su comportamiento disponible en cualquier lugar, en cualquier momento.

Usualmente encuentro que diferentes implementaciones de una interfaz tienen algún estado (establecido en constructor, por ejemplo), pero a veces el tipo de tu clase puede determinar su comportamiento por completo.

Por ejemplo:

public interface IExporter { /// <summary> /// Transforms the specified export data into a text stream. /// </summary> /// <param name="exportData">The export data.</param> /// <param name="outputFile">The output file.</param> void Transform(IExportData exportData, string outputFile); }

puede ser implementado como

class TabDelimitedExporter : IExporter { ... } class CsvExporter : IExporter { ... } class ExcelExporter : IExporter { ... }

Para implementar la exportación desde IExportData (cualquiera que sea) a un archivo CSV, probablemente no necesite ningún estado. ExcelExporter , por otro lado, podría tener varias propiedades para las opciones de exportación, pero también podría ser apátrida.

[Editar]

Mover GetInvoices y GetArticles a la clase WebService significa que vinculará su implementación al tipo de WebService. Tenerlos en clases separadas le permitirá tener diferentes implementaciones tanto para facturas como para artículos. En general, parece mejor tenerlos separados.


Esencialmente, lo que el libro está diciendo es que el diseño OO se trata de extraer los objetos (los sustantivos) e identificar las operaciones (los verbos) que ocurren en y entre esos objetos.

Los sustantivos se convierten en objetos, los verbos se convierten en los métodos que operan en estos objetos.

La idea es que el

más cerca el programa modela el problema del mundo real, mejor será el programa.

En términos prácticos, lo útil con un objeto es que puede representar un estado particular. Luego puede tener varias instancias diferentes de esta clase, cada una con un estado diferente para representar algún aspecto del problema.

En el caso de la clase InvoiceReader

  • solo vas a crear una instancia
  • el único estado que representa es el de contener un dataProvider
  • solo contiene un método

no hay ventaja de colocarlo en un objeto.


Ignoro esta "regla", personalmente. .NET Framework está lleno de clases de "verbos": TextReader , BinaryWriter , XmlSerializer , CodeGenerator , StringEnumerator , HttpListener , TraceListener , ConfigurationManager , TypeConverter , RoleProvider ... si cree que el Framework está mal diseñado, entonces por RoleProvider , no use nombres como estos

La intención de Steve es comprensible. Si te encuentras creando docenas y docenas de clases solo para realizar una tarea específica, es probable que sea un signo de un modelo de dominio anémico , donde los objetos que deberían poder hacer estas cosas por sí mismos no lo son. Pero en algún momento debe elegir entre un OOP "puro" y el SRP .

Mi sugerencia sería esta: si te encuentras creando una clase de "verbo" que actúa en una sola clase de "sustantivo", piensa honestamente si la clase "sustantivo" puede o no realizar la acción por sí misma. Pero no empieces a crear Objetos de Dios ni propongas nombres sin sentido o engañosos con el único propósito de evitar la clase de los verbos.


Los nombres de clases, como el lector de facturas, PriceCalculator, MessageBuilder, ArticleReader, InvoiceReader no son realmente nombres de verbos. En realidad, son nombres de clase "nombre-agente-sustantivo". Ver sustantivos del agente .

Un nombre de clase de verbo sería algo así como Validate, Operate, Manage, etc. Obviamente, estos se usan mejor como métodos y serían bastante incómodos como nombres de clase.

El mayor problema con los nombres de clase "nombre-agente-sustantivo" es que pueden dar muy poco significado a lo que la clase realmente hace (por ejemplo, UserManager, DataProcessor, etc.). Como resultado, es más probable que estén hinchados y pierdan la cohesión interna. (Ver el Principio de Responsabilidad Individual ).

Por lo tanto, la clase WebService con las interfaces IInvoiceReader y IArticleReader es probablemente el diseño de OO más claro y significativo.

Esto le da el nombre de clase simple y obvio "WebService", junto con los nombres de la interfaz "sustantivo agente-nombre" que anuncian claramente lo que la clase WebService puede hacer por los llamantes.

Probablemente también podría darle más significado a la clase real prefijando otro nombre, por ejemplo PaymentWebService.

Sin embargo, las interfaces son siempre mejores que un solo nombre de clase al describir más específicamente lo que la clase puede hacer por los llamantes. A medida que la clase se vuelve más compleja, también se pueden agregar nuevas interfaces con nombres significativos.


Tenga en cuenta el uso de la palabra "Evitar". No es eliminar, ni erradicar, ni quemar en el infierno si alguna vez los usas.

Lo que el autor quiso decir es que si te encuentras con un montón de clases con todos los nombres de verbos y todo lo que haces es crear estáticamente esas clases, llamar a una función y olvidarte de ellas, es probable que sea una señal de que estás separando un poco demasiadas preocupaciones de clase.

Sin embargo, hay situaciones en las que la creación de clases para implementar una acción es algo bueno, como cuando tienes diferentes estrategias para la misma acción. Un muy buen ejemplo es IComparer <>. Todo lo que hace es comparar dos cosas, pero hay varias formas de comparar cosas.

Como sugirió el autor, en esos casos, una buena forma de hacerlo es crear una interfaz e implementarla. IComparer <> viene a la mente de nuevo.

Otra situación común es cuando la acción tiene un estado pesado, como la carga de archivos. Podría justificarse encapsular el estado en una clase.


No sigas ningún consejo a ciegas. Estas son solo pautas.

Dicho esto, los sustantivos son muy buenos nombres de clase , siempre que modelen objetos lógicos. Como la clase "Persona" es un diseño para todos los objetos "Persona", llamarla "Persona" es muy útil, porque le permite razonar así: "Crearé una Persona a partir de la información del usuario, pero primero necesito para validarlo ... "