La idea básica detrás de OOP es que los datos y el comportamiento (sobre esos datos) son inseparables y están unidos por la idea de un objeto de una clase. Los objetos tienen datos y métodos que funcionan con eso (y otros datos). Obviamente, según los principios de OOP, los objetos que son solo datos (como las estructuras C) se consideran un antipatrón.
Hasta aquí todo bien.
El problema es que he notado que mi código parece ir más y más en la dirección de este antipatrón últimamente. Me parece que cuanto más trato de lograr que la información se oculte entre las clases y los diseños vagamente acoplados, más mis clases llegan a ser una mezcla de datos puros sin clases de comportamiento y todos los comportamientos sin clases de datos.
Generalmente diseño clases de una manera que minimiza su conocimiento de la existencia de otras clases y minimiza su conocimiento de las interfaces de otras clases. Especialmente hago cumplir esto de arriba hacia abajo, las clases de nivel inferior no conocen las clases de nivel superior. P.ej:
Supongamos que tiene una API general de juegos de cartas. Tiene una clase Card. Ahora esta Cardclase necesita determinar la visibilidad para los jugadores.
Una forma es tener boolean isVisible(Player p)en Cardclase.
Otro es tener boolean isVisible(Card c)en Playerclase.
No me gusta el primer enfoque en particular, ya que otorga conocimiento sobre la Playerclase de nivel superior a una Cardclase de nivel inferior .
En su lugar, opté por la tercera opción donde tenemos una Viewportclase que, dada una Playery una lista de cartas, determina qué cartas son visibles.
Sin embargo, este enfoque le roba a ambos Cardy a las Playerclases una posible función miembro. Una vez hecho esto para otras cosas que la visibilidad de las tarjetas, lo que queda Cardy Playerclases que contienen los datos puramente como toda la funcionalidad se implementa en otras clases, que son sobre todo las clases que no tienen datos, sólo métodos, como el Viewportanterior.
Esto está claramente en contra de la idea principal de OOP.
¿Cuál es la forma correcta? ¿Cómo debo realizar la tarea de minimizar las interdependencias de clase y minimizar el conocimiento asumido y el acoplamiento, pero sin terminar con un diseño extraño donde todas las clases de bajo nivel contienen solo datos y las clases de alto nivel contienen todos los métodos? ¿Alguien tiene alguna tercera solución o perspectiva sobre el diseño de clase que evite todo el problema?
PD Aquí hay otro ejemplo:
Supongamos que tiene una clase DocumentIdque es inmutable, solo tiene un solo BigDecimal idmiembro y un captador para este miembro. Ahora necesita tener un método en alguna parte, que proporcione DocumentIdretornos Documentpara esta identificación de una base de datos.
Vos si:
- Agregue un
Document getDocument(SqlSession)método a laDocumentIdclase, introduciendo de repente el conocimiento sobre su persistencia ("we're using a database and this query is used to retrieve document by id"), la API utilizada para acceder a DB y similares. Además, esta clase ahora requiere un archivo JAR de persistencia solo para compilar. - Agregue alguna otra clase con método
Document getDocument(DocumentId id), dejando laDocumentIdclase como muerta, sin comportamiento, clase de estructura.