Search code examples
c#design-patternsinheritancemultiple-inheritancecode-reuse

How To Implement Shared Behavior Between Classes (Without Multiple Inheritance Of Course) in C#


UPDATE: So pretty much everyone here has told me that I just need to start all over again on how I designed my classes (thank you folks for your excellent answers by the way!). Taking the hint, I started doing extensive reading on the strategy pattern. I want to create behavior classes (or strategy classes) that inherit from an abstract base class or classes. The Candidate class would then have properties w/ the different abstract base class/classes as the Type for the behaviors or strategies. maybe something like this:

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
}

Any comments or suggestions?


ORIGINAL Question:

I am trying to learn and become more proficient at design patterns and principles. I have am currently working on a design for few classes that has stumped me. Here's a very condensed version of the code:

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
}

The InternalEntryLevelCandidate class is primarily an EntryLevelCandidate but needs to share some of the implementations of InternalCandidate. I say implementations because I don't want the implementations to be different or repeated, otherwise I would use an interface for common contracts and have concrete implementations in each class. Some of the implementations of the InternalCandidate properties and behaviors need to be common or shared. I have read about C++ and Ruby mixins, which seem to be something similar to what I want to do. I also read this interesting blog post that discusses an idea for a behavior type where a class would be able to inherit multiple behaviors while still maintaining a single "is a" relationship: http://www.deftflux.net/blog/post/A-good-design-for-multiple-implementation-inheritance.aspx. This seems to convey what I am wanting. Can anyone give me some direction on how I can accomplish this using good design practices?


Solution

  • Immutable data value classes. If any properties in your various Candidate subclasses represent some kind of meaningful data value, create an immutable class for it, with the behaviors you need. Each of your distinct Candidate subclasses can then use the data type, but your code is still encapsulated in the data classes.

    Extension methods. These could be overloaded to work with any classes.

    I'd avoid the decorator pattern and stick with compiled/reflectable functionality.

    Composition. Develop the unique behaviors in separate classes right away, and build your Candidate classes around them, rather than writing unique behaviors in your Candidate classes and trying to pull out their functionality for use in related classes later.

    Depending on how you use the classes, you could also implement and make use of explicit and implicit conversion operators to a related type, so instead of reimplementing interfaces (which you wanted to avoid), you could actually cast your object into the type/implementation you need for whatever purpose.

    Another thing I just thought of, related to that last paragraph, is to have a leasing system, where your class spawns and object of the appropriate type, allows it to be manipulated, then consumes it later to assimilate the updated information.