2

I've experienced this business behavior many times in the past, and this time decided to post a question here since I'm very curious how other people implemented it.

I have four processes in my system. Each process represented as a class (in C# environment), composed of shared functionality and functionality that exclusively used by this process.

Here's a class hierarchy of my system:

             BASE
            /     \
           A       B
          / \     / \
        Ax  Ay   Bx   By

BASE, A and B are abstract classes, which contain abstract methods as well as actual implementations.
Ax, Ay, Bx and By are the classes that represent processes. Each class has its unique methods, as well as methods shared with other processes. For example, Ax has its own methods, and it shares methods with Ay and Bx. There're additional functions that shared by all the classes, and they implemented in BASE.

To be precise:
Ax class shares the same methods (actual implementations) with Ay via A class. So Bx shares its methods with By via B class.

In addition to that, Ax shares another portion of methods with Bx. In the same way, Ay shares same methods with By. This sharing done via the BASE class. The problem is that each of the classes are exposed to functionality that they shouldn't be aware of. For example, Ax shouldn't be aware of methods shared between Ay and By..

How could I design a better implementation of my system? Are there any design patterns which intended to ease this setup?

Alex Lipov
  • 13,503
  • 5
  • 64
  • 87
  • So you basically want multi-inheritance, e.g. `class Ax : A, X`, `class Ay : A, Y`, `class Bx : B, X` and `class By : B, Y`? – dtb Feb 26 '12 at 15:15
  • Are you sure `Ax` is actually a `BASE`, or are you using inheritance just to share helper methods between classes? – dtb Feb 26 '12 at 15:17
  • @dtb Sort of.. in any case, it is not available in C#. – Alex Lipov Feb 26 '12 at 15:21
  • I think this is kind of a duplicate of http://stackoverflow.com/questions/1125746/how-to-hide-remove-a-base-classs-methods-in-c but asked in a different way. – Chris Gessler Feb 26 '12 at 16:04
  • Maybe you should break up A/B and x/y into separate classes. Can you post a sample of what exactly A/B and x/y are? It's hard to answer a question that is as general as this one. – svick Feb 26 '12 at 16:25

5 Answers5

2

Your problem is called combinatoric explosion.

It's better to use composition than inheritance. Like Strategy patern or maybe Decorator (depends on details of your problem). For example:

interface IBigLetterStrategy { /*some methods common for A and B*/ }
class A : IBigLetterStrategy { /* impl */ }
class B : IBigLetterStrategy { /* impl */}

interface ISmallLetterStrategy { /*some methods common for x and y*/ }
class x : ISmallLetterStrategy { /* impl */ }
class y : ISmallLetterStrategy { /* impl */ }

And finally:

class Process
{
    IBigLetterStrategy bigLetter;
    ISmallLetterStrategy smallLetter;
    /*unique logic here*/
}
Pein
  • 1,216
  • 9
  • 16
2

If I understand your question correctly, simple declaring common interfaces won't work here because you need a shared implementation, not just a shared structure.

Since you need a shared implementation, that implementation is actually a shared dependency. What you're looking for is simple dependency inversion: moving the shared behavior out to another class and calling that class.

Let's make your example a bit more concrete:

         Animal
          /  \
   Mammal    Insect
    /  \        /  \
  Bat  Cat    Bee  Grasshopper

Assume for the purposes of this example that:

  • Bats and bees can fly but not jump.
  • Cats and grasshoppers can jump but not fly.
  • These are known facts (even though they're wrong :)).

Also assume that jumpers jump the same way, and that fliers fly the same way. Here's one way to implement it:

  1. Add implementation classes for Flight and Jumping that have Fly() and Jump() methods, respectively.
  2. Create IFlier and IJumper interfaces and associate them with the correct animals (this is optional).
  3. Pass a Jumping object to the IJumper constructors (Cat and Grasshopper).
  4. Pass a Flight object to the IFlier constructors (Bat and Bee).

This could look something like:

To get this to work, the animals would have to call the implementation:

public class Bat : Mammal, IFlier
{
    private readonly IFlight _flight;

    public Bat(Flight flight)
    {
        _flight = flight;
    }

    public void Fly() // implements IFlier.Fly()
    {
        _flight.Fly();
    }
}

Needless to say, the other animal implementations would be similar. Using it is very simple:

var flight = new Flight();
var jumping = new Jumping();

IFlier bat = new Bat(flight);
IJumper cat = new Cat(jumping);
IFlier bee = new Bee(flight);
IJumper grasshopper = new Grasshopper(jumping);

bat.Fly();
cat.Jump();
bee.Fly();
grasshopper.Jump();

You can alternatively create a common abstract method Travel() on Animal, which would call the _flight.Fly() or _jumping.Jump() methods, as appropriate, instead of using the IFlier and IJumper interfaces. It really depends on what you need.

To sum it up:

  • Use Dependency Inversion.
  • Extract the common behaviors and move them to classes that are passed as dependencies to the implementations that require them.

Hope that helps.

shovavnik
  • 2,878
  • 3
  • 24
  • 21
1

It sounds to me that your classes is not small enough (violating SOLID) and that your relationships isn't really is-a. That usually leads to the described problems (at least for me).

Always try to favor composition over inheritance.

Add more detailed code if you want a more detailed answer.

jgauffin
  • 99,844
  • 45
  • 235
  • 372
0

You can control exposure to methods in base classes by using Interfaces. This is known as the Adapter Pattern, see question How to hide (remove) a base class's methods in C#?

Community
  • 1
  • 1
Chris Gessler
  • 22,727
  • 7
  • 57
  • 83
-2

If you have methods shared by Ax and Bx, you might consider another abstract class X to contain them and use multiple inheritance to include it in Ax and Bx.

shawnhcorey
  • 3,545
  • 1
  • 15
  • 17