Se eu quiser salvar e recuperar um objeto, devo criar outra classe para lidar com isso, ou seria melhor fazer isso na própria classe? Ou talvez misturando os dois?
O que é recomendado de acordo com o paradigma OOD?
Por exemplo
Class Student
{
public string Name {set; get;}
....
public bool Save()
{
SqlConnection con = ...
// Save the class in the db
}
public bool Retrieve()
{
// search the db for the student and fill the attributes
}
public List<Student> RetrieveAllStudents()
{
// this is such a method I have most problem with it
// that an object returns an array of objects of its own class!
}
}
Versus. (Eu sei que o seguinte é recomendado, no entanto, isso me parece um pouco contra a coesão da Student
classe)
Class Student { /* */ }
Class DB {
public bool AddStudent(Student s)
{
}
public Student RetrieveStudent(Criteria)
{
}
public List<Student> RetrieveAllStudents()
{
}
}
Que tal misturá-los?
Class Student
{
public string Name {set; get;}
....
public bool Save()
{
/// do some business logic!
db.AddStudent(this);
}
public bool Retrieve()
{
// build the criteria
db.RetrieveStudent(criteria);
// fill the attributes
}
}
design
object-oriented
Ahmad
fonte
fonte
Respostas:
Princípio da responsabilidade única , separação de preocupações e coesão funcional . Se você ler esses conceitos, a resposta é: Separe-os .
Um motivo simples para separar a
Student
classe "DB" (ouStudentRepository
, seguir convenções mais populares) é permitir que você altere suas "regras de negócios", presentes naStudent
classe, sem afetar o código responsável pela persistência e vice- versa.Esse tipo de separação é muito importante, não apenas entre regras de negócios e persistência, mas entre as muitas preocupações do seu sistema, para permitir que você faça alterações com um impacto mínimo em módulos não relacionados (mínimo porque às vezes é inevitável). Ajuda a construir sistemas mais robustos, mais fáceis de manter e mais confiáveis quando ocorrem mudanças constantes.
Ao mesclar regras de negócios e persistência, uma classe única como no seu primeiro exemplo ou com
DB
uma dependênciaStudent
, você está juntando duas preocupações muito diferentes. Pode parecer que eles pertencem um ao outro; eles parecem coesos porque usam os mesmos dados. Mas eis o seguinte: a coesão não pode ser medida apenas pelos dados compartilhados entre os procedimentos, você também deve considerar o nível de abstração em que eles existem. De fato, o tipo ideal de coesão é descrito como:E, claramente, a execução de validações por um
Student
tempo também persistente não forma "uma única tarefa bem definida". Então, novamente, regras de negócios e mecanismos de persistência são dois aspectos muito diferentes do sistema, que por muitos princípios de bom design orientado a objetos, devem ser mantidos separados.Eu recomendo a leitura sobre Arquitetura Limpa , assistindo a essas palestras sobre o Princípio de Responsabilidade Única (onde um exemplo muito semelhante é usado) e assistindo a essa palestra sobre Arquitetura Limpa . Esses conceitos descrevem as razões por trás dessas separações.
fonte
Student
classe deve ser devidamente encapsulada, sim? Como uma classe externa pode gerenciar a persistência do estado privado? O encapsulamento deve ser equilibrado com o SoC e o SRP, mas simplesmente escolher um sobre o outro sem pesar cuidadosamente as trocas provavelmente está errado. Uma solução possível para esse dilema é usar acessadores privados de pacotes para o código de persistência usar.Ambas as abordagens violam o princípio da responsabilidade única. Sua primeira versão atribui à
Student
classe muitas responsabilidades e a vincula a uma tecnologia específica de acesso ao banco de dados. A segunda leva a uma grandeDB
turma que será responsável não apenas pelos alunos, mas por qualquer outro tipo de objeto de dados em seu programa. EDIT: sua terceira abordagem é a pior, pois cria uma dependência cíclica entre a classe DB e aStudent
classe.Portanto, se você não for escrever um programa de brinquedos, não use nenhum deles. Em vez disso, use uma classe diferente como a
StudentRepository
para fornecer uma API para carregar e salvar, desde que você implemente o código CRUD por conta própria. Você também pode considerar usar uma estrutura ORM , que pode fazer o trabalho duro por você (e a estrutura normalmente aplicará algumas decisões nas quais as operações de Carregar e Salvar devem ser colocadas).fonte
Existem alguns padrões que podem ser usados para persistência de dados. Existe o padrão de Unidade de Trabalho , existe o padrão de Repositório , existem alguns padrões adicionais que podem ser usados como a Fachada Remota, e assim por diante.
A maioria deles tem fãs e críticos. Muitas vezes, trata-se de escolher o que parece ser o melhor para o aplicativo e mantê-lo (com todas as suas vantagens e desvantagens, ou seja, não usar os dois padrões ao mesmo tempo ... a menos que você esteja realmente certo disso).
Como uma observação lateral: no seu exemplo, o RetrieveStudent, AddStudent deve ser métodos estáticos (porque eles não dependem da instância).
Outra maneira de ter métodos de salvar / carregar em classe é:
Pessoalmente, eu usaria essa abordagem apenas em aplicativos bastante pequenos, possivelmente ferramentas para uso pessoal ou onde eu possa prever com segurança que não terei casos de uso mais complicados do que apenas salvar ou carregar objetos.
Também pessoalmente, consulte o padrão da Unidade de Trabalho. Quando você o conhece, é realmente bom em casos pequenos e grandes. E é suportado por muitas estruturas / APIs, para nomear EntityFramework ou RavenDB, por exemplo.
fonte
Se é um aplicativo muito simples, onde o objeto está mais ou menos vinculado ao armazenamento de dados e vice-versa (ou seja, pode ser considerado uma propriedade do armazenamento de dados), é recomendável ter um método .save () para essa classe.
Mas acho que isso seria excepcional.
Em vez disso, geralmente é melhor deixar a classe gerenciar seus dados e sua funcionalidade (como um bom cidadão de OO) e externalizar o mecanismo de persistência para outra classe ou conjunto de classes.
A outra opção é usar uma estrutura de persistência que defina persistência declarativamente (como nas anotações), mas isso ainda está externalizando a persistência.
fonte
Sobre o
RetrieveAllStudents()
método, seu sentimento é correto, provavelmente é extraviado, porque você pode ter várias listas distintas de alunos. Por que não simplesmente manter a (s) lista (s) fora daStudent
classe?fonte