A idéia básica por trás da OOP é que dados e comportamento (com base nesses dados) são inseparáveis e são acoplados à idéia de um objeto de uma classe. O objeto possui dados e métodos que funcionam com esse (e outros dados). Obviamente, pelos princípios da OOP, objetos que são apenas dados (como estruturas C) são considerados um antipadrão.
Por enquanto, tudo bem.
O problema é que notei que meu código parece estar indo cada vez mais na direção desse antipadrão ultimamente. Parece-me que quanto mais eu tento obter informações ocultas entre classes e designs fracamente acoplados, mais minhas classes passam a ser uma mistura de dados puros sem classes de comportamento e todo comportamento sem classes de dados.
Geralmente, eu desenho classes de uma maneira que minimize a conscientização da existência de outras classes e minimize o conhecimento das interfaces de outras classes. Eu imponho isso especialmente de cima para baixo, as classes de nível inferior não conhecem as classes de nível superior. Por exemplo:
Suponha que você tenha uma API geral de jogo de cartas. Você tem uma aula Card
. Agora essa Card
classe precisa determinar a visibilidade para os jogadores.
Uma maneira é ter boolean isVisible(Player p)
na Card
aula.
Outra é ter boolean isVisible(Card c)
na Player
aula.
Não gosto da primeira abordagem em particular, pois concede conhecimento sobre Player
classe de nível superior a uma Card
classe de nível inferior .
Em vez disso, optei pela terceira opção, onde temos uma Viewport
classe que, dada ae Player
uma lista de cartões, determina quais cartões são visíveis.
No entanto, essa abordagem rouba as classes Card
e as Player
classes de uma possível função membro. Depois de fazer isso para outras coisas que a visibilidade de cartões, você é deixado com Card
e Player
classes que contêm dados puramente como toda a funcionalidade é implementada em outras classes, que são principalmente as classes sem dados, apenas métodos, como o Viewport
acima.
Isso é claramente contra a idéia principal do POO.
Qual é o caminho correto? Como devo executar a tarefa de minimizar as interdependências de classe e minimizar o conhecimento e o acoplamento assumidos, mas sem acabar com um design estranho, onde todas as classes de baixo nível contêm apenas dados e as classes de alto nível contêm todos os métodos? Alguém tem alguma terceira solução ou perspectiva sobre o design de classe que evite todo o problema?
PS Aqui está outro exemplo:
Suponha que você tenha uma classe DocumentId
imutável, que tenha apenas um único BigDecimal id
membro e um getter para esse membro. Agora você precisa ter um método em algum lugar, que DocumentId
retorne Document
esse ID a partir de um banco de dados.
Você:
- Adicione
Document getDocument(SqlSession)
método àDocumentId
classe, introduzindo repentinamente conhecimento sobre sua persistência ("we're using a database and this query is used to retrieve document by id"
), a API usada para acessar o DB e similares. Além disso, essa classe agora requer o arquivo JAR de persistência apenas para compilar. - Adicione outra classe com o método
Document getDocument(DocumentId id)
, deixando aDocumentId
classe como morta, sem comportamento, como uma estrutura.