Pergunta

Eu tive uma discussão no trabalho sobre "a herança no modelo de domínio está complicando a vida dos desenvolvedores". Eu sou um programador OO, então comecei a procurar argumentos de que ter herança no modelo de domínio facilitará a vida do desenvolvedor, em vez de ter interruptores em todo o lugar.

O que eu gostaria de ver é o seguinte:

class Animal {

}

class Cat : Animal {

}

class Dog : Animal {

}

O que o outro colega está dizendo é:

public enum AnimalType {
    Unknown,
    Cat,
    Dog
}

public class Animal {

    public AnimalType Type { get; set; }

}

Como faço para convencê -lo (links são BEM-VINDO ) que uma hierarquia de classe seria melhor do que ter uma propriedade enum para esse tipo de situações?

Obrigado!

Foi útil?

Solução

Aqui está como eu raciocho sobre isso:

Use apenas a herança se a função/tipo nunca mudar. por exemplo

Usando a herança para coisas como:

Bombeiro <- funcionário <- pessoa está errada.

Assim que Freddy, o bombeiro muda de emprego ou fica desempregado, você deve matá -lo e recriar um novo objeto do novo tipo com todas as relações antigas anexadas a ele.

Portanto, a solução ingênua para o problema acima seria dar uma propriedade Jobtitle Enum à classe de pessoa. Isso pode ser suficiente em alguns cenários, por exemplo, se você não precisar de comportamentos muito complexos associados à função/tipo.

A maneira mais correta seria dar à Pessoa uma lista de funções de classe. Cada função representa, por exemplo, um emprego com um período de tempo.

por exemplo

freddy.Roles.Add(new Employement( employmentDate, jobTitle ));

Ou se isso for um exagero:

freddy.CurrentEmployment = new Employement( employmentDate, jobTitle );

Dessa forma, Freddy pode se tornar um desenvolvedor sem que tenhamos que matá -lo primeiro.

No entanto, todas as minhas divagações ainda não responderam se você deve usar uma hierarquia de enumeração ou tipo para o Jobtitle.

Em Pure in Mem Oo, eu diria que é mais correto usar a herança para os Jobtitles aqui.

Mas se você estiver fazendo mapeamento de O/R, poderá acabar com um modelo de dados um pouco supercomplexo nos bastidores, se o mapeador tentar mapear cada sub -tipo para uma nova tabela. Portanto, nesses casos, geralmente adoto a abordagem enum se não houver um comportamento real/complexo associado aos tipos. Eu posso viver com um "if type == jobtitles.fireman ..." se o uso for limitado e tornar as coisas mais fáceis ou menos complexas.

Por exemplo, o Designer 4 do Entity Framework 4 para .NET só pode mapear cada sub -tipo para uma nova tabela. E você pode obter um modelo feio ou muitas junções quando consultar seu banco de dados sem qualquer benefício real.

No entanto, eu uso a herança se o tipo/função for estático. por exemplo, para produtos.

Você pode ter CD <- Produto e livro <- Produto. A herança vence aqui porque, neste caso, você provavelmente tem um estado diferente associado aos tipos. O CD pode ter várias propriedades de faixas, enquanto um livro pode ter o número de páginas.

Então, em suma, depende ;-)

Além disso, no final do dia, você provavelmente acabará com muitas declarações de troca de qualquer maneira. Digamos que você queira editar um "produto", mesmo que você use herança, provavelmente terá código como este:

if (produto é resposta) Response.Redict ("~/EditBook.aspx? Id" + Product.id);

Porque a codificação do URL do livro de edição na aula de entidade seria simples, pois isso forçaria o seu negócio a saber sobre a estrutura do seu site etc.

Outras dicas

Enumes são bons quando:

  1. O conjunto de valores é fixo e nunca ou muito raramente muda.
  2. Você quer ser capaz de representar uma união de valores (ou seja, bandeiras combinando).
  3. Você não precisa anexar outro estado a cada valor. (Java não tem essa limitação.)

Se você puder resolver seu problema com um número, uma enumeração provavelmente é um bom ajuste e mais um tipo seguro. Se você precisar de mais flexibilidade do que o acima, então as enumes são prováveis não a resposta certa. Usando classes polimórficas, você pode:

  1. Certifique-se estaticamente que todo o comportamento específico do tipo seja tratado. Por exemplo, se você precisar de todos os animais para poder Bark(), fazendo Animal classes com um abstrato Bark() O método permitirá que o compilador verifique se cada subclasse o implementa. Se você usar uma enumeração e um grande switch, não garantirá que você lidou com todos os casos.

  2. Você pode adicionar novos casos (tipos de animais em seu exemplo). Isso pode ser feito através de arquivos de origem e mesmo nos limites do pacote. Com uma enumeração, depois de declarar, está congelado. A extensão aberta é um dos pontos fortes primários da OOP.

É importante observar que o exemplo de seu colega não está em oposição direta à sua. Se ele quer que um tipo de animal seja uma propriedade exposta (que é útil para algumas coisas), você ainda pode fazer isso sem usar uma enumeração, usando o Digite o padrão de objeto:

public abstract class AnimalType {
    public static AnimalType Unknown { get; private set; }
    public static AnimalType Cat { get; private set; }
    public static AnimalType Dog { get; private set; }

    static AnimalType() {
        Unknown = new AnimalType("Unknown");
        Cat = new AnimalType("Cat");
        Dog = new AnimalType("Dog");
    }
}

public class Animal {
    public AnimalType Type { get; set; }
}

Isso dá a você a conveniência de uma enumeração: você pode fazer AnimalType.Cat E você pode obter o tipo de animal. Mas também oferece a flexibilidade das classes: você pode adicionar campos a AnimalType Para armazenar dados adicionais com cada tipo, adicionar métodos virtuais, etc. Mais importante, você pode definir novos tipos de animais apenas criando novas instâncias de AnimalType.

Eu recomendaria que você reconsidere: em um Modelo de domínio anêmico (de acordo com os comentários acima), os gatos não comporte-se Diferentemente dos cães, então não há polimorfismo. O tipo de animal realmente é apenas um atributo. É difícil ver o que a herança compra você lá.

Ter uma enumeração é como dar uma festa para todos aqueles Open/Closed Principle is for suckers pessoas.

Realmente convida você a verificar se um animal é de um determinado tipo e, em seguida, aplique a lógica personalizada para cada tipo. E isso pode renderizar código horrível, o que dificulta a construção do seu sistema.

O mais importante ainda significa modelar a realidade. A herança dá a você a oportunidade de dizer que Cat é um animal. O animal não deve saber se é um gato agora grita e depois decidir que é supor que, para miar e não latir, o encapsulamento é derrotado lá. Menos código como agora você não precisa fazer se mais, como disse.

Ambas as soluções estão certas. Você deve procurar quais técnicas se aplicam melhor ao seu problema.

Se o seu programa usa poucos objetos diferentes e não adicionar novas classes, é melhor ficar com enumerações.

Mas se você o programa usa muitos objetos diferentes (classes diferentes) e poderá adicionar novas classes, no futuro, melhor tente a maneira da herança.

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