Подтвердить что ты не робот

Шаблон проектирования для использования вместо множественного наследования

Исходя из фона С++, Im используется для множественного наследования. Мне нравится ощущение дробовика, прямо направленного на мою ногу. В настоящее время я больше работаю на С# и Java, где вы можете наследовать только один базовый класс, но реализовать любое количество интерфейсов (правильно ли я получил терминологию?).

Например, рассмотрим два класса, которые реализуют общий интерфейс, но разные (но требуемые) базовые классы:

public class TypeA : CustomButtonUserControl, IMagician
{
    public void DoMagic()
    {
        // ...
    }
}

public class TypeB : CustomTextUserControl, IMagician
{
    public void DoMagic()
    {
        // ...
    }
}

Оба класса UserControl, поэтому я не могу заменить базовый класс. Оба должны реализовать функцию DoMagic. Моя проблема в том, что обе реализации функции идентичны. И я ненавижу код копирования и вставки.

Возможные решения:

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

Кроме того, пожалуйста, укажите, что я, по-видимому, неправильно понял какие-либо шаблоны, которые я назвал.

В С++ я бы просто создал класс с частными полями, эту реализацию функции и поместил ее в список наследования. Каков правильный подход к С#/Java и тому подобное?

4b9b3361

Ответ 1

Вы можете использовать шаблон стратегии или что-то вроде этого, чтобы использовать , имеет (состав) вместо - (наследование):

public class TypeA : CustomButtonUserControl, IMagician {
    IMagician magicObj = new Magical();
    public void DoMagic() {
        magicObj.DoMagic();
    }
}

public class TypeB : CustomButtonUserControl, IMagician {
    IMagician magicObj = new Magical();
    public void DoMagic() {
        magicObj.DoMagic();
    }
}

public class Magical : IMagician {
    public void DoMagic() {
        // shared magic
    }
}

Существуют другие способы создания экземпляров ваших личных членов IMagician (например, передача их в качестве параметра через конструктор), но выше вы должны начать.

Ответ 2

  • В .Net вы можете применять методы расширения к интерфейсам. Он действительно опрятен, когда это возможно, и применим для вас, потому что это редкий способ применить общую реализацию к интерфейсу. Конечно, подумайте, но это может не сработать для вас, так как вы говорите, что DoMagic работает с большим количеством частных членов. Можете ли вы упаковать эти частные переменные internal, возможно? Таким образом метод расширения может получить к ним доступ.
  • Имеют общую функциональность в другом классе. Если есть логическое место для размещения этой общей функции, передайте свои объекты этому методу другого класса (возможно, это функциональность пользовательского интерфейса, и у вас уже есть помощник интерфейса). Опять же, можете ли вы предоставить личные данные с внутренним/общедоступным свойством? (Разумеется, безопасность/инкапсуляция является предметом беспокойства. Я не знаю, предназначены ли только ваши классы для внутреннего использования или будут публиковаться публично.)
  • В противном случае передайте отдельный функциональный класс (или определенный указатель функции) в определяемый интерфейсом метод. Вам нужно будет немного дублировать код, чтобы передать ваши частные переменные этой ссылке на внешние функции, но, по крайней мере, этого было бы не так много, и ваша реализация была бы в одном месте.
  • Мы можем сделать это слишком сложно. Это не заставит вас чувствовать себя объектно-ориентированными, когда вы будете спать сегодня вечером, но можете ли вы иметь статическую рутину в своей библиотеке где-нибудь, что звонят все разработчики IMagician?
  • В конце концов, адаптер действительно может быть тем, что вы ищете. Менее вероятно, но все же стоит рассмотреть шаблон Decorator.

Если ничего не кажется особенно хорошим, выберите, что лучше, используйте его пару раз и завтра перестройте.:)

Ответ 3

Заменить наследование композицией.

Переместите свою "общую" функцию, чтобы разделить класс, создать экземпляр этого класса и вставить его в объект TypeA и объект TypeB.

Ответ 4

В этом случае ваша кишка правильная. Шаблон адаптера - это то, что вы ищете.

DoFactory имеет хорошие .NET-примеры (которые также должны быть близки к их Java-аналогам):

Шаблон проектирования адаптера в С# и VB.NET

Ответ 5

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

Ответ 6

public interface  IMagician{ /* declare here all the getter/setter methods that you need; they will be implemented both in TypeA and TypeB, right? */ }

public static class MyExtensions {
  public static void doMagic(this IMagician obj)
  { 
           // do your magic here
  }
}   

Теперь проблема заключается в том, что вам действительно нужно использовать частные свойства/методы (в отличие от "внутренних" ), этот подход не будет работать. Ну, на самом деле, вы можете сделать свою магию, если сможете прочитать эти свойства через отражение, но даже если это сработает, это довольно уродливое решение:)

[Обратите внимание, что "doMagic" автоматически станет частью TypeA и TypeB, просто потому, что вы реализуете IMagician - там нет необходимости в реализации)

Ответ 7

Вы можете использовать композицию, чтобы иметь волшебника как свойство типа A и typeB

class Magician : IMagician
{
    public void DoMagic()
    {}
}

Class TypeA : CustomButtonUserControl
{
   //property
   Magician magicianInTypeA
}

Class TypeB : CustomTextUserControl
{
    //property
    Magician magicianInTypeB
}

Ответ 8

abstract class Magical: CustomButtonUserControl
{
    public void DoMagic()
    {
        // ...
    }
}

public class TypeA : Magical
{

}

public class TypeB : Magical
{

}