views:

102

answers:

3

I have 3 classes, two inherit from 1:

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() { ... }
}

So the question is sometimes there will be a Volunteer who gets the Nepotism bonus - is there some way to write the constructors to allow overriding/nesting the base class like this:

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

I'm thinking something like:

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

Basically I want some objects to have the overrides from both classes.

I would like to avoid writing the override methods to check for nested classes.

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

How can I do this? Am I on the right track? Am I off the rails?

+10  A: 

Instead of subclassing, you might want to consider using the Decorator Pattern.

It provides an alternative to subclassing, and it useful when you may need to add "multiple" pieces of additional functionality to a single instance of a class, which is exactly the scenario.

Reed Copsey
Awesome suggestion. Decorator fits perfectly. There's a reason you should favor composition over inheritance...
Randolpho
Looks like decorator is exactly what I'm trying to reinvent. Thanks!
willoller
A: 

If an object can be both classes at once, then you may need to rethink how you're doing your inheritance.

It seems to me that if a Volunteer can sometimes get a Nepotism bonus, then really, your Volunteer class should have a getBonus() method, and this method really belongs in the base class. It would return zero for most volunteers, but occasionally it wouldn't - there's nothing wrong with that.

womp
+4  A: 

I think you are trying to use inheritance in an ill-advised way. This approach creates a mess of dependences and oddball business rules, which results in a rigid architecture that is hard to use and maintain.

If calculating an employees salary is dependent upon the Employee as well as "bonus traits", then it would be better to separate all three things from each other:

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;
    }
}
jrista
This is interesting as it would allow for easy addition of new *types* of bonuses as well
willoller
Exactly. :) `Separation of Concerns` and `Single Responsibility` my friend...best tools of an architect.
jrista
I think this is well done, however my concern about this approach is that it violates the Law of Demeter. SalaryCalculator is showing signs of feature envy and knowing the intimate details about the BonusTraits property. I'm not trying to bash your solution -it shows a very good `Separation of Concerns` and `SRP`. Any body have thoughts about possibly breaking `LoD`?
Jerod Houghtelling
I might make salarycalculator a method of Employee. I don't know if that is "looser" or "tighter" coupling, but if only employees need salaries calculated, then it's at least a consistent data boundary.
willoller
@Jerod: I think your taking a code smell to its utmost extreme. Feature envy occurs when one class tends to consume many properties and/or methods of another class. In this case, SalaryCalculator is a business component that is simply utilizing the public API of another class...and only ONE element of that public API at that. I would agree that SalaryCalculator had feature envy if it made use of several methods and properties, however that is not the case here. By decoupling the SalaryCalculator from the Employ class, you add the opportunity to create alternative implementations.
jrista