¿Cuál es la idea detrás de nombrar clases con el sufijo "Info", por ejemplo: "SomeClass" y "SomeClassInfo"?

20

Estoy trabajando en un proyecto que trata con dispositivos físicos, y me ha confundido cómo nombrar correctamente algunas clases en este proyecto.

Teniendo en cuenta que los dispositivos reales (sensores y receptores) son una cosa, y su representación en el software es otra, estoy pensando en nombrar algunas clases con el patrón de nombre de sufijo "Info".

Por ejemplo, mientras que a Sensorsería una clase para representar el sensor real (cuando realmente está conectado a algún dispositivo en funcionamiento), SensorInfose usaría para representar solo las características de dicho sensor. Por ejemplo, al guardar el archivo, serializaría SensorInfoa en el encabezado del archivo, en lugar de serializar a Sensor, lo que no tendría sentido.

Pero ahora estoy confundido, porque hay un término medio en el ciclo de vida de los objetos en el que no puedo decidir si debo usar uno u otro, o cómo obtener uno de otro, o incluso si ambas variantes deberían colapsarse en una sola clase.

Además, la Employeeclase de ejemplo demasiado común obviamente es solo una representación de la persona real, pero nadie sugeriría nombrar la clase EmployeeInfo, hasta donde yo sé.

El lenguaje con el que estoy trabajando es .NET, y este patrón de nombres parece ser común en todo el marco, por ejemplo con estas clases:

  • Directoryy DirectoryInfoclases;
  • Filey FileInfoclases;
  • ConnectionInfoclase (sin Connectionclase correspondiente );
  • DeviceInfoclase (sin Deviceclase correspondiente );

Entonces mi pregunta es: ¿hay una razón común sobre el uso de este patrón de nombres? ¿Hay casos en los que tiene sentido tener pares de nombres ( Thingy ThingInfo) y otros casos en los que solo debería existir la ThingInfoclase, o la Thingclase, sin su contraparte?

heltonbiker
fuente
55
Ben Aaronson acertó en su respuesta a continuación; el Infosufijo distingue una staticclase que contiene métodos de utilidad de su contraparte con estado. No es una "mejor práctica" como tal; es solo una forma en que el equipo .NET ideó para resolver un problema en particular. Podrían haber encontrado fácilmente FileUtilityy File, pero, File.DoSomething()y FileInfo.FileNameparecen leer mejor.
Robert Harvey
1
Vale la pena señalar que este es un patrón común, pero con tantas convenciones de nomenclatura diferentes como idiomas y API. En Java, por ejemplo, si tiene una clase con estado Foo, es posible que tenga una clase de utilidad no instanciable Foos. Cuando se trata de nombrar, lo importante es la coherencia dentro de la API, e idealmente entre las API de la plataforma.
Kevin Krumwiede
1
De Verdad? "Nadie sugeriría nombrar la clase EmployeeInfo" ?? ¿NADIE? Eso parece un poco fuerte para algo no tan obvio.
ThePopMachine
@ThePopMachine Estoy de acuerdo con que la palabra "nadie" podría ser demasiado difícil en este caso, pero Employeedocenas encuentran ejemplos, ya sea en línea o en libros clásicos, aunque todavía no los he visto EmployeeInfo(quizás porque un empleado es un ser vivo, no una construcción técnica como una conexión o un archivo). Pero, de acuerdo, si la clase EmployeeInfofuera propuesta en un proyecto, creo que podría ser útil.
Heltonbiker

Respuestas:

21

Creo que "información" es un nombre inapropiado. Los objetos tienen estado y acciones: "información" es solo otro nombre para "estado" que ya está integrado en OOP.

¿Qué estás tratando realmente de modelar aquí? Necesita un objeto que represente el hardware en el software para que otro código pueda usarlo.

Eso es fácil de decir, pero como descubriste, hay más que eso. La "representación de hardware" es sorprendentemente amplia. Un objeto que hace eso tiene varias preocupaciones:

  • Comunicación de dispositivos de bajo nivel, ya sea hablando con la interfaz USB, un puerto serie, TCP / IP o una conexión patentada.
  • Estado de gestión. ¿Está encendido el dispositivo? ¿Listo para hablar con el software? ¿Ocupado?
  • Manejo de eventos. El dispositivo produjo datos: ahora necesitamos generar eventos para pasar a otras clases que estén interesadas.

Ciertos dispositivos, como los sensores, tendrán menos preocupaciones que, por ejemplo, un dispositivo multifunción de impresora / escáner / fax. Es probable que un sensor solo produzca un flujo de bits, mientras que un dispositivo complejo puede tener protocolos e interacciones complejas.

De todos modos, volviendo a su pregunta específica, hay varias formas de hacerlo dependiendo de sus requisitos específicos, así como de la complejidad de la interacción del hardware.

Aquí hay un ejemplo de cómo diseñaría la jerarquía de clases para un sensor de temperatura:

  • ITemperatureSource: interfaz que representa cualquier cosa que pueda producir datos de temperatura: un sensor, incluso podría ser un contenedor de archivos o datos codificados (piense: prueba simulada).

  • Acme4680Sensor: sensor ACME modelo 4680 (ideal para detectar cuando el Roadrunner está cerca). Esto puede implementar múltiples interfaces: tal vez este sensor detecta tanto la temperatura como la humedad. Este objeto contiene un estado de nivel de programa como "¿está conectado el sensor?" y "¿cuál fue la última lectura?"

  • Acme4680SensorComm: se utiliza únicamente para comunicarse con el dispositivo físico. No mantiene mucho estado. Se utiliza para enviar y recibir mensajes. Tiene un método C # para cada uno de los mensajes que comprende el hardware.

  • HardwareManager: utilizado para obtener dispositivos. Esto es esencialmente una fábrica que almacena instancias en caché: solo debe haber una instancia de un objeto de dispositivo para cada dispositivo de hardware. Tiene que ser lo suficientemente inteligente como para saber que si el hilo A solicita el sensor de temperatura ACME y el hilo B solicita el sensor de humedad ACME, estos son en realidad el mismo objeto y deben devolverse a ambos hilos.


En el nivel superior tendrá interfaces para cada tipo de hardware. Describen las acciones que su código C # tomaría en los dispositivos, utilizando tipos de datos C # (no, por ejemplo, conjuntos de bytes que el controlador de dispositivo sin formato podría usar).

En el mismo nivel, tiene una clase de enumeración con una instancia para cada tipo de hardware. El sensor de temperatura puede ser de un tipo, el sensor de humedad de otro.

Un nivel por debajo de esto son las clases reales que implementan esas interfaces: representan un dispositivo similar al Acme4680Sensor I descrito anteriormente. Cualquier clase particular puede implementar múltiples interfaces si el dispositivo puede realizar múltiples funciones.

Cada clase de dispositivo tiene su propia clase privada de comunicación (comunicación) que maneja la tarea de bajo nivel de hablar con el hardware.

Fuera del módulo de hardware, la única capa visible es la interfaz / enumeración más el HardwareManager. La clase HardwareManager es la abstracción de fábrica que maneja la creación de instancias de clases de dispositivos, instancias de almacenamiento en caché ( realmente no desea que dos clases de dispositivos se comuniquen con el mismo dispositivo de hardware), etc. Una clase que necesita un tipo particular de sensor le pide al HardwareManager que obtenga el dispositivo para la enumeración particular, que luego descubre si ya está instanciado, si no cómo crearlo e inicializarlo, etc.

El objetivo aquí es desacoplar la lógica empresarial de la lógica de hardware de bajo nivel. Cuando está escribiendo un código que imprime datos del sensor en la pantalla, ese código no debería importar qué tipo de sensor tiene si y solo si este desacoplamiento está en su lugar y se centra en esas interfaces de hardware.


Ejemplo de diagrama de clase UML que muestra el diseño descrito en esta respuesta

Nota: existen asociaciones entre el HardwareManager y cada clase de dispositivo que no dibujé porque el diagrama se habría convertido en una sopa de flechas.


fuente
Muy interesante respuesta. Solicitaría una edición rápida: deje más explícito cuál es la herencia / contención / colaboración entre las cuatro clases que mencionó. ¡Muchas gracias!
Heltonbiker
Agregué un diagrama de clase UML. ¿Esto ayuda?
2
Diría que esta es una respuesta asesina , y no solo me va a ayudar mucho, sino que creo que podría ayudar a mucha más gente en el futuro. Además, el ejemplo de jerarquía de clases que proporciona asigna nuestros dominios de problemas y soluciones notablemente bien. ¡De nuevo, muchas gracias!
Heltonbiker
Cuando leí la pregunta, me di cuenta de que estaban sucediendo más cosas, así que me excedí un poco y compartí todo el diseño. Esto es más que un simple problema de nombres porque los nombres son indicativos del diseño. He trabajado con sistemas construidos de esta manera y funcionaron bastante bien.
11

Puede ser un poco difícil encontrar una sola convención unificadora aquí porque estas clases se distribuyen en varios espacios de nombres ( ConnectionInfoparece estar dentro CrystalDecisionsy DeviceInfodentro System.Reporting.WebForms).

Sin embargo, al observar estos ejemplos, parece haber dos usos distintos del sufijo:

  • Distinguir una clase que proporciona métodos estáticos con una clase que proporciona métodos de instancia. Este es el caso de las System.IOclases, como lo subrayan sus descripciones:

    Directorio :

    Expone métodos estáticos para crear, mover y enumerar a través de directorios y subdirectorios. Esta clase no puede heredarse.

    DirectoryInfo :

    Expone métodos de instancia para crear, mover y enumerar a través de directorios y subdirectorios. Esta clase no puede heredarse.

    InfoParece una elección un poco extraña aquí, pero hace la diferencia relativamente clara: una Directoryclase podría representar razonablemente un directorio particular o proporcionar métodos auxiliares generales relacionados con el directorio sin tener ningún estado, mientras DirectoryInfoque solo podría ser el primero.

  • Enfatizando que la clase solo contiene información y no proporciona un comportamiento que podría esperarse razonablemente del nombre sin sufijo .

    Creo que la última parte de esa oración podría ser la pieza del rompecabezas que distingue, digamos, ConnectionInfode EmployeeInfo. Si se llamara a una clase Connection, razonablemente esperaría que realmente me proporcionara la funcionalidad que tiene una conexión; estaría buscando métodos como void Open(), etc. Sin embargo, nadie en su sano juicio esperaría que una Employeeclase realmente pudiera hacer lo que hace un real Employee, o buscar métodos como void DoPaperwork()o bool TryDiscreetlyBrowseFacebook().

Ben Aaronson
fuente
1
¡Muchas gracias por su respuesta! Creo que la primera viñeta en su respuesta definitivamente describe lo que sucede en las clases .NET, pero la segunda tiene más valor (y, por cierto, es diferente), porque revela mejor la abstracción deseada: algo que se debe usar frente a un Lo que debe describirse. Fue difícil elegir qué respuesta aceptar.
Heltonbiker
4

En general, un Infoobjeto encapsula información sobre el estado de un objeto en algún momento en el tiempo . Si le pido al sistema que mire un archivo y me dé un FileInfoobjeto asociado con su tamaño, esperaría que ese objeto informe el tamaño del archivo en el momento en que se realizó la solicitud (o, para ser más precisos, el tamaño de el archivo en algún momento entre cuando se realizó la llamada y cuándo regresó). Si el tamaño del archivo cambia entre el momento en que vuelve la solicitud y el momento en FileInfoque se examina el objeto, no esperaría que tal cambio se refleje en el FileInfoobjeto.

Tenga en cuenta que este comportamiento sería muy diferente al de un Fileobjeto. Si una solicitud para abrir un archivo de disco en modo no exclusivo produce un Fileobjeto que tiene una Sizepropiedad, esperaría que el valor devuelto cambie cuando cambie el tamaño del archivo de disco, ya que el Fileobjeto no representa simplemente el estado de un archivo: representa el archivo en sí .

En muchos casos, los objetos que se unen a un recurso deben limpiarse cuando ya no se necesitan sus servicios. Debido a que los *Infoobjetos no se unen a los recursos, no requieren limpieza. Como consecuencia, en los casos en que un Infoobjeto satisfará los requisitos de un cliente, puede ser mejor que el código use uno que usar un objeto que represente el recurso subyacente, pero cuya conexión con ese recurso tendría que limpiarse.

Super gato
fuente
1
Esto no es exactamente mal, pero no parece explicar los patrones de nombres de .NET muy bien a todos, ya que la Fileclase no se pueden crear instancias y FileInfoobjetos de hacer la actualización con el sistema de archivos subyacente.
Nathan Tuggy
@NathanTuggy Estoy de acuerdo en que esto no se relaciona bien con esta convención de nomenclatura en .NET, pero con respecto a mi problema actual, creo que es muy relevante y podría inducir una solución consistente. Voté esta respuesta, pero es difícil elegir solo una que acepte ...
heltonbiker
@NathanTuggy: No recomendaría usar .NET como modelo de ningún tipo de consistencia. Es un marco bueno y útil que encapsula muchas buenas ideas, pero algunas ideas malas también se capturan en la mezcla.
supercat
@supercat: claro; parece extraño responder una pregunta sobre "¿por qué .NET hace las cosas de esta manera?" con "sería una buena idea hacer cosas% THIS_WAY%".
Nathan Tuggy
@NathanTuggy: No recordaba los detalles exactos de las clases en cuestión, pero pensé que FileInfosolo contenía información estática capturada. ¿No es así? Además, nunca tuve la oportunidad de abrir archivos en modo no exclusivo, pero debería ser posible, y esperaría que exista un método que informe el tamaño actual de un archivo, incluso si el objeto utilizado para abrir no es llamada File(normalmente sólo tiene que utilizar ReadAllBytes, WriteAllBytes, ReadAllText, WriteAllText, etc.).
supercat
2

Teniendo en cuenta que los dispositivos reales (sensores y receptores) son una cosa, y su representación en el software es otra, estoy pensando en nombrar algunas clases con el patrón de nombre de sufijo "Info".

Por ejemplo, mientras que a Sensorsería una clase para representar el sensor real (cuando realmente está conectado a algún dispositivo en funcionamiento), SensorInfose usaría para representar solo las características de dicho sensor. Por ejemplo, al guardar el archivo, serializaría SensorInfoa en el encabezado del archivo, en lugar de serializar a Sensor, lo que no tendría sentido.

No me gusta esta distinción. Todos los objetos son "representación [s] en software". Eso es lo que significa la palabra "objeto".

Ahora, podría tener sentido separar la información sobre un periférico del código real que interactúa con el periférico. Entonces, por ejemplo, un Sensor tiene un SensorInfo, que contiene la mayoría de las variables de instancia, junto con algunos métodos que no requieren hardware, mientras que la clase Sensor es responsable de interactuar realmente con el sensor físico. No tiene un a Sensormenos que su computadora tenga un sensor, pero podría tenerlo SensorInfo.

El problema es que este tipo de diseño puede generalizarse a (casi) cualquier clase. Así que hay que tener cuidado. Obviamente no tendrías una SensorInfoInfoclase, por ejemplo. Y si tiene una Sensorvariable, puede encontrarse violando la ley de Demeter al interactuar con su SensorInfomiembro. Nada de esto es fatal, por supuesto, pero el diseño de API no es solo para autores de bibliotecas. Si mantiene su propia API limpia y simple, su código será más fácil de mantener.

Los recursos del sistema de archivos como directorios, en mi opinión, están muy cerca de esta ventaja. Hay algunas situaciones en las que desea describir un directorio que no es accesible localmente, es cierto, pero el desarrollador promedio probablemente no se encuentre en una de esas situaciones. Complicar la estructura de clases de esta manera es, en mi opinión, inútil. Contraste el enfoque de Python en pathlib: hay una sola clase que "probablemente sea lo que necesita" y varias clases auxiliares que la mayoría de los desarrolladores pueden ignorar de manera segura. Sin embargo, si realmente los necesita, proporcionan en gran medida la misma interfaz, solo que con métodos concretos eliminados.

Kevin
fuente
Gracias por su respuesta, y felicitaciones por su construcción "have-a";) Su respuesta me recuerda la incomodidad causada en algunos por el concepto "DTO" (objeto de transferencia de datos), o su hermano, el objeto de parámetro, que está mal visto por fanáticos OO más ortodoxos porque generalmente no contiene métodos (después de todo, es un objeto de transferencia de datos ). En ese sentido, y resumiendo también lo que otros han dicho, creo que SensorInfosería una especie de DTO, y / o también como una "instantánea", o incluso una "especificación", que representa solo la parte de datos / estado del Sensorobjeto real que "tal vez ni siquiera esté allí".
Heltonbiker
Como con la mayoría de los problemas de diseño, no existe una regla estricta y rápida. En el ejemplo pathlib que proporcioné, PureWindowsPathactúa un poco como un objeto Info, pero tiene métodos para hacer cosas que no requieren un sistema Windows (por ejemplo, tomar un subdirectorio, dividir una extensión de archivo, etc.). Esto es más útil que solo proporcionar una estructura glorificada.
Kevin
1
Nuevamente, felicitaciones por la parte de "estructura glorificada" :). Eso me recuerda una cita relacionada del libro "Clean Code" del Tío Bob, Capítulo 6: "Los programadores maduros saben que la idea de que todo es un objeto es un mito. A veces realmente se quieren estructuras de datos simples con procedimientos que operen en ellas".
heltonbiker
2

Diría que el contexto / dominio es importante, ya que tenemos código de lógica de negocios de alto nivel y modelos de bajo nivel, componentes de arquitectura, etc.

'Información', 'Datos', 'Administrador', 'Objeto', 'Clase', 'Modelo', 'Controlador' etc. pueden ser sufijos malolientes, especialmente en un nivel inferior, ya que cada objeto tiene cierta información o datos, por lo que esa información no es necesaria.

Los nombres de clase del dominio comercial deben ser como todas las partes interesadas hablan al respecto, sin importar si suena extraño o no es un lenguaje 100% correcto.

Los buenos sufijos para las estructuras de datos son, por ejemplo, 'Lista', 'Mapa' y para insinuar patrones 'Decorador', 'Adaptador' si cree que es necesario.

Para el escenario de su sensor, no esperaría SensorInfoguardar cuál es su sensor, pero SensorSpec. Infoimho es más una información derivada, como FileInfoes algo así como el tamaño, no se guarda o la ruta del archivo que se construye a partir de la ruta y el nombre del archivo, etc.

Otro punto:

Solo hay dos cosas difíciles en informática: invalidación de caché y nombrar cosas.

- Phil Karlton

Eso siempre me recuerda pensar en un nombre solo por unos segundos y, si no encuentro ninguno, uso nombres raros y 'TODO' los marca. Siempre puedo cambiarlo más tarde ya que mi IDE proporciona soporte de refactorización. No es un eslogan de la compañía lo que debe ser bueno, sino solo un código que podemos cambiar cada vez que queramos. Ten eso en mente.

Aitch
fuente
1

ThingInfo puede servir como un gran Proxy de solo lectura para The Thing.

ver http://www.dofactory.com/net/proxy-design-pattern

Proxy: "Proporcione un sustituto o marcador de posición para otro objeto para controlar el acceso a él".

Normalmente, ThingInfo tendrá propiedades públicas sin establecedores. Estas clases y los métodos de la clase son seguros de usar y no comprometerán ningún cambio en los datos de respaldo, el objeto ni ningún otro objeto. No se producirán cambios de estado u otros efectos secundarios. Estos pueden usarse para informes y servicios web o en cualquier lugar donde necesite información sobre el objeto pero desee limitar el acceso al objeto real en sí.

Use ThingInfo siempre que sea posible y limite el uso de Thing real a las veces que realmente necesita cambiar el objeto Thing. Hace que la lectura y la depuración sean considerablemente más rápidas cuando te acostumbras a usar este patrón.

Shmoken
fuente
Excelente. Hoy temprano estaba analizando mis necesidades arquitectónicas, con respecto a las clases que usé en la pregunta, y llegué a esta misma conclusión. En mi caso, tengo un Receiverque recibe flujos de datos de muchos Sensors. La idea es: el receptor debe abstraer los sensores reales. Pero el problema es: necesito la información del sensor para poder escribirla en algún encabezado de archivo. La solución: cada uno IReceivertendrá una lista de SensorInfo. Si envío comandos al receptor que implican cambiar el estado del sensor, estos cambios se reflejarán (a través de getter) en el respectivo SensorInfo.
Heltonbiker
(continuación ...) es decir: no hablo con los sensores en sí, solo hablo con el receptor a través de comandos de nivel superior, y el receptor a su vez habla con cada uno de los sensores. Pero eventualmente necesito información de los sensores, que obtengo de la instancia de Receiver a través de su List<SensorInfo>propiedad de solo lectura.
heltonbiker
1

Hasta ahora, nadie en esta pregunta parece haber captado la verdadera razón de esta convención de nomenclatura.

A DirectoryInfono es el directorio. Es un DTO con datos sobre el directorio. Puede haber muchas instancias que describan el mismo directorio. No es una entidad. Es un objeto de valor desechable. A DirectoryInfono representa el directorio real. También puede considerarlo como un controlador o controlador para un directorio.

Contraste eso con una clase llamada Employee. Este podría ser un objeto de entidad ORM y es el único objeto que describe a ese empleado. Si se tratara de un objeto de valor sin identidad, debería llamarse EmployeeInfo. Un Employeesí representa al empleado real. Una clase de DTO similar a un valor llamada EmployeeInfoclaramente no representaría al empleado, sino que lo describiría o almacenaría datos al respecto.

En realidad, hay un ejemplo en el BCL donde existen ambas clases: A ServiceControlleres una clase que describe un servicio de Windows. Puede haber cualquier número de tales controladores para cada servicio. Una ServiceBase(o una clase derivada) es el servicio real y no tiene sentido conceptualmente tener múltiples instancias de este por servicio distinto.

usr
fuente
Esto suena similar al Proxypatrón mencionado por @Shmoken, ¿no?
heltonbiker
@heltonbiker no necesariamente tiene que ser un proxy. Podría ser un objeto que no está conectado de ninguna manera a lo que describe. Por ejemplo, puede obtener un EmployeeInfoservicio web. Eso no es un proxy. Otro ejemplo: Una AddressInfoni siquiera tiene algo que pueda delegar porque una dirección no es una entidad. Es un valor independiente.
usr
1
Ya veo, eso tiene sentido!
heltonbiker