Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

I'm having trouble designing an inheritance hierarchy.

In the figure:

Base class:

  • This uses the Template Method.
  • Contains core logic/algorithm that calls virtual/abstract methods

Derived types:

  • These classes provides concrete implementation of the virtual/abstract methods expected by the Base.

Conditions:

  1. I have a virtual method (Run).
  2. The default Run method uses another virtual method (Proc).
  3. If a derived class uses the default Run behaviour, they must implement Proc.
  4. If a derived class overrides the Run behaviour, then it shouldn't have to care about the existence of Proc, let alone overriding it.

enter image description here

I've considered making Run() abstract and let each derived class handle their own. But then I'll end up with duplicate code.

enter image description here

I'm using a simplified example here.

In my actual project, I have several "Run"-typed virtual methods, each calling several "Proc"-typed methods.

Because of that, I have a whole bunch of virtual "Proc" methods with "throw new NotImplementedException()"

Creating a new derived type is also confusing, because the coder may not know which virtual methods must be implemented/can be ignored/etc.

I've considered using a strategy pattern, but since I have several "Run" methods, each one of those would have to be a strategy, which seems like overkill/overengineering.

enter image description here

Is there any way to simplify this?


[Update 1]

Here's an example that's closer to the kind of methods I'm working with

enter image description here

And imagine yet another method:

enter image description here

share|improve this question
    
Have you considered creating a default implementation in the top-level class, letting your descendant classes call Proc in the Top-level class, and only overriding Proc if necessary? –  Robert Harvey Jan 8 at 4:34
    
This sounds like the perfect example for a strategy pattern. You could also have type 2 and type 3 inherit from a type 5 that inherits from Base. This would be the standard inheritance way, but I would stick with strategy. If you later add classes that share run then they can reuse different run methods. Especially if you already have an example of two types sharing a run method. –  Nick Bray Jan 8 at 5:06
    
"each one of those would have to be a strategy, which seems like overkill/overengineering." I disagree. I think it is perfectly fine to express this behavior in it's own class. –  Euphoric Jan 8 at 6:47
    
In the update, what corresponds to proc() in the new diagram? getData()? –  itsbruce Jan 8 at 10:59
    
GetData corresponds to Run, GetSelectionName and GetXmlList to proc. Next example, RunQuery(Run), InsertData(proc). –  jsjslim Jan 8 at 11:46

2 Answers 2

up vote 2 down vote accepted

You have created your own, unnecessary problem here.

  1. Everything must be runnable
  2. The default implementation of run requires proc
  3. Anybody implementing their own run can ignore proc (but all that useless code still adorns the class and any descendants).
  4. Your concrete, exception-throwing implementation of proc enables people to set up themselves up for runtime errors. Why do that?

OK, so you wanted to be helpful by providing a default implementation but you have bolted on implementation details which you know will not always be desired. You say you want to avoid code duplication but you have encumbered your hierarchy with code which will be obsolete as soon as somebody extends it.

I would strongly urge you to go the interface/abstract-base-class route here:

  1. Abstract base class runnable
  2. Abstract base class proccer
  3. Create proc-calling concrete implementation of runnable.
  4. Wherever possible (even in proc-caller class methods) refer to runnables and not proc-callers).

So your default run implentation can be used where desired but can be dropped without penalty. Also, this way people are forced to implement proc if they mix in proc-caller but you don't have to write that "Not implemented" version of proc - they can't compile their code if they do not implement proc. Why create that runtime trap when you can require them to fix things at compile time?

If you think there are other useful behaviours which should be available, you can avoid code duplication by providing "interfaces" in the same way I have shown for proc. Anybody who wants that can mix it in.

share|improve this answer
    
did you mean something like in the 3rd figure (strategy approach)? –  jsjslim Jan 8 at 10:52

Why not add one more level into your hierarchy?

public class Base
{
    public virtual void Run()
    {
        // some code
    }
}

public class ProcBase : Base
{
    public sealed override void Run() // make sure it cannot be overriden
    {
        Proc();
        base.Run();
    }

    public virtual void Proc()
    { 
        // some code
    }
}

public class Type1 : Base // same with Type4
{
    public override void Run()
    {
        base.Run();
    }
}

public class Type2 : ProcBase // same with Type3
{
    public override void Proc()
    {
        base.Proc();
    }
}

Also, if this hierarchy changes between different Runs, then using strategy pattern becomes inevitable.

share|improve this answer
    
Perhaps Proc() could be made protected? –  rwong Jan 8 at 8:05
    
@Euphoric the hierarchy does indeed change (I've updated my question). I have several methods like Run(), and it would be hard, if not impossible, to refactor and group multiple base classes –  jsjslim Jan 8 at 10:47
    
@jsjslim Then I see no other way than to use Strategy for each different Run. –  Euphoric Jan 8 at 14:49

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.