Pergunta

Qual design de classe é melhor e por quê?

public class User
{
    public String UserName;
    public String Password;
    public String FirstName;
    public String LastName;
}

public class Employee : User
{
    public String EmployeeId;
    public String EmployeeCode;
    public String DepartmentId;
}

public class Member : User
{
    public String MemberId;
    public String JoinDate;
    public String ExpiryDate;
}

OU

public class User
{
    public String UserId;
    public String UserName;
    public String Password;
    public String FirstName;
    public String LastName;
}

public class Employee
{
    public User UserInfo;
    public String EmployeeId;
    public String EmployeeCode;
    public String DepartmentId;
}

public class Member
{
    public User UserInfo;
    public String MemberId;
    public String JoinDate;
    public String ExpiryDate;
}
Foi útil?

Solução

A questão é simplesmente respondida reconhecendo que a herança modela um relacionamento “É-A”, enquanto a associação modela um relacionamento “TEM-A”.

  • Um funcionário É um usuário
  • Um funcionário TEM informações de usuário

Qual deles está correto?Esta é a sua resposta.

Outras dicas

Eu não gosto de nenhum dos dois.O que acontece quando alguém é membro e funcionário?

Pergunte a si mesmo o seguinte:

  • Você quer modelar um funcionário É um usuário?Se sim, escolha herança.
  • Você quer modelar um funcionário TEM uma informação do usuário?Se sim, use composição.
  • Existem funções virtuais envolvidas entre o Usuário (info) e o Colaborador?Nesse caso, use herança.
  • Um Funcionário pode ter múltiplas instâncias de Usuário (informações)?Se sim, use composição.
  • Faz sentido atribuir um objeto Employee a um objeto User (info)?Nesse caso, use herança.

Em geral, esforce-se para modelar a realidade que seu programa simula, sob as restrições da complexidade do código e da eficiência necessária.

Não acho que a composição seja sempre melhor que a herança (normalmente).Se Funcionário e Membro são realmente Usuários e são mutuamente exclusivos, então o primeiro design é melhor.Considere o cenário em que você precisa acessar o UserName de um Funcionário.Usando o segundo design você teria:

myEmployee.UserInfo.UserName

o que é ruim (lei de Deméter), então você refatoraria para:

myEmployee.UserName

que requer um pequeno método em Employee para delegar ao objeto User.Tudo isso é evitado pelo primeiro design.

Boa pergunta, embora para evitar distrações sobre certo e errado Eu consideraria perguntar os prós e os contras de cada abordagem - acho que é isso que você quis dizer com o que é melhor ou pior e por quê.De qualquer forma ....

A primeira abordagem, também conhecida como herança

Prós:

  • Permite comportamento polimórfico.
  • É inicialmente simples e conveniente.

Contras:

  • Poderia tornar-se complexo ou desajeitado ao longo do tempo se mais comportamento e relações são adicionados.

A segunda abordagem, também conhecida como composição

Prós:

  • Mapeia bem para cenários não-OOP, como tabelas relacionais, programação estruturada, etc.
  • É simples (se não necessariamente conveniente) de incrementalmente ampliar relações e comportamento.

Contras:

  • Nenhum polimorfismo, portanto, é menos conveniente usar informações e comportamentos relacionados

Listas como essas + as perguntas Jon Limjap mencionado irá ajudá-lo a tomar decisões e começar - então você poderá descobrir o que certo as respostas deveriam ter sido ;-)

Você também pode pensar em Funcionário como um papel do Usuário (Pessoa).A função de um usuário pode mudar com o tempo (o usuário pode ficar desempregado) ou o usuário pode ter várias funções ao mesmo tempo.

A herança é muito melhor quando há real "é uma" relação, por exemplo Maçã - Fruta.Mas tenha muito cuidado:Círculo - A elipse não é uma relação real "é uma", porque o círculo tem menos "liberdade" que a elipse (o círculo é um estado da elipse) - veja: Problema de círculo elipse.

As verdadeiras questões são:

  • Quais são as regras de negócios e histórias de usuários por trás de um usuário?
  • Quais são as regras de negócios e histórias de usuários por trás de um funcionário?
  • Quais são as regras de negócios e histórias de usuários por trás de um membro?

Estas podem ser três entidades completamente não relacionadas ou não, e isso determinará se o seu primeiro ou segundo design funcionará, ou se outro design completamente diferente será adequado.

Nenhum dos dois é bom.Muito estado mutável.Você não deve conseguir construir uma instância de uma classe que esteja em um estado inválido ou parcialmente inicializado.

Dito isto, o segundo é melhor porque favorece a composição em vez da herança.

Declarar seus requisitos/especificações pode ajudar a chegar ao 'melhor design'.
Sua pergunta é muito 'sujeita à interpretação do leitor' no momento.

Aqui está um cenário em que você deve pensar:

A composição (2º exemplo) é preferível se o mesmo Utilizador puder ser Funcionário e Membro.Por que?Porque para duas instâncias (Funcionário e Membro) que representam o mesmo Usuário, se os dados do Usuário forem alterados, você não precisará atualizá-los em dois locais.Somente a instância User contém todas as informações do usuário e somente elas devem ser atualizadas.Como as classes Employee e Member contêm a mesma instância de User, ambas conterão automaticamente as informações atualizadas.

Mais três opções:

  1. Tenha o User classe contém as informações suplementares para funcionários e membros, com campos não utilizados em branco (o ID de um determinado User indicaria se o usuário era funcionário, membro, ambos ou qualquer outro).

  2. Tem um User classe que contém uma referência a um ISupplementalInfo, onde ISupplementalInfo é herdado por ISupplementalEmployeeInfo, ISupplementalMemberInfo, etc.Código que é aplicável a todos os usuários pode funcionar com User objetos de classe e código que tinha um User referência poderia obter acesso às informações suplementares de um usuário, mas esta abordagem evitaria ter que mudar User se diferentes combinações de informações suplementares forem necessárias no futuro.

  3. Como acima, mas tenha o User classe contém algum tipo de coleção de ISupplementalInfo.Esta abordagem teria a vantagem de facilitar a adição de propriedades em tempo de execução a um usuário (por exemplo,porque um Member foi contratado).Ao utilizar a abordagem anterior, seria necessário definir diferentes classes para diferentes combinações de propriedades;transformar um "membro" em um "membro + cliente" exigiria um código diferente de transformar um "funcionário" em um "funcionário + cliente".A desvantagem desta última abordagem é que tornaria mais difícil proteger-se contra atributos redundantes ou inconsistentes (usando algo como um Dictionary<Type, ISupplementalInfo> armazenar informações suplementares poderia funcionar, mas pareceria um pouco "volumoso").

Eu tenderia a favorecer a segunda abordagem, na medida em que permite uma expansão futura melhor do que a herança direta.Trabalhar com uma coleção de objetos em vez de um único objeto pode ser um pouco trabalhoso, mas essa abordagem pode ser mais capaz do que as outras para lidar com requisitos variáveis.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top