Как спроектировать отсутствие множественного наследования? - PullRequest
3 голосов
/ 17 июня 2011

Использование интерфейсов не будет работать, потому что я хочу одну реализацию. Использование этого решения приведет к большому количеству избыточного кода, потому что я планирую иметь довольно много подклассов (состав против наследования). Я решил, что именно дизайнерское решение - это то, что мне нужно, и я не могу придумать ничего элегантного.

По сути, я хочу, чтобы у классов были отдельные свойства, и чтобы эти свойства были присоединены во время разработки к любому подклассу, который я выберу. Скажем, у меня есть класс "ниндзя". Я хотел бы иметь возможность создавать произвольные подклассы, такие как «серый ниндзя», где у серого ниндзя всегда будет меч и метательные звезды. Тогда возможно 'RedNinja', который всегда будет иметь меч и плащ. Очевидно, мечи, звезды и накидки будут иметь свою собственную реализацию - и здесь я не могу использовать интерфейсы. Самым близким решением, которое я смог найти, был шаблон декоратора , но я не хочу эту функциональность во время выполнения. Является ли лучшее решение ответвлением от этого? Где внутри конструктора класса Black Ninja, я передаю его через конструкторов меча и бросая Star? (это абстрактные классы)

давно не кодировал, и чтение не зашло слишком далеко - простите, если ответ прост.

Редактировать: Ответил на мой собственный вопрос. Я не могу пометить его как «ответ» до завтра. Пожалуйста, дайте мне знать, если есть проблема, которую я не уловил. Все чтение этой проблемы заставило меня сделать это было здорово. Узнал совсем немного.

Ответы [ 4 ]

0 голосов
/ 17 июня 2011

Хорошо, так что добавления через методы расширения будут моим предпочтительным маршрутом. Я не мог понять, как использовать динамические прокси в vb.net (казалось, требовались библиотеки с большим количеством документации, которая не охватывала конкретно то, что мне нужно). Динамические прокси также кажутся более грязными, чем использование методов расширения. Композиция была бы по умолчанию, если предыдущие два не работали.

Итак, одна проблема с методами расширения заключается в том, что код становится немного грязнее, если вы хотите хранить переменные. Не так много, хотя. Другая проблема заключается в том, что все методы расширения должны быть определены в модулях, поэтому код может показаться немного глупым на новый взгляд. Я решу это, определив мой интерфейс и модуль с соответствующим методом расширения в том же файле.

наконец, вот пример кода vb.net, если вы не хотите видеть полноценный пример по ссылке.

Imports System.Runtime.CompilerServices 'for extension methods

Public Interface ISword
End Interface
Public Interface IThrowingStar
End Interface

Module ExtensionMethods

    <Extension()>
    Public Sub swingSword(ByVal hasASword As ISword)
        Console.WriteLine("Sword has been swung")
    End Sub

    <Extension()>
    Public Sub throwStar(ByVal hasAStar As IThrowingStar)
        Console.WriteLine("Star has been thrown")
    End Sub

End Module

Public Class RedNinja
    Inherits Ninja
    Implements IThrowingStar, ISword

    Public Sub New()
    End Sub

End Class

Public MustInherit Class Ninja

    private curHealth as Integer

    Public Sub New()
        curHealth = 100
    End Sub

    Public Function getHP() As Integer
        Return curHealth
    End Function

End Class

Module Module1

    Sub main()

        Console.WriteLine("Type any character to continue.")
        Console.ReadKey()

        Dim a As New RedNinja
        a.swingSword() 'prints "Sword has been swung"
        a.throwStar()  'prints "Star has been thrown"

        Console.WriteLine("End of program - Type any key to exit")
        Console.ReadKey()

    End Sub
End Module
0 голосов
/ 17 июня 2011

Вы хотите, чтобы у классов были отдельные свойства.Рассматривали ли вы именно кодирование?

Например, вам нужен RedNinja, который имеет ниндзя с мечом и накидкой.Итак, определите Ninja, чтобы иметь инвентарь, сделать его доступным через Ninja и передать его через конструктор RedNinja.Вы можете сделать то же самое для поведения.

0 голосов
/ 17 июня 2011

Грязное решение, если вам просто нужно иметь множественное наследование, использует что-то вроде динамических прокси в Java .

Но я полагаю, что вы, вероятно, программируете на C #, и это вопрос, не зависящий от языка, поэтому здесь идет ответ, не зависящий от языка: посмотрите составные и фабричные шаблоны проектирования, которые должны дать вам несколько идей.

Кроме того, может и не понадобиться передавать все в конструктор. Проверьте также IoC .

0 голосов
/ 17 июня 2011

Я однажды сделал подобное приложение. с более ранним компилятором "C ++", который поддерживал только одиночное наследование и вообще никаких интерфейсов.

// base class for all ninjas
public class Ninja {

  // default constructor
  public Ninja() { ... }

  // default destructor
  public ~Ninja() { ... }
} // class

public class StarNinja: public Ninja {

  // default constructor
  public StarNinja() { ... }

  // default destructor
  public ~StarNinja() { ... }

  public void throwStars() { ... }
} // class

public class KatannaNinja: public Ninja {

  // default constructor
  public KatannaNinja() { ... }

  // default destructor
  public ~KatannaNinja() { ... }

  public void useKatanna() { ... }
} // class

public class InvisibleNinja: public Ninja {

  // default constructor
  public InvisibleNinja() { ... }

  // default destructor
  public ~InvisibleNinja() { ... }

  public void becomeVisible() { ... }  
  public void becomeInvisible() { ... }
} // class

public class FlyNinja: public Ninja {

  // default constructor
  public FlyNinja() { ... }

  // default destructor
  public ~FlyNinja() { ... }

  public void fly() { ... }  
  public void land() { ... }
} // class

public class InvincibleNinja: public Ninja {

  // default constructor
  public InvincibleNinja() { ... }

  // default destructor
  public ~InvincibleNinja() { ... }

  public void turnToStone() { ... }  
  public void turnToHuman() { ... }
} // class

// --> this doesn't need to have the same superclass,
// --> but, it helps
public class SuperNinja: public Ninja {

  StarNinja* LeftArm;
  InvincibleNinja* RightArm;
  FlyNinja* LeftLeg;
  KatannaNinja* RightLeg;
  InvisibleNinja* Body;

  // default constructor
  public SuperNinja() { 
    // -> there is no rule to call composed classes,
    LeftArm = new StarNinja();
    RightArm = new InvincibleNinja();
    LeftLeg = new FlyNinja();
    RightLeg = new KatannaNinja();
    Body = new InvisibleNinja();
  }

  // default destructor
  public ~SuperNinja() { 
    // -> there is no rule to call composed classes
    delete LeftArm();
    delete RightArm();
    delete LeftLeg();
    delete RightLeg();
    delete Body();
  }

  // --> add all public methods from peers,
  // --> to main class
  public void throwStars() { LeftArm->throwStars(); }
  public void useKatanna() { RightLeg->useKatanna(); }  
  public void becomeVisible() { Body->becomeVisible() }  
  public void becomeInvisible() { Body->becomeInvisible() }
  public void fly() { LeftLeg->fly() }  
  public void land() { LeftLeg->land() }
  public void turnToStone() { RightArm->turnToStone(); }  
  public void turnToHuman() { RightArm->turnToHuman(); }
} // class

Боюсь, самый близкий пример - шаблон дизайна композиции. Чтобы стать более похожим на наследование, я создаю базовый базовый класс, который разделяют все составные классы, и делаю основной класс, который будет результатом множественного наследования, который имеет копию всех открытых методов компонента классы.

Если вы хотите использовать интерфейсы, чтобы у этого основного класса были все важные методы, затем создайте интерфейс, соответствующий каждому классу создания и реализованный в основном классе.

public interface INinja {
  public void NinjaScream() { ... }
} // class

public interface IStarNinja {
  void throwStars();
} // class

public interface IKatannaNinja {
  void useKatanna();
} // class

public interface IInvisibleNinja {
  void becomeVisible();
  void becomeInvisible();
} // class

public interface CFlyNinja {
  void fly();
  void land();
} // class

public interface IInvincibleNinja {
  void turnToStone() { ... }  
  void turnToHuman() { ... }
} // class

// base class for all ninjas
public class CNinja: public INinja {

  // default constructor
  public CNinja() { ... }

  // default destructor
  public ~CNinja() { ... }

  public void NinjaScream() { ... }
} // class

public class CStarNinja: public CNinja, INinja {

  // default constructor
  public CStarNinja() { ... }

  // default destructor
  public ~CStarNinja() { ... }

  public void NinjaScream() { ... }
  public void throwStars() { ... }
} // class

public class CKatannaNinja: public CNinja, IKatannaNinja {

  // default constructor
  public CKatannaNinja() { ... }

  // default destructor
  public ~CKatannaNinja() { ... }

  public void NinjaScream() { ... }
  public void useKatanna() { ... }
} // class

public class CInvisibleNinja: public CNinja, IInvisibleNinja {

  // default constructor
  public CInvisibleNinja() { ... }

  // default destructor
  public ~CInvisibleNinja() { ... }

  public void becomeVisible() { ... }  
  public void becomeInvisible() { ... }
} // class

public class CFlyNinja: public CNinja, IFlyNinja {

  // default constructor
  public CFlyNinja() { ... }

  // default destructor
  public ~CFlyNinja() { ... }

  public void fly() { ... }  
  public void land() { ... }
} // class

public class CInvincibleNinja: public CNinja, IInvincibleNinja {

  // default constructor
  public CInvincibleNinja() { ... }

  // default destructor
  public ~CInvincibleNinja() { ... }

  public void turnToStone() { ... }  
  public void turnToHuman() { ... }
} // class

// --> this doesn't need to have the same superclass,
// --> but, it helps
public class CSuperNinja: public CNinja,
  IKatannaNinja,
  IInvisibleNinja,
  IFlyNinja,
  IInvincibleNinja  
{

  CStarNinja* LeftArm;
  CInvincibleNinja* RightArm;
  CFlyNinja* LeftLeg;
  CKatannaNinja* RightLeg;
  CInvisibleNinja* Body;

  // default constructor
  public CSuperNinja() { 
    // -> there is no rule to call composed classes
    LeftArm = new CStarNinja();
    RightArm = new CInvincibleNinja();
    LeftLeg = new CFlyNinja();
    RightLeg = new CKatannaNinja();
    Body = new CInvisibleNinja();
  }

  // default destructor
  public ~SuperNinja() { 
    // -> there is no rule to call composed classes
    delete LeftArm();
    delete RightArm();
    delete LeftLeg();
    delete RightLeg();
    delete Body();
  }

  // --> add all public methods from peers,
  // --> to main class
  public void throwStars() { LeftArm->throwStars(); }
  public void useKatanna() { RightLeg->useKatanna(); }  
  public void becomeVisible() { Body->becomeVisible() }  
  public void becomeInvisible() { Body->becomeInvisible() }
  public void fly() { LeftLeg->fly() }  
  public void land() { LeftLeg->land() }
  public void turnToStone() { RightArm->turnToStone(); }  
  public void turnToHuman() { RightArm->turnToHuman(); }
} // class

Я знаю, что это сложное решение, но, похоже, другого пути нет.

Приветствие.

...