La idea básica detrás de la POO es que los datos y el comportamiento (sobre esos datos) son inseparables y están acoplados 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 la POO, los objetos que son solo datos (como estructuras C) se consideran un antipatrón.
Hasta ahora 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 anti-patrón últimamente. Me parece que cuanto más trato de lograr que la información se oculte entre las clases y los diseños poco acoplados, más mis clases se convierten en una mezcla de datos puros, clases de comportamiento y todo tipo de comportamiento, no clases de datos.
Generalmente, diseño las 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 aplico esto de manera descendente, las clases de nivel inferior no saben acerca de las clases de nivel superior. Por ejemplo:
Supongamos que tienes una API general de juegos de cartas. Tienes una clase Card
. Ahora esta clase Card
necesita determinar la visibilidad para los jugadores.
Una forma es tener boolean isVisible(Player p)
en la clase Card
.
Otro es tener boolean isVisible(Card c)
en la clase Player
.
No me gusta el primer enfoque en particular, ya que otorga conocimiento sobre la clase Player
de nivel superior a una clase Card
de nivel inferior.
En cambio, opté por la tercera opción, donde tenemos una clase Viewport
que, dado un Player
y una lista de tarjetas determina qué tarjetas son visibles.
Sin embargo, este enfoque roba las clases Card
y Player
de una posible función miembro. Una vez que haga esto para otras cosas que no sean la visibilidad de las tarjetas, se le dejarán las clases Card
y Player
que contienen puramente datos, ya que toda la funcionalidad se implementa en otras clases, que en su mayoría son clases sin datos, solo métodos, como el Viewport
arriba.
Esto está claramente en contra de la idea principal de la POO.
¿Cuál es la forma correcta? ¿Cómo debo realizar la tarea de minimizar las interdependencias de clase y minimizar el conocimiento y el acoplamiento asumidos, pero sin terminar con un diseño extraño donde todas las clases de bajo nivel contienen solo datos y las 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?
P.S. Aquí hay otro ejemplo:
Supongamos que tiene la clase DocumentId
que es inmutable, solo tiene un único miembro BigDecimal id
y un captador para este miembro. Ahora necesitas tener un método en algún lugar, que dado un DocumentId
devuelve Document
para este ID desde una base de datos.
¿Usted:
- Agregue el método
Document getDocument(SqlSession)
a la claseDocumentId
, 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 la base de datos y similares. Además, esta clase ahora requiere un archivo JAR de persistencia solo para compilar. - Agregue alguna otra clase con el método
Document getDocument(DocumentId id)
, dejando la claseDocumentId
como muerta, sin comportamiento, clase de estructura.