Resposta tardia, mas não consigo resistir.
A maioria das classes X em Y é boa ou é antipadrão?
Na maioria dos casos, a maioria das regras, aplicadas sem pensar, na maioria das vezes dá terrivelmente errado (incluindo esta).
Deixe-me contar uma história sobre o nascimento de um objeto em meio ao caos de algum código processual, rápido e sujo, que aconteceu, não por design, mas por desespero.
Meu estagiário e eu somos uma programação em pares para criar rapidamente algum código descartável para raspar uma página da web. Não temos absolutamente nenhuma razão para esperar que esse código continue por muito tempo, então estamos apenas produzindo algo que funcione. Agarramos a página inteira como um barbante e cortamos as coisas de que precisamos da maneira mais incrível que você possa imaginar. Não julgue. Funciona.
Agora, ao fazer isso, criei alguns métodos estáticos para cortar. Meu estagiário criou uma classe de DTO muito parecida com a sua CatData
.
Quando olhei pela primeira vez para o DTO, ele me incomodou. Os anos de dano que Java causou ao meu cérebro me fizeram recuar nos campos públicos. Mas estávamos trabalhando em c #. O C # não precisa de getters e setters prematuros para preservar seu direito de tornar os dados imutáveis ou encapsulados posteriormente. Sem alterar a interface, você pode adicioná-los sempre que quiser. Talvez apenas para que você possa definir um ponto de interrupção. Tudo sem contar a seus clientes nada sobre isso. Sim C #. Boo Java.
Então eu segurei minha língua. Eu assisti enquanto ele usava meus métodos estáticos para inicializar essa coisa antes de usá-la. Tivemos cerca de 14 deles. Era feio, mas não tínhamos motivos para nos importar.
Então precisávamos disso em outros lugares. Nos descobrimos querendo copiar e colar o código. 14 linhas de inicialização sendo lançadas. Estava começando a ficar doloroso. Ele hesitou e me pediu idéias.
Relutantemente, perguntei: "você consideraria um objeto?"
Ele olhou para o DTO e torceu o rosto, confuso. "É um objeto".
"Quero dizer um objeto real"
"Hã?"
"Deixe-me mostrar uma coisa. Você decide se é útil"
Eu escolhi um novo nome e rapidamente criei algo que se parecia um pouco com isso:
public class Cat{
CatData(string catPage) {
this.catPage = catPage
}
private readonly string catPage;
public string name() { return chop("name prefix", "name suffix"); }
public string weight() { return chop("weight prefix", "weight suffix"); }
public string image() { return chop("image prefix", "image suffix"); }
private string chop(string prefix, string suffix) {
int start = catPage.indexOf(prefix) + prefix.Length;
int end = catPage.indexOf(suffix);
int length = end - start;
return catPage.Substring(start, length);
}
}
Isso não fez nada que os métodos estáticos já não estavam fazendo. Mas agora eu havia sugado os 14 métodos estáticos para uma classe em que eles poderiam ficar sozinhos com os dados em que trabalhavam.
Não forcei meu estagiário a usá-lo. Eu apenas o ofereci e deixei que ele decidisse se queria seguir os métodos estáticos. Fui para casa pensando que ele provavelmente seguiria o que já tinha trabalhado. No dia seguinte, descobri que ele o estava usando em vários lugares. Organizou o restante do código, que ainda era feio e processual, mas agora esse pouco de complexidade estava escondido atrás de um objeto. Foi um pouco melhor.
Agora, toda vez que você acessa, está fazendo um bom trabalho. Um DTO é um valor em cache rápido e agradável. Eu me preocupei com isso, mas percebi que poderia adicionar o cache, se precisarmos, sem tocar em nenhum código de uso. Então eu não vou incomodar até que nos importemos.
Estou dizendo que você deve sempre manter objetos OO sobre DTOs? Não. Os DTOs brilham quando você precisa cruzar um limite que o impede de mover métodos. Os DTOs têm o seu lugar.
Mas o mesmo acontece com objetos OO. Aprenda a usar as duas ferramentas. Saiba o que cada um custa. Aprenda a deixar o problema, a situação e o estagiário decidirem. Dogma não é seu amigo aqui.
Como minha resposta já é ridiculamente longa, deixe-me desapontá-lo de alguns conceitos errados com uma revisão do seu código.
Por exemplo, uma classe geralmente possui membros e métodos, por exemplo:
public class Cat{
private String name;
private int weight;
private Image image;
public void printInfo(){
System.out.println("Name:"+this.name+",weight:"+this.weight);
}
public void draw(){
//some draw code which uses this.image
}
}
Onde está o seu construtor? Isso não está me mostrando o suficiente para saber se é útil.
Porém, depois de ler sobre o princípio de responsabilidade única e o princípio aberto aberto, prefiro separar uma classe no DTO e a classe auxiliar apenas com métodos estáticos, por exemplo:
public class CatData{
public String name;
public int weight;
public Image image;
}
public class CatMethods{
public static void printInfo(Cat cat){
System.out.println("Name:"+cat.name+",weight:"+cat.weight);
}
public static void draw(Cat cat){
//some draw code which uses cat.image
}
}
Eu acho que se encaixa no princípio de responsabilidade única, porque agora a responsabilidade do CatData é manter apenas os dados, não se importa com os métodos (também para o CatMethods).
Você pode fazer muitas coisas tolas em nome do Princípio da Responsabilidade Única. Eu poderia argumentar que Cat Strings e Cat ints devem ser separadas. Todos os métodos de desenho e imagens devem ter sua própria classe. Como o seu programa em execução é de responsabilidade única, você deve ter apenas uma classe. : P
Para mim, a melhor maneira de seguir o Princípio da Responsabilidade Única é encontrar uma boa abstração que permita colocar complexidade em uma caixa para que você possa escondê-la. Se você pode dar um bom nome para evitar que as pessoas se surpreendam com o que descobrem quando olham para dentro, você o seguiu bastante bem. Esperar que dite mais decisões do que está pedindo problemas. Honestamente, as duas listagens de código fazem isso, então não vejo por que o SRP é importante aqui.
E também se encaixa no princípio aberto fechado, porque adicionar novos métodos não precisa alterar a classe CatData.
Bem não. O princípio de fechamento aberto não é sobre a adição de novos métodos. Trata-se de poder alterar a implementação de métodos antigos e ter que editar nada. Nada que use você e não seus métodos antigos. Em vez disso, você escreve um novo código em outro lugar. Alguma forma de polimorfismo fará isso muito bem. Não veja isso aqui.
Minha pergunta é: é um bom ou um antipadrão?
Bem, inferno, como eu deveria saber? Olha, fazer de qualquer maneira tem benefícios e custos. Quando você separa o código dos dados, pode alterar sem precisar recompilar o outro. Talvez isso seja extremamente importante para você. Talvez isso apenas torne seu código desnecessariamente complicado.
Se isso faz você se sentir melhor, não está longe de algo que Martin Fowler chama de objeto de parâmetro . Você não precisa apenas levar primitivas para o seu objeto.
O que eu gostaria que você fizesse é desenvolver um senso de como fazer sua separação, ou não, nos dois estilos de codificação. Porque acredite ou não, você não está sendo forçado a escolher um estilo. Você apenas tem que viver com sua escolha.