Вдохновленный отличным видео на тему "Одобрить композицию объекта над наследованием", в которой использовались примеры JavaScript; Я хотел попробовать это на С#, чтобы проверить свое понимание концепции, но это не так, как я надеялся.
/// PREMISE
// Animal base class, Animal can eat
public class Animal
{
public void Eat() { }
}
// Dog inherits from Animal and can eat and bark
public class Dog : Animal
{
public void Bark() { Console.WriteLine("Bark"); }
}
// Cat inherits from Animal and can eat and meow
public class Cat : Animal
{
public void Meow() { Console.WriteLine("Meow"); }
}
// Robot base class, Robot can drive
public class Robot
{
public void Drive() { }
}
Проблема заключается в том, что я хочу добавить класс RobotDog, который может использовать Bark and Drive, но не есть.
Первое решение - создать RobotDog в качестве подкласса Robot,
public class RobotDog : Robot
{
public void Bark() { Console.WriteLine("Bark"); }
}
но чтобы дать ему функцию Bark, мы должны были скопировать и вставить функцию Dog Bark, так что теперь у нас есть повторяющийся код.
Второе решение состояло в том, чтобы создать общий суперкласс с методом Bark, который затем и классы Animal и Robot, унаследованные от
public class WorldObject
{
public void Bark() { Console.WriteLine("Bark"); }
}
public class Animal : WorldObject { ... }
public class Robot : WorldObject { ... }
Но теперь КАЖДОЕ животное и КАЖДЫЙ робот будут иметь метод Барка, который большинству из них не нужен. Продолжая этот шаблон, подклассы будут нагружены методами, которые им не нужны.
Третьим решением было создание интерфейса IBarkable для классов, которые могут Bark
public interface IBarkable
{
void Bark();
}
И реализуйте его в классах Dog и RobotDog
public class Dog : Animal, IBarkable
{
public void IBarkable.Bark() { Console.WriteLine("Bark"); }
}
public class RobotDog : Robot, IBarkable
{
public void IBarkable.Bark() { Console.WriteLine("Bark"); }
}
Но снова у нас есть повторяющийся код!
Четвертый метод состоял в том, чтобы снова использовать интерфейс IBarkable, но создать класс-помощник Bark, который затем выполняет каждая из реализаций интерфейса Dog и RobotDog.
Это похоже на лучший метод (и то, что видео, по-видимому, рекомендует), но я также мог видеть проблему из проекта, загроможденную помощниками.
Пятое предложенное решение (hacky?) заключалось в том, чтобы повесить метод расширения с пустого интерфейса IBarkable, так что если вы реализуете IBarkable, то вы можете Bark
public interface IBarker { }
public static class ExtensionMethods
{
public static void Bark(this IBarker barker) {
Console.WriteLine("Woof!");
}
}
Многие похожие ответы на этом сайте, а также статьи, которые я прочитал, по-видимому, рекомендуют использовать классы abstract
, однако, не будут иметь те же проблемы, что и решение 2?
Каков наилучший объектно-ориентированный способ добавления класса RobotDog к этому примеру?