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 Sensor
sería una clase para representar el sensor real (cuando realmente está conectado a algún dispositivo en funcionamiento), SensorInfo
se usaría para representar solo las características de dicho sensor. Por ejemplo, al guardar el archivo, serializaría SensorInfo
a 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 Employee
clase 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:
Directory
yDirectoryInfo
clases;File
yFileInfo
clases;ConnectionInfo
clase (sinConnection
clase correspondiente );DeviceInfo
clase (sinDevice
clase 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 ( Thing
y ThingInfo
) y otros casos en los que solo debería existir la ThingInfo
clase, o la Thing
clase, sin su contraparte?
fuente
Info
sufijo distingue unastatic
clase 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ácilmenteFileUtility
yFile
, pero,File.DoSomething()
yFileInfo.FileName
parecen leer mejor.Foo
, es posible que tenga una clase de utilidad no instanciableFoos
. Cuando se trata de nombrar, lo importante es la coherencia dentro de la API, e idealmente entre las API de la plataforma.Employee
docenas encuentran ejemplos, ya sea en línea o en libros clásicos, aunque todavía no los he vistoEmployeeInfo
(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 claseEmployeeInfo
fuera propuesta en un proyecto, creo que podría ser útil.Respuestas:
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:
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.
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
Puede ser un poco difícil encontrar una sola convención unificadora aquí porque estas clases se distribuyen en varios espacios de nombres (
ConnectionInfo
parece estar dentroCrystalDecisions
yDeviceInfo
dentroSystem.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.IO
clases, como lo subrayan sus descripciones:Directorio :
DirectoryInfo :
Info
Parece una elección un poco extraña aquí, pero hace la diferencia relativamente clara: unaDirectory
clase podría representar razonablemente un directorio particular o proporcionar métodos auxiliares generales relacionados con el directorio sin tener ningún estado, mientrasDirectoryInfo
que 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,
ConnectionInfo
deEmployeeInfo
. Si se llamara a una claseConnection
, razonablemente esperaría que realmente me proporcionara la funcionalidad que tiene una conexión; estaría buscando métodos comovoid Open()
, etc. Sin embargo, nadie en su sano juicio esperaría que unaEmployee
clase realmente pudiera hacer lo que hace un realEmployee
, o buscar métodos comovoid DoPaperwork()
obool TryDiscreetlyBrowseFacebook()
.fuente
En general, un
Info
objeto 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é unFileInfo
objeto 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 enFileInfo
que se examina el objeto, no esperaría que tal cambio se refleje en elFileInfo
objeto.Tenga en cuenta que este comportamiento sería muy diferente al de un
File
objeto. Si una solicitud para abrir un archivo de disco en modo no exclusivo produce unFile
objeto que tiene unaSize
propiedad, esperaría que el valor devuelto cambie cuando cambie el tamaño del archivo de disco, ya que elFile
objeto 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
*Info
objetos no se unen a los recursos, no requieren limpieza. Como consecuencia, en los casos en que unInfo
objeto 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.fuente
File
clase no se pueden crear instancias yFileInfo
objetos de hacer la actualización con el sistema de archivos subyacente.FileInfo
solo 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 llamadaFile
(normalmente sólo tiene que utilizarReadAllBytes
,WriteAllBytes
,ReadAllText
,WriteAllText
, etc.).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
Sensor
menos que su computadora tenga un sensor, pero podría tenerloSensorInfo
.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
SensorInfoInfo
clase, por ejemplo. Y si tiene unaSensor
variable, puede encontrarse violando la ley de Demeter al interactuar con suSensorInfo
miembro. 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.fuente
SensorInfo
serí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 delSensor
objeto real que "tal vez ni siquiera esté allí".PureWindowsPath
actú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.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
SensorInfo
guardar cuál es su sensor, peroSensorSpec
.Info
imho es más una información derivada, comoFileInfo
es 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:
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.
fuente
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.
fuente
Receiver
que recibe flujos de datos de muchosSensor
s. 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 unoIReceiver
tendrá una lista deSensorInfo
. 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 respectivoSensorInfo
.List<SensorInfo>
propiedad de solo lectura.Hasta ahora, nadie en esta pregunta parece haber captado la verdadera razón de esta convención de nomenclatura.
A
DirectoryInfo
no 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. ADirectoryInfo
no 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 llamarseEmployeeInfo
. UnEmployee
sí representa al empleado real. Una clase de DTO similar a un valor llamadaEmployeeInfo
claramente 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
ServiceController
es una clase que describe un servicio de Windows. Puede haber cualquier número de tales controladores para cada servicio. UnaServiceBase
(o una clase derivada) es el servicio real y no tiene sentido conceptualmente tener múltiples instancias de este por servicio distinto.fuente
Proxy
patrón mencionado por @Shmoken, ¿no?EmployeeInfo
servicio web. Eso no es un proxy. Otro ejemplo: UnaAddressInfo
ni siquiera tiene algo que pueda delegar porque una dirección no es una entidad. Es un valor independiente.