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?
-
5It's not obvious what's the problem(s) with your current design. Can you please elaborate?pabdulin– pabdulin2013年08月15日 07:25:12 +00:00Commented Aug 15, 2013 at 7:25
-
I have added more information in the question to describe the problem.db42– db422013年08月15日 12:00:15 +00:00Commented Aug 15, 2013 at 12:00
-
1The 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– Job2013年08月16日 04:32:00 +00:00Commented Aug 16, 2013 at 4:32
4 Answers 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– db422013年08月15日 12:56:29 +00:00Commented Aug 15, 2013 at 12:56
-
Look at my last quote. It explains what might be wrong.Yusubov– Yusubov2013年08月15日 13:05:04 +00:00Commented Aug 15, 2013 at 13:05
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– Gibson2013年08月16日 09:14:14 +00:00Commented Aug 16, 2013 at 9:14
-
1Horse would not inherit from the Legs classes. Horse would contain the classes.Aaron Kurtzhals– Aaron Kurtzhals2013年08月16日 13:27:05 +00:00Commented Aug 16, 2013 at 13:27
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– db422013年08月15日 12:45:48 +00:00Commented Aug 15, 2013 at 12:45
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– Gibson2013年08月15日 13:58:12 +00:00Commented Aug 15, 2013 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– Korey Hinton2013年08月15日 14:12:35 +00:00Commented Aug 15, 2013 at 14:12
-
i understand your point, but how does my solution break LSP ?db42– db422013年08月15日 15:40:07 +00:00Commented Aug 15, 2013 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– Gibson2013年08月15日 16:02:25 +00:00Commented Aug 15, 2013 at 16:02
Explore related questions
See similar questions with these tags.