Pergunta

UPDATE: Assim, praticamente todos aqui me disse que eu só preciso começar tudo de novo em como eu projetei minhas aulas (obrigado pessoal para suas excelentes respostas por sinal!). Tomando a dica, eu comecei a fazer a leitura extensiva na estratégia padrão . Eu quero criar classes de comportamento (ou classes de estratégia) que herdam de uma classe base abstrata ou classes. A classe Candidato teria, então, propriedades w / as diferentes abstract classe base / classes como o Type para os comportamentos ou estratégias. talvez algo como isto:

public abstract class SalaryStrategy {
    public abstract decimal Salary { get; set; }
    public abstract decimal Min { get; set; }
    public abstract decimal Mid { get; set; }
    public decimal CompaRatio {
        get {
            if (this.Mid == 0) { return 0; }
            else { return this.Salary / this.Mid; }
        }
    }
}

public class InternalCurrentSalaryStrategy {
    public override decimal Salary { get; set; }
    public override decimal Min {
        get { return this.Salary * .25m; }
        set { }
    }
    public override decimal Mid { get; set; }
}

public class Candidate {
    public int Id { get; set; }
    public string Name { get; set; }
    public SalaryStrategy CurrentSalaryStrategy { get; set; }
}

public static void Main(string[] args) {
    var internal = new Candidate();
    internal.CurrentSalaryStrategy = new InternalCurrentSalaryStrategy();
    var internalElp = new Candidate();
    internalElp.CurrentSalaryStrategy = new InternalCurrentSalaryStrategy();
    var elp = new Candidate();
    // elp.CurrentSalaryStrategy can stay null cause it's not used for elps
}

Quaisquer comentários ou sugestões?


ORIGINAL Pergunta:

Eu estou tentando aprender e se tornar mais proficiente em padrões e princípios de design. Eu estou trabalhando atualmente em um projeto para algumas classes que tem me perplexo. Aqui está uma versão muito condensada do código:

public class Candidate {
    public int Id { get; set; }
    public string Comments { get; set; }
    // lots more properties and behaviors...
}

public class InternalCandidate : Candidate {
    public decimal CurrentMid { get; set; }
    public decimal CurrentMax {
         get { return this.CurrentMin * 1.3m;
    }
    // lots more properties and behaviors...
}

public class EntryLevelCandidate : Candidate {
    public string Gpa { get; set; }
    // lots more properties and behaviors...
}

public class InternalEntryLevelCandidate /* what do I inherit here??? */ {
    // needs all of the properties and behaviors of
    // EntryLevelCandidate but also needs the CurrentMin and
    // CurrentMax (and possibly more) in InternalCandidate
}

A classe InternalEntryLevelCandidate é principalmente um EntryLevelCandidate mas as necessidades para compartilhar algumas das implementações de InternalCandidate. Eu digo implementações porque eu não quero que as implementações de ser diferente ou repetida, caso contrário eu iria usar uma interface para contratos comuns e têm implementações concretas em cada classe. Algumas das implementações das propriedades e comportamentos InternalCandidate precisa ser comum ou partilhado. Eu li sobre mixins C ++ e Ruby, que parecem ser algo parecido com o que eu quero fazer. Eu também ler este post interessante que discute uma idéia para um tipo de comportamento onde uma classe seria capaz de múltiplos comportamentos herdar, mantendo um único "é um" relacionamento: http://www.deftflux.net/blog/post/A-good-design-for- multiple-implementação-inheritance.aspx . Este parece transmitir o que estou querendo. Alguém pode me dar alguma orientação sobre como eu posso fazer isso usando boas práticas de design?

Foi útil?

Solução

aulas valor de dados imutáveis. Se nenhum Propriedades em suas várias subclasses candidatos representam algum tipo de valor dados significativos, criar uma classe imutável para ele, com os comportamentos que você precisa . Cada uma de suas subclasses candidatos distintos pode então usar o tipo de dados, mas o código ainda está encapsulado nas classes de dados.

Os métodos de extensão. Estes poderiam ser sobrecarregados para trabalhar com qualquer classes.

Eu gostaria de evitar o padrão de decorador e vara com compilado funcionalidade / reflectable.

Composição. Desenvolver os comportamentos únicos em classes separadas imediatamente, e construir suas classes candidatos redor deles, ao invés de escrever comportamentos únicos em suas classes candidatos e tentando retirar sua funcionalidade para utilização em afins classes mais tarde.

Dependendo de como você usar as classes, você também pode implementar e fazer uso de operadores de conversão explícitas e implícitas a um tipo relacionado, assim em vez de reimplementar interfaces (que você queria evitar), você poderia realmente lançar o seu objeto na tipo / implementação que você precisa para qualquer fim.

Outra coisa que eu só pensava, relacionado a esse último parágrafo, é ter um sistema de leasing, onde suas desovas de classe e objeto do tipo apropriado, permite que ele seja manipulado, em seguida, consome-lo mais tarde para assimilar as informações atualizadas.

Outras dicas

Aqui está um artigo acadêmico sobre o assunto que eu acho que é bastante interessante ( PDF ligação ).

Mas, eu acho que você está tentando impor lógica de negócios em suas generalizações. Você por acaso sabe que um InternalCandidate nunca terá seu GPA olhado. Mas, uma InternalCandidate certamente tem um GPA. Então, você tem rachado esse cara estranho chamado um InternalEntryLevelCandidate porque você já sabe que você quer olhar para GPA desse cara. Arquitetonicamente, acho que o EntryLevelCandidate está incorreto. Gostaria de acrescentar um conceito "Level" para um candidato e dar-lhe um GPA. É até o lógica de negócios para decidir se eles olham para o GPA ou não.

Edit: Além disso, Scott Meyers faz um ótimo trabalho de dissecar esta questão em seus livros.

Aviso :

Em minha experiência precisando herança múltipla é a excepção e não a regra, design cuidadoso das hierarquias de classe geralmente pode evitar a necessidade desse recurso. Concordo com JP que essa exigência poderia ser evitado em sua amostra.

Voltar para a questão, não há nenhuma solução limpa, no entanto você tem algumas opções:

  • Usar métodos de extensão, tem a desvantagem de clique direito Resolve não funciona, também algumas pessoas realmente não gosto desses filhotes.

  • Criar um objeto agregado que detém e instância de cada classe que você deseja composta, re-implementar métodos stub esse delegado.

  • Definir uma interface para cada comportamento e têm os métodos na verificação de base se this is IInterface antes de executar o comportamento. (Permite definições de comportamento puxar para a base)

duplicado Perto: herança múltipla em C #

Eu concordo que a herança não parece ser a coisa certa aqui. Eu não tenho certeza que eu sei a resposta perfeita, mas talvez o padrão Decorator é apropriado.

Outra ideia, mais esotérico é pensar sobre programação orientada a aspectos. Você pode fazer coisas incríveis com os aspectos, mas é um tema muito avançada que eu ainda não domina. O tipo de pessoas que têm são como Rikard Oberg e sua Qi4J coortes.

Eu tinha acabado de usar o Delegação padrão . Em última análise, eu usaria uma interface para cada peça distinta da funcionalidade, em seguida, ter uma classe concreta como um delegado para cada interface. Em seguida, suas classes finais só usar os delegados de que necessitam e podem herdar de várias interfaces.

public class InternalEntryLevelCandidate : EntryLevelCandidate {
    private  InternalCandidate internalCandidateDelegate
        = new InternalCandidate();

    public decimal CurrentMid { 
        get { return internalCandidateDelegate.CurrentMid; }
        set { internalCandidateDelegate.CurrentMid = value; }
    }

    public decimal CurrentMax {
        get { return internalCandidateDelegate.CurrentMax }
    }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top