1

While working on a project I've come up with some design solution. I am having a hard time relating it to any general design pattern or analyzing this situation in details. This is also prohibiting me to find any better design for this.

This is a simple analogy of my solution - Both Kangaroo and Tiger can walk but only Kangaroo can jump and only Tiger can run.

Animal<TLeg> where TLegs: ILegs
{
    TLegs Legs;
    void Walk(){ Legs.Walk(); }
}

Kangaroo: Animal<IJumpingLegs>
{
    void Jump() { Legs.Jump(); }
}

Tiger: Animal<IRunningLegs>
{
    void Run() { Legs.Run(); }
}

ILegs
{
   void Walk();
}

IRunningLegs : ILegs
{
   void Run();
}

IJumpingLegs : Ilegs
{
   void Jump();
}

[Edit]
Problem Description:
Here, Kangaroo and Tiger share common functions and a property Legs. Therefore, I have a base class for common functions. Base class needs to have access to this property Legs.
Functions in subclasses too rely on Legs and based on type of Legs, subclass will need to call different functions of this property.

Basically, two concrete animals need some common methods and their uncommon methods(Jump and Run) require different TYPE of a common property.

For this problem, I have come up with the solution described above. Hoping that I am able to describe my problem, I would like to know if there could be any alternate solution.

Does this problem/solution sound familiar? Is there anything that can be improved?

  • 5
    It's not obvious what's the problem(s) with your current design. Can you please elaborate? – Petr Abdulin Aug 15 '13 at 7:25
  • I have added more information in the question to describe the problem. – db42 Aug 15 '13 at 12:00
  • 1
    The stated design is synthetic and therefore is useless to me. I want to help, but I can only solve real problems. What is the actual and not hypothetical problem that you are trying to solve? Something that a customer is paying you to figure out or something you need for a real life hobby project - what is it? – Job Aug 16 '13 at 4:32
4

This described problem reminds me the Adapter and Facade Patterns: Being Adaptive.

While reading your question, i also have remembered a famous funny old saying:

If it walks like a duck and quacks like a duck, then it must be a turkey wrapped with a duck adapter...

You may get detailed explanation from the following book - Head First Design Patterns

In addition, there are SOLID principles and Liskov Substitution Principle is one of them. It also addresses this issue with a different flavor.

If it looks like a duck, quacks like a duck, but needs batteries – you probably have the wrong abstraction

  • to me, it doesn't seem like 'being adaptive' pattern. Here, there is no adaption of interface A to interface B of any sort. I'll try to summarise the situation again. Basically, two concrete animals need some common methods and their uncommon methods(Jump and Run) require different TYPE of a common property. – db42 Aug 15 '13 at 12:56
  • Look at my last quote. It explains what might be wrong. – EL Yusubov Aug 15 '13 at 13:05
3

It is a mistake to use inheritance with the Legs classes and interfaces.(But if your teacher/boss insists that you have to...you'll probably have to give in.)

Your design works well enough for the requirements you listed. But suppose there is a Horse that can both run and jump. How do you implement that?

I suggest that WalkingLegs, RunningLegs, and JumpingLegs should all be separate classes with no inheritance. The Tiger would have WalkingLegs and RunningLegs, the Kangaroo would have WalkingLegs and JumpingLegs, and the Horse would have all three.

The above suggestion confuses the Legs metaphor. What we are really modeling is behavior, not types of legs.

Using classes to represent a behavior is part of the strategy pattern. In the strategy pattern, behaviors have different implementations (in this example, different implements of walking, jumping, etc.)

  • Why would WalingLegs, RunningLegs and JumpingLegs all be classes?? In .NET it is not possible for a class to inherit from more than one other class. So a Horse class inheriting from WalkingLegs, RunningLegs, JumpingLegs would not work. – Gibson Aug 16 '13 at 9:14
  • 1
    Horse would not inherit from the Legs classes. Horse would contain the classes. – Aaron Kurtzhals Aug 16 '13 at 13:27
2

To me, it seems like case of bad abstraction. Base classes are for abstracting behavior that is same for all expected children. Which is wrong in your example. In your case you either want Move virtual method, and then override it in concrete animals. Or you want Jump and Run methods, but then there is nothing common between them, so abstracting it away doesn't make sense.

And there is one rule I like when doing this :

Abstractions should always be seen from point of who will use those abstractions. Not from the inside of the implementation.

If you are writing some abstraction and you haven't written code that will use this abstraction, then you are doing something wrong.

  • in this case, I don't want to override 'Move' in concrete animals.(Move -> walk, just using the same notation that you referred). But those concrete animals will have either Jump or Run methods. Any suggestion what to do if my abstractions doesn't seem right? – db42 Aug 15 '13 at 12:45
1

I would probably implement something along the lines of this:

public abstract class Animal: ILegs
{
    public virtual void Walk(){/*Base logic for walking*/}
}

public interface ILegs
{
    void Walk();
}

public interface IJumpingLegs
{
    void Jump();
}

public interface IRunningLegs
{
    void Run();
}

public class Kangeroo : Animal, IJumpingLegs
{
    public void Jump()
    {
        //Logic for Jumping
    }
}

public class Tiger : Animal, IRunningLegs
{
    public void Run()
    {
        //Logic for Jumping
    }
}


//Custom Implementation
public class OtherAnimal : Animal
{
    public override void Walk() { /*Custom how I walk*/ }
}

When instanciating my objects I would use

OtherAnimal a = new OtherAnimal();
a.Walk(); //Custom override logic fro OtherAnimal class

Tiger t = new Tiger();
t.Run();  //Custom Run logic
t.Walk(); //Logic from Animal abstract class

Each Tiger/Kangeroo class only implements the necessary interface for each class and is not inheriting any unnecessary logic and not breaking the Liskov Substitution Principle mentioned by @Yusubov.

The Tiger/Kangeroo class also use the base Walk() logic from the Animal abstract class and have the option if necessary to override it lie the OtherAnimal class does.

Just don't implement a Fish class or similar extending the Animal. Fish don't walk, or have Legs!!

  • @Korey Hinton Thanks for fixing the spelling mistakes. They would have come out in a code review!! – Gibson Aug 15 '13 at 13:58
  • np. +1 'Just don't implement a Fish class or similar extending the Animal'. It's okay to solve the problem in the context of what you are trying to accomplish without trying to over-engineer a solution with the hope to mirror the real-world. – Korey Hinton Aug 15 '13 at 14:12
  • i understand your point, but how does my solution break LSP ? – db42 Aug 15 '13 at 15:40
  • Sorry, perhaps my answer wasn't very clear. I didn't say you were breaking LSP. I was just highlighting the fact that mine didn't. Also, when instantiating your objects I am unsure to use Animal<Tiger> t = new Animal<Tiger>(), which I would expect to use, but it gives me a compiler error. I need to add ILegs to each concrete implementation or remove the where TLegs : ILegs from the Animal<TLegs> class. I have to use Kangaroo k = new Kangaroo() if I want to create one of your objects with no compiler errors. – Gibson Aug 15 '13 at 16:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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