Softerware
Softerware

Reputation: 2575

Multi-level Inheritance Extensible Methods

Say I have three or more classes: Engineer : Employee : Person. Consider the following example (pretend I use StringBuilder and other better practices for now)

public abstract class Person
{
   public string Name{get;set;}
   public string SaySomething()
   {
      string introduction = "Hello, my name is " + Name + ". ";
      return OnSaySomething(introduction);
   }
   protected virtual string OnSaySomething(string thoughts)
   { return thoughts; }
}

public abstract class Employee : Person
{
   public string Employer{get;set;}

   protected override string OnSaySomething(string thoughts)
   {
      return thoughts + "I work for " + Employer + ".";
   }
}

public class Engineer : Employee
{
   public string Discipline{get;set;}

   protected override string OnSaySomething(string thoughts)
   {
      return thoughts + "My discipline is " + Discipline + ".";
   }
}

With the above example, the output of:

 Engineer engineer = new Engineer();
 engineer.Name = "Dennis Ritchie";
 engineer.Discipline = "Software Engineering";
 Console.WriteLine(engineer.SaySomething());

would yield

Hello, my name is Dennis Ritchie. My discipline is Software Engineering.

Is it possible to structure these classes such that each override "tacks on" more functionality such that the output is:

Hello, my name is Dennis Ritchie. I work for . My discipline is Software Engineering.

Is there a standard way to implement this kind of idea? My knee-jerk solution is to seal OnSaySomething() at Employee and introduce a new method OnEmployeeSaySomething() which Engineer would override for it's own specific logic.

This seems to get messy quite quickly though since SaySomething(), OnSaySomething() and OnEmployeeSaySomething() are all exposed at Engineer (and even worse if there is another level in the hierarchy!). I wonder if there is a better solution. Thanks for your attention!

TL;DR: I want to extend without destroying base logic. But I don't want my classes to be really messy.

EDIT:

Someone pointed out a good (perhaps obvious) solution which is my fault for providing a bad example. Instead, say we're validating some data in this kind of scenario:

public class Level0
{
    public string Data{get;set;}
    public bool IsValid()
    {
        if (Data == null) return false;
        else return OnIsValid();
    }
    protected virtual bool OnIsValid()
    {return true;}  
}
public class Level1 : Level0
{
    public string Data_Level1{get;set;}
    protected override OnIsValid()
    {
        if (Data_level1 == null) return false;
        else return OnLevel1IsValid();
    }
    protected virtual bool OnLevel1IsValid()
    {return true;}
}
public class Level2 : Level1
{
    public string Data_Level2{get;set;}

    protected override OnLevel1IsValid()
    {
        return Data_level2 != null;
    }
}

Upvotes: 0

Views: 388

Answers (4)

Scott Chamberlain
Scott Chamberlain

Reputation: 127553

You need to call the base version of the function so the next higher level up can build its string and return it to the derived version so it can add additional text.

public abstract class Employee : Person
{
   public string Employer{get;set;}

   protected override string OnSaySomething(string thoughts)
   {
      return base.OnSaySomething(thoughts)+ "I work for " + Employer + ".";
   }
}

public class Engineer : Employee
{
   public string Discipline{get;set;}

   protected override string OnSaySomething(string thoughts)
   {
      return base.OnSaySomething(thoughts) + "My discipline is " + Discipline + ".";
   }
}

EDIT: Using your updated code, it still is the same thing

public class Level1 : Level0
{
    public string Data_Level1{get;set;}
    protected override bool OnIsValid()
    {
        return base.OnIsValid() && Data_level1 != null;
    }
}
public class Level2 : Level1
{
    public string Data_Level2{get;set;}

    protected override OnLevel1IsValid()
    {
        return base.OnIsValid() && Data_level2 != null;
    }
}

EDIT2: To make it "Automatic" you pretty much had the implementation done already, one additional thing you could do is mark the properties sealed so further down the chain it could not be overridden again, however I would not recommend this approach and instead go with the overrides in my previous example.

public class Level1 : Level0
{
    public string Data_Level1 { get; set; }

    protected override sealed bool OnIsValid()
    {
        if (Data_Level1 == null) return false;
        else return OnLevel1IsValid();
    }

    protected virtual bool OnLevel1IsValid()
    {
        return true;
    }
}

public class Level2 : Level1
{
    public string Data_Level2 { get; set; }

    protected override sealed bool OnLevel1IsValid()
    {
        return Data_Level2 != null;
    }
}

Upvotes: 1

Scott Chamberlain
Scott Chamberlain

Reputation: 127553

You need to call the base version.

protected override string OnSaySomething(string thoughts)
{
      return base.OnSaySomething(thoughts) + "My discipline is " + Discipline + ".";
}

Upvotes: 1

Christoph
Christoph

Reputation: 2317

I don't quite understand what you mean with "I want to extend, not override" because you obviously use the "override" keyword. In any case, may I propose the following solution:

public abstract class Person
{
   public string Name{get;set;}
   public virtual string SaySomething()
   {
      return = "Hello, my name is " + Name + ". ";
   }
}

public abstract class Employee : Person
{
   public string Employer{get;set;}

   public override string SaySomething()
   {
      return base.SaySomething() + "I work for " + Employer + ". ";
   }
}

public class Engineer : Employee
{
   public string Discipline{get;set;}

   public override string SaySomething()
   {
      return base.SaySomething() + "My discipline is " + Discipline + ". ";
   }
}

Upvotes: 0

Darthg8r
Darthg8r

Reputation: 12675

Make use of base:

protected override string OnSaySomething(string thoughts)
   {

      return base.OnSaySomething() + thoughts + "My discipline is " + Discipline + ".";
   }

Upvotes: 1

Related Questions