¿Un objeto tiene que representar una entidad?
Por una entidad que quiero decir algo así como un Product
, Motor
una ParkingLot
, etc, un examen físico, o incluso un objeto conceptual no física clara - algo que está bien definido, con algunos datos básicos que pertenece claramente al objeto, y algunas funciones / métodos que claramente operan en los datos centrales.
Por ejemplo, puedo tener un objeto de a Demon
, una entidad en sí misma, una imaginaria quizás y no física, pero una entidad no obstante
¿Puede un objeto ser solo una colección de métodos , un conjunto común de procedimientos que se vinculan con un objetivo común?
Ejemplo: se puede llamar a una clase MotorOperations
o MotorActions
, donde no hay entidad, pero los métodos dentro de la clase hacen cosas como
- getMotorDataFromHTMLForm ()
- getMotorManufacturers ()
- selectMotorFromUserRequirements ($ requisitos)
- canMotorCanHandleOperatingConditions ($ condiciones)
- computePowerConsumptionForMotor ($ id)
Una clase se define típicamente como datos centrales para el objeto + operaciones en los datos. Entonces, para un Motor
puede haber algunas variables del motor relacionadas con las especificaciones del motor, y puede haber operaciones que combinen esos datos para producir algo.
En mi caso, es más como si tuviera una clase con operaciones en datos + datos que se pasan a través de la clase, no hay datos centrados en "Motor Operations", aparte de los datos temporales de paso a través de la clase.
Pregunta
¿Pueden las clases representar objetos sin entidad? Si no, ¿por qué son malos / incompletos / no centrados en OOP? ¿Hay formas en que deben cambiarse / mejorarse conceptualmente para estar en línea con OOP?
MotorActions
oMotorOperations
son buenas ideas o no.Respuestas:
No , un objeto no tiene que representar una entidad.
De hecho, diría que cuando dejas de pensar en los objetos como entidades físicas es cuando finalmente obtienes los beneficios que promete OOP.
Este no es el mejor ejemplo, pero el diseño de la cafetera es probablemente donde la luz comenzó a encenderse para mí.
Los objetos son sobre mensajes. Se trata de responsabilidades. No se trata de automóviles, usuarios u órdenes.
Sé que enseñamos a OO de esta manera, pero después de algunos intentos se hace evidente cuán fundamentalmente frustrante es descubrir adónde van las cosas cuando intentas hacer MVC, MVVM o MVWhatever. O tus modelos se vuelven ridículamente hinchados o tus controladores lo hacen. Para la navegabilidad, es bueno saber que todo lo que toca Vehículos está en el archivo Vehicle.ext, pero cuando su aplicación es sobre Vehículos, inevitablemente termina con 3000 líneas de espagueti en ese archivo.
Cuando tiene que enviar un nuevo mensaje, tiene al menos un nuevo objeto, y quizás un par de ellos. Entonces, en su pregunta sobre un conjunto de métodos, diría que potencialmente está hablando de un conjunto de mensajes. Y cada uno podría ser su propio objeto, con su propio trabajo que hacer. Y eso esta bien. Será evidente a medida que separas las cosas que realmente necesitan estar juntas. Y los pones juntos. Pero no deja caer inmediatamente todos los métodos en un cajón vagamente apropiado por razones de conveniencia si desea disfrutar de OO.
Hablemos de bolsas de funciones.
Un objeto puede ser solo una colección de métodos y seguir siendo OO, pero mis "reglas" son bastante estrictas.
La colección debe tener una única responsabilidad, y esa responsabilidad no puede ser tan genérica como "Hace cosas a los motores". Podría hacer algo como una fachada de capa de servicio, pero soy muy consciente de que soy flojo por razones de navegación / descubrimiento, no porque esté tratando de escribir código OO.
Todos los métodos deben estar en una capa consistente de abstracción. Si un método recupera objetos de Motor y otro devuelve Caballos de fuerza, probablemente eso esté demasiado separado.
El objeto debería funcionar en el mismo "tipo" de datos. Este objeto hace cosas a los motores (inicio / parada), este hace cosas con longitudes de manivela, este maneja la secuencia de encendido, este toma una forma html. Estos datos podrían ser campos del objeto y parecería coherente.
Generalmente construyo objetos de este tipo cuando estoy haciendo transformaciones, composición, o simplemente no quiero preocuparme por la mutabilidad.
Creo que centrarme en las responsabilidades objetivas me lleva a la cohesión. Tiene que haber cierta cohesión para ser un objeto, pero no es necesario que haya ningún campo ni mucho comportamiento para que sea un objeto. Si estuviera construyendo un sistema que necesitara esos 5 métodos motores, comenzaría con 5 objetos diferentes que hacen esas cosas. A medida que encontraba algo en común, comenzaba a fusionar las cosas o a usar objetos "auxiliares" comunes. Eso me lleva a inquietudes abiertas / cerradas: ¿cómo puedo extraer este bit de funcionalidad para que nunca tenga que modificar ese archivo en particular nuevamente pero aún así usarlo donde sea necesario?
Los objetos son sobre mensajes
Los campos apenas le importan a un objeto: obtener y establecer registros no cambia el mundo fuera del programa. Colaborar con otros objetos hace el trabajo. Sin embargo, la fortaleza de OO es que podemos crear abstracciones para no tener que pensar en todos los detalles individuales a la vez. Las abstracciones que se filtran o no tienen sentido son problemáticas, por lo que pensamos profundamente (demasiado, tal vez) sobre la creación de objetos que coincidan con nuestros modelos mentales.
Pregunta clave: ¿Por qué estos dos objetos necesitan comunicarse entre sí?
Piense en el objeto como un órgano en una persona: tiene un propósito predeterminado y solo cambia el comportamiento cuando recibe un mensaje específico que le interesa.
Imagina un escenario en el que estás en el cruce de peatones y un automóvil viene rápido. Como objeto del cerebro, detecto un factor estresante. Le digo al hipotálamo que envíe hormona liberadora de corticotropina. La glándula pituitaria recibe ese mensaje y libera la hormona corticotrófica suprarrenal. Las glándulas suprarrenales reciben ese mensaje y crean adrenalina. Cuando el objeto muscular recibe ese mensaje de adrenalina, se contrae. Cuando el corazón recibe el mismo mensaje, late más rápido. Hay toda una cadena de jugadores involucrados en comenzar el comportamiento complejo de correr a toda velocidad en la calle y son los mensajes lo que importan. El objeto cerebral sabe cómo hacer que el hipotálamo envíe la alerta, pero no conoce la cadena de objetos que eventualmente harán que el comportamiento suceda. Del mismo modo, el corazón no tiene idea de dónde viene la adrenalina,
Entonces, en este ejemplo ( simplificado ), el objeto de la glándula suprarrenal solo necesita saber cómo tomar ACTH y producir adrenalina. No necesita ningún campo para hacer eso, sin embargo, todavía me parece un objeto.
Ahora, si nuestra aplicación está diseñada solo para correr a través de la calle, es posible que no necesite la glándula pituitaria y los objetos de la glándula suprarrenal. O solo necesito un objeto de glándula pituitaria que solo haga una pequeña parte de lo que conceptualmente podríamos ver como el "modelo de glándula pituitaria". Todos estos conceptos existen como entidades conceptuales, pero es software y podemos hacer AdrenalineSender o MuscleContractor o lo que sea y no preocuparnos mucho por la "incompletitud" de nuestro modelo.
fuente
MotorOperations
oMotorActions
". A partir de esto, estoy bastante seguro de que tanto los diseños de clase originales como los finales del ejemplo CoffeeMaker caen bajo la definición del OP de "representar una entidad"En resumen, puede hacer cualquier cosa, pero este escenario específico estaría en contra de los principios de OOP :)
Lo que está describiendo a veces se llama una clase de "utilidad", generalmente una señal de olor a código. Desea evitar crear clases en aras de mantener algunos métodos juntos.
No todos los objetos en su aplicación tienen que estar vinculados a una entidad de la vida real como un motor o automóvil; Esos son objetos de negocios. En su aplicación, es probable que utilice muchos de sus objetos comerciales, pero también necesitará más segregación para otras operaciones que no forman parte de las entidades comerciales, como las que enumeró.
Debería echar un vistazo a los patrones arquitectónicos comunes, uno de ellos es MVC (Model-View-Controller) .
Si tuviera que distribuir los métodos que enumeró usando MVC, esto es lo que haría:
Ver clase:
Clase de modelo (motor esencialmente):
Clase de controlador (MotorsController):
Parece que estás usando PHP; Un buen marco MVC para echar un vistazo es Laravel . En sus ejemplos, ilustran bien a qué métodos pertenecen.
Otra fuente de información más genérica sobre cómo decidir dónde pertenecen los métodos son los principios GRASP y SOLID .
fuente
MotorOperations
deMotorsController
y limpiarlo un poco, mi colección de funciones estará en línea con la programación orientada a objetos?Utils
clase sería un ejemplo de olor a código? Es curioso, ya que disfruté tener funciones de utilidad aleatorias incluidas en unaUtils
clase en lugar de ser funciones independientes ... en mi experiencia, hace que el código sea un poco más legible, pero mi experiencia es limitada.¿Lata? Si. ¿Debería? Probablemente no, o al menos, no cómo estás redactando cosas.
Los objetos en realidad son mejores cuando no representan un objeto físico directamente, ya que la realidad se asigna muy poco al código. Pero sí necesitan representar un concepto coherente o un objeto de código. Necesitan representar una responsabilidad cohesiva única.
Solo agrupar un conjunto de funciones relacionadas tangencialmente es un espacio de nombres o un módulo, no un objeto en el lenguaje OOP. Esos pueden ser útiles, pero no son lo mismo y requieren un tipo diferente de uso / pensamiento para trabajar con eficacia.
fuente
bundling a bunch of tangentially related functions together is a namespace
: eso suena más como un paradigma de procedimiento que como un objeto orientado.Una clase debería modelar algo ; de lo contrario, no tiene sentido. Sin embargo, lo que se está modelando puede no ser una "cosa" física; en cambio, puede ser una representación de algo que no es "real", pero que es necesario para controlar el sistema que se está modelando. Por ejemplo, en un sistema de control de semáforo, podría tener algún tipo de clase ControlSignal, que representa una señal enviada desde una parte del sistema a otra para indicar que es necesario realizar alguna acción. En "el mundo real", estas señales pueden consistir en impulsos eléctricos transmitidos a través de cables de una caja a otra. El proceso de modelar algo que no es "real" como un objeto concreto se denomina "reificación".
La mejor de las suertes.
fuente
No. (¡Título editado para hacer la pregunta opuesta!)
p.ej:
o
Sin embargo, en general, el ideal detrás de OOP es alejarse de
a
fuente
Creo que visualizar algo en el mundo real es útil cuando se codifican clases, pero no es necesario.
De hecho, dependiendo de cuán literal quieras ser, muchos objetos con los que trabajamos no tienen representaciones físicas en absoluto. Por ejemplo, considere la arquitectura MVC. En el mundo real no hay Modelo o Controlador, aunque generalmente están representados por clases. Los tres juntos a menudo representan algo, pero no siempre, pero como dije, probablemente podrías forzar un ajuste en algo si realmente quieres (¡y poder imaginar que ALGO ayuda! Visualizo la mayoría de los objetos de alguna manera, simplemente no nada alguna vez verías en el mundo real).
En su mayoría, estas "Reglas" que aprenderá sobre OO son solo pautas destinadas a orientarlo a pensar en OO, no necesariamente cosas que le preocupan mucho una vez que lo usa para codificar a diario.
Por cierto, OO en sí no es una panacea y no te ayuda en absoluto durante tu primer paso para codificar alguna solución, pero creo que si se hace bien, es una forma realmente fantástica de organizar el código para que pueda ser más fácilmente entendido más tarde. Escribir código mantenible es probablemente una de las tareas más difíciles en el desarrollo de software y en muchos casos (cualquier cosa que requiera depuración / mantenimiento continuo) es, con mucho, el más importante.
fuente
Pregunta: ¿Qué entidad
Logger
representa un ?No metafóricamente, literalmente.
Al explicar OOP a los estudiantes, es útil explicar objetos con analogías al mundo físico.
Alan Kay , uno de los padres de OOP , escribió lo siguiente:
fuente
A partir de esto, los objetos se entienden mejor como
objetos autárquicos que encapsulan / ocultan datos
comunicarse con otros objetos a través de mensajes
Definitivamente: piensa en
Math
En cuanto a su ejemplo:
Para decidir dónde colocar estos métodos, debe mirar
responsibilities
: quién es responsable de qué .El contexto de tal método sería construir un motor-objeto . No es responsabilidad del motor crearse a partir de los datos recuperados a través de un formulario . Hay al menos dos objetos involucrados; uno es el motor y el otro es el creador del motor .
El contexto típico en el que uno escribiría tal método es a
service
.Esto también se usaría en un
service
contextoEsta es una pregunta a un
motor
-objetoEsta también es una pregunta que se hace mejor a un motor.
tl; dr
La metáfora de
objects
como objetos físicos es más irritante que útil.fuente
thing
con datos donde los datos claramente pertenecen a la cosa y las funciones que operan claramente en los datos que pertenecen a la cosa". Entonces, Logger en este caso, aunque no es algo físico, es un concepto similar al de un "libro de registro físico". Si tiene datos que son fundamentales para su estado, y no solo transitorios, puede depender de la implementación e interpretación ... y más aún hacia dónde iba mi pregunta ...Sí, usted puede hacer esto. Pero esencialmente está creando una clase para proporcionar la misma funcionalidad que una biblioteca de procedimientos. La única razón para hacerlo es si su idioma carece de módulos de procedimiento (por ejemplo, Java o Ruby).
En un lenguaje con módulos de procedimiento (creo que PHP tiene módulos de procedimiento), podría considerar simplemente usar un módulo de procedimiento.
También podría considerar rediseñar su clase para que una instancia de la clase represente un objeto coherente.
¡Pero solo usa la notación OO cuando te da poder extra, no solo por el hecho de ser OO!
fuente
En palabras simples
Estas clases existen, por ejemplo, el SDK de Java tiene la clase Colecciones que consiste exclusivamente en métodos estáticos que operan en colecciones o las devuelven.
Entonces la respuesta sería no, no todas las clases necesitan ser modeladas a partir de una entidad de dominio.
Por otro lado, tales clases son solo unas pocas o deberían ser solo unas pocas en un programa hecho en un lenguaje OOP, ya que el verdadero poder de la OOP reside en el polimorfismo y la encapsulación.
Sería como usar un avión para ir de un lugar a otro, no volando, sino conduciéndolo por las autopistas. Los aviones tienen ruedas como los automóviles, pero están destinados a volar.
fuente
No, una clase simplemente representa algo que puede ser instanciado, tiene miembros y de donde se puede heredar.
De hecho, su idioma puede tener clases estáticas, que ni siquiera se instancian más de una vez.
.NET
Math
es un gran ejemplo de una clase "profesional" que no representa a ninguna entidad (a menos que quiera ser filosófico sobre lo que son las matemáticas ...), y también ilustra un caso de uso legítimo de dicha clase. Tiene solo métodos y 2 campos (ambos representan constantes individuales).La jerarquía de clases de una biblioteca similar
Math.Net
, agrupa métodos matemáticos / numéricos en clases por tipo. Aquí las clases no representan una entidad, sino una categoría lógica.Yo mismo a menudo termino creando clases (estáticas) que no son más que una bolsa de funciones relacionadas (estáticas), o un grupo de constantes convenientemente agrupadas en un lugar central. No diría que este es un buen diseño, pero a veces es la solución más sencilla.
fuente
Las respuestas a sus preguntas dependen en última instancia de cuán precisamente se definen términos como "clase" y "entidad". Hiciste un buen trabajo definiendo lo último, permitiendo tanto entidades físicas como conceptuales. Pero el término "clase" para los puristas siempre será "una fábrica para crear instancias de objetos con una representación interna particular" y para los pragmáticos el término abarcará aquellas cosas Java o C ++ despreciadas por los puristas. Lo mismo ocurre con el término "OOP", que los pragmáticos podrían decir que hacen Java y C ++, pero que los puristas tomarían en el sentido de lo que Alan Kay quiso decir cuando comentó ["Inventé el término orientado a objetos, y puedo decirte que no lo hice". tener C ++ en mente "] ( Entonces, ¿qué * quiso decir * Alan Kay realmente con el término" orientado a objetos "? ).
Si toma la vista pragmática, puede aceptar sus clases de utilidad sin instancia. Pero la visión purista es más interesante. OOP, según Kay, siempre fue más sobre los mensajes que las clases. Entonces a sus preguntas:
No. Las clases clasifican objetos. Las clases son, por definición, aquellas entidades a las que envía un mensaje, como
new
para producir un objeto. Un objeto es una entidad hecha de una clase. Las clases existen para fabricar y, de hecho, clasificar objetos. Eso es lo que hacen las clases. Las clases hacen objetos. Usamos clases para clasificar objetos. Entonces, si C es solo un conjunto de métodos que no permite la creación de instancias o subclases, no puede haber ningún objeto clasificado por C, por lo que C no es una clase.No estan mal. Simplemente no los llames clases. Un conjunto de métodos es OOPable: algo a lo que podría enviar un mensaje para invocar un método, pero no es una clase a menos que pueda crear una instancia de un objeto. ¿Conoces a Ruby? En Ruby, un módulo es un conjunto de métodos. Una clase es un módulo que puede instanciar objetos. No hay nada malo en un módulo . Pero lo que hace que una clase sea diferente de un módulo (en términos OOP puros) es que una clase puede tener instancias. Un módulo es solo algunos métodos. La confusión es que C ++ y Java y otros lenguajes usan el término "clase" tanto para clase como para módulo .
Ahora, para ver uno de sus ejemplos específicos, pregunta acerca de una
MotorOperations
clase a la que podría enviar el mensajecomputePowerConsumptionForMotor
con argumentoid
. ¿Es tan malo? Al menos no viola el Principio Tell Don't Ask . Es lo que tenemos que hacer una clase de motor y enviarlo alpowerConsumption
mensaje? Tal vez no el 100% del tiempo, pero tal vez intentar hacer esas cosas dará lugar a algunas buenas ideas sobre su código. O bien, puede conducir a una explosión de pequeñas clases que serían malas.Si "hacer OOP" es importante para usted, entonces querrá buscar formas de diseñar un sistema en componentes que se comuniquen enviándose mensajes entre sí. Eso es exactamente lo que es OOP , o al menos lo que tenía en mente el inventor del término. Las clases de utilidad no son OOP en esta vista. Pero para algunas personas, pueden ser.
Intentar hacer un gran sistema completo de millones de objetos vivos, respiratorios, todos instanciados de clases, puede funcionar para algunos proyectos. Pero si te encuentras creando clases artificiales que suenan mal, entonces los módulos deberían ser introducidos. Intente enfatizar las clases y use módulos cuando las clases instanciables serían ridículas.
TL; DR: los paquetes de métodos no siempre son malos. Intente usar clases instanciables primero. Vuelva a "módulos" solo cuando sea necesario.
fuente
Tomando los ejemplos dados:
Estos parecen métodos de "fábrica", que devolverían un
Motor
objeto. El segundo implica que está creando un nuevo objeto para cumplir con los requisitos, o tiene una base de datos o una colección en memoria de motores que está examinando. En cuyo caso debería ser un método para eso. O podría ser un método en el objeto de requisitos.¿De dónde los sacas? De eso debería ser un método.
Parece que deberían ser métodos
Motor
.fuente
getMotorManufacturers
es devolver a todos los fabricantes de motores de la base de datos. No estoy seguro de a dónde va eso. Me llevará algún tiempo desentrañar este BOPUM para poner las cosas donde tienen que ir ...getMotorManufacturers
de la base de datos. La base de datos no puede tener un método ... Actualmente tengo un patrón DataMapper que consulta la base de datos, inicializa una matriz y la llena con objetos de motor (que también contienen información del fabricante), y luego devuelve esa matriz a la persona que llama. La persona que llama es miMotorOperations
clase, que a su vez se llama desde unaProductSelection
clase (que contiene un algoritmo que se utiliza para seleccionar Motores, y la información del motor cargado son los datos para ese algoritmo)Un objeto es una colección de métodos y datos que tienen una alta cohesión. Pertenecen a una clase porque están altamente interrelacionados y se conserva el estado.
Esto es cierto ya sea que use un diseño inicial grande e intente modelar todo, o un diseño emergente en el que itera y evoluciona el objeto para adaptarlo a las necesidades del sistema en ese momento.
Si no tiene ninguna razón para retener el estado, probablemente no tenga ninguna razón para crear una instancia de un objeto, lo que significa que probablemente no haya razón para tener una clase. A menos que el lenguaje que está utilizando no tenga la capacidad de definir un espacio de nombres que no sea el uso de la clase. En el que usar clase debería estar bien porque sería idiomático.
No puedo pensar en ningún aspecto negativo para usar las clases de esta manera que no sea la típica. Su costo innecesario y su complejidad innecesarios al tener que crear una instancia de un "espacio de nombres". Cualquiera que respalde este código se preguntará dónde cuestan los datos una carga cognitiva adicional (muy probablemente un costo único por persona). Es un uso inusual sin ningún valor adicional.
fuente