Salvando um objeto através de um método próprio ou através de outra classe?

36

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 ambos?

Qual é 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 o seguinte é recomendado, no entanto, parece-me um pouco contra a coesão de Student class)

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
        }
    }
    
por Ahmad 09.12.2014 / 17:21
fonte

5 respostas

32

Princípio de Responsabilidade Única , Separação de Questões e Coesão Funcional . Se você ler esses conceitos, a resposta é: Separe-os .

Um motivo simples para separar a Student da classe "DB" (ou StudentRepository , para seguir as convenções mais populares) é permitir que você altere suas "regras de negócios", presentes na classe Student , sem afetar o código que é responsável pela persistência e vice-versa.

Esse tipo de separação é muito importante, não apenas entre as regras de negócios e persistência, mas entre as várias preocupações do seu sistema, para permitir que você faça alterações com 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 há mudanças constantes.

Por ter regras de negócios e persistência misturadas, seja uma única classe como em seu primeiro exemplo ou com DB como uma dependência de Student , você está associando duas preocupações muito diferentes. Pode parecer que eles pertencem juntos; eles parecem ser coesos porque usam os mesmos dados. Mas aqui está a coisa: a coesão não pode ser medida apenas pelos dados que são 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:

Functional cohesion is when parts of a module are grouped because they all contribute to a single well-defined task of the module.

E, claramente, a realização de validações sobre um Student , embora também persistam, 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 design orientado a objetos, devem ser mantidos separados.

Eu recomendo ler sobre a arquitetura limpa , assistindo < a href="https://www.youtube.com/watch?v=Gt0M_OHKhQE"> fala sobre o Princípio da Responsabilidade Única (onde um exemplo muito similar é usado), e assistindo esta conversa sobre Arquitetura Limpa também. Esses conceitos descrevem as razões por trás dessas separações.

    
por 09.12.2014 / 17:41
fonte
10

Ambas as abordagens violam o Princípio de Responsabilidade Única. Sua primeira versão dá à classe Student muitas responsabilidades e a vincula a uma tecnologia específica de acesso ao banco de dados. A segunda leva a uma grande DB de classe, 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 é o pior, pois cria uma dependência cíclica entre a classe DB e a classe Student .

Então, se você não for escrever um programa de brinquedo, não use nenhum deles. Em vez disso, use uma classe diferente como StudentRepository para fornecer uma API para carregamento e salvamento, presumindo que você irá implementar o código CRUD sozinho. Você também pode considerar usar uma estrutura ORM , que pode fazer o trabalho pesado por você (e a estrutura normalmente impor algumas decisões em que as operações Carregar e Salvar devem ser colocadas).

    
por 09.12.2014 / 17:38
fonte
3

Existem alguns padrões que podem ser usados para persistência de dados. Há o padrão Unidade de trabalho , há Repositório padrão, existem alguns padrões adicionais que podem ser usados como o Remote Facade, e assim por diante.

A maioria deles tem seus fãs e seus críticos. Muitas vezes, trata-se de escolher o que parece se adequar melhor à aplicação e ficar com ela (com todas as vantagens e desvantagens, ou seja, não usar os dois padrões ao mesmo tempo ... a menos que você tenha certeza disso).

Como uma observação: no seu exemplo o RetrieveStudent, o AddStudent deve ser métodos estáticos (porque eles não são dependentes da instância).

Outra maneira de ter métodos de salvamento / carregamento na classe é:

class Animal{
    public string Name {get; set;}
    public void Save(){...}
    public static Animal Load(string name){....}
    public static string[] GetAllNames(){...} // if you just want to list them
    public static Animal[] GetAllAnimals(){...} // if you actually need to retrieve them all
}

Pessoalmente, eu usaria essa abordagem apenas em aplicativos relativamente 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.

Além disso, pessoalmente, veja o padrão da Unidade de Trabalho. Quando você fica sabendo, é muito bom em casos pequenos e grandes. E é suportado por muitos frameworks / apis, para nomear EntityFramework ou RavenDB por exemplo.

    
por 09.12.2014 / 17:39
fonte
1

Se for um aplicativo muito simples em que 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), ter um método .save () para essa classe pode faz sentido.

Mas acho que seria bem excepcional.

Em vez disso, geralmente é melhor deixar a classe gerenciar seus dados e sua funcionalidade (como um bom cidadão 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 a persistência declarativamente (como com anotações), mas que ainda está externando a persistência.

    
por 13.12.2014 / 03:06
fonte
-1
  • Defina um método nessa classe que serialize o objeto e retorne uma sequência de bytes que você pode colocar em um banco de dados ou arquivo ou o que for
  • Ter um construtor para esse objeto que usa uma sequência de bytes como entrada

Sobre o método RetrieveAllStudents() , seu sentimento está correto, é de fato provavelmente equivocado, porque você pode ter várias listas distintas de alunos. Por que não simplesmente manter as listas fora da classe Student ?

    
por 12.12.2014 / 22:56
fonte