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 Card
clase necesita determinar la visibilidad para los jugadores.
Una forma es tener boolean isVisible(Player p)
en Card
clase.
Otro es tener boolean isVisible(Card c)
en Player
clase.
No me gusta el primer enfoque en particular, ya que otorga conocimiento sobre la Player
clase de nivel superior a una Card
clase de nivel inferior .
En su lugar, opté por la tercera opción donde tenemos una Viewport
clase que, dada una Player
y una lista de cartas, determina qué cartas son visibles.
Sin embargo, este enfoque le roba a ambos Card
y a las Player
clases una posible función miembro. Una vez hecho esto para otras cosas que la visibilidad de las tarjetas, lo que queda Card
y Player
clases 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 Viewport
anterior.
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 DocumentId
que es inmutable, solo tiene un solo BigDecimal id
miembro y un captador para este miembro. Ahora necesita tener un método en alguna parte, que proporcione DocumentId
retornos Document
para esta identificación de una base de datos.
Vos si:
- Agregue un
Document getDocument(SqlSession)
método a laDocumentId
clase, 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 laDocumentId
clase como muerta, sin comportamiento, clase de estructura.