2010-06-24 17 views
4

J'ai 3 classes, deux Hériter de 1:classes de base nichant dans C#

public class Employee { 
    private virtual double getBonus() { ... } 
    private virtual double getSalary() { ... } 
} 

public class Nepotism : Employee { 
    private double getBonus() { ... } 
} 

public class Volunteer : Employee { 
    private double getSalary() { ... } 
} 

La question est donc parfois il y aura un bénévole qui obtient le bonus Népotisme - est-il possible d'écrire les constructeurs à permettent la surcharge/imbrication de la base classe comme ceci:

Employee Bill = new Volunteer(new Nepotism()); 

Je pense quelque chose comme:

public class Volunteer : Employee { 
    private Employee _nest; 
    public Volunteer(Employee nest) 
     : base() { 
     _nest = nest; 
     // now what? 
    } 
} 

Fondamentalement, je veux que certains objets aient les remplacements des deux classes.

Je voudrais d'éviter écrire les méthodes de substitution pour vérifier les classes imbriquées.

getSalary() { 
    return (nest != null) ? nest.salary : salary; // I want to avoid this if I can 
} 

Comment est-ce que je peux faire ceci? Suis-je sur la bonne voie? Suis-je hors des rails?

+2

Dans votre modèle, un employé détermine le salaire de cet employé. Cela correspond-il au monde réel? Non. Vous constaterez probablement que votre conception OO se passe plus facilement lorsque le code modélise le processus métier avec plus de précision; ce qui détermine en fait le salaire d'un employé est la * politique de rémunération * de l'organisation. Si cette politique de rémunération change, vous voulez mettre à jour le code dans un seul endroit, au lieu d'une demi-douzaine de classes d'employés différents. Donc, faites une classe qui représente une politique d'entreprise, et mettez les règles là-dedans. –

+0

D'accord - c'est pourquoi je suis allé avec la réponse de Jrista. Cela permettrait d'appliquer des politiques aux primes, salaires et autres méthodes d'indemnisation futures. – willoller

+0

Bien que la méthode getSalary puisse accéder à un moteur de règles ou à une autre source (découplée?) Pour obtenir des informations sur le salaire. – willoller

Répondre

4

Je pense que vous essayez d'utiliser l'héritage d'une manière mal avisée. Cette approche crée un fouillis de dépendances et de règles commerciales bizarres, ce qui entraîne une architecture rigide difficile à utiliser et à maintenir.

Si le calcul d'un salaire des employés dépend de l'employé ainsi que des « traits de bonus », alors il serait préférable de séparer les trois choses les uns des autres:

interface IBonusTrait 
{ 
    decimal ApplyBonus(Employee employee, decimal currentTotal); 
} 

class Employee 
{ 
    // ... 

    public decimal BaseSalary { get; set; } 
    public IList<IBonusTrait> BonusTraits { get; set; } 
} 

class SalaryCalculator 
{ 
    public decimal CalculateSalary(Employee employee) 
    { 
     decimal totalSalary = employee.BaseSalary; 
     foreach (IBonusTrait bonusTrait in employee.BonusTraits) 
     { 
      totalSalary = bonusTrait.ApplyBonus(employee, totalSalary); 
     } 

     return totalSalary; 
    } 
} 
+0

Ceci est intéressant car il permettrait d'ajouter facilement de nouveaux * types * de bonus ainsi – willoller

+0

Exactement. :) 'Séparation des préoccupations 'et' Single Responsibility' mon ami ... meilleurs outils d'un architecte. – jrista

+0

Je pense que c'est bien fait, mais mon inquiétude à propos de cette approche est qu'elle viole la loi de Demeter. SalaryCalculator montre des signes de jalousie et connaît les détails intimes de la propriété BonusTraits. Je n'essaie pas de critiquer votre solution - elle montre une très bonne «Séparation des préoccupations» et «SRP». Est-ce que n'importe quel corps a des pensées au sujet de casser peut-être 'LoD'? –

13

Au lieu de sous-classer, vous pouvez envisager d'utiliser le Decorator Pattern.

Il fournit une alternative au sous-classement, et il est utile lorsque vous avez besoin d'ajouter "plusieurs" fonctionnalités supplémentaires à une seule instance d'une classe, ce qui est exactement le scénario.

+0

Super suggestion. Le décorateur convient parfaitement. Il y a une raison pour laquelle vous devriez favoriser la composition plutôt que l'héritage ... – Randolpho

+0

On dirait que le décorateur est exactement ce que j'essaie de réinventer. Merci! – willoller

0

Si un objet peut être les deux classes à la fois, vous devrez peut-être repenser la façon dont vous faites votre héritage.

Il me semble que si un bénévole peut parfois obtenir un bonus de Népotisme, alors vraiment, votre classe bénévole devrait avoir une méthode getBonus(), et cette méthode appartient vraiment dans la classe de base. Cela reviendrait à zéro pour la plupart des bénévoles, mais parfois ce ne serait pas le cas - il n'y a rien de mal à cela.

0

Reed Copsey déjà dit, que décorateur Le motif est quelque chose à considérer.

Il y a aussi cette youtube video qui est très similaire à votre cas (John Skeet le présente).