Parallel Inheritance Hierarchies makes many unnecessary classes and makes code very fragile and tightly coupled.
For example, we have class Sportsman
and its Goal
's.
public abstract class Sportsman
{
public string Name { get; set; }
public abstract string ShowGoal();
}
and concrete class Footballer
:
public class Footballer : Sportsman
{
public override string ShowGoal()
{
return new FootballerGoal().Get();
}
}
and Runner
:
public class Runner : Sportsman
{
public override string ShowGoal()
{
return new RunnerGoal().Get();
}
}
Then we have their goals:
public abstract class Goal
{
public abstract string Get();
}
and its concrete classes:
public class FootballerGoal : Goal
{
public override string Get()
{
return "Score 1 goal";
}
}
And runner goal:
public class RunnerGoal : Goal
{
public override string Get()
{
return "Run 1 km";
}
}
Now, it can be seen that if we add new type of sportsman, then we need add a new class to hierarchy of Goal
.
We can try to avoid of creation of that hierarchy tree by using dependency injection and extracting method to interface.
At first, we create interface:
public interface IGoal
{
string Visit(Sportsman sportsman);
}
and then just implement it:
public class FootballerGoal : IGoal
{
public string Visit(Sportsman sportsman)
{
return "Score 1 goal";
}
}
and use it in Footballer
class:
public class Footballer : Sportsman
{
public string ShowGoal(IGoal goal)
{
return goal.Visit(this);
}
}
Now we do not have hierarchy tree and we can call it like this:
new Footballer().GetGoal(new FootballerGoal())
UPDATE:
There is a good article about Parallel Inheritance Hierarchies.. It proposes some ways to solve this task. Let me show the third way.
Solution 3 Collapse a hierarchy.
Pros:
Cons
So Footballer
class would like this:
public class Footballer : Sportsman
{
public override string ShowGoal()
{
return new FootballerGoal().Get();
}
public string GetGoal()
{
return "Score 1 goal";
}
}
And Runner
class would like this:
public class Runner : Sportsman
{
public override string ShowGoal()
{
return new RunnerGoal().Get();
}
public string GetGoal()
{
return "Run 1 km";
}
}