Интерфейсы против наследования: что лучше в этом случае? - PullRequest
4 голосов
/ 11 августа 2010

Предположим, у меня есть класс виджетов:

struct Widget {
    public Color Color { get; set; }
    public int Frobbles { get; set; }
}

Теперь мне нужно создать фабрику для создания этих виджетов, поэтому я создаю WidgetFactory:

abstract class WidgetFactory {
    public virtual Widget GetWidget();
}

Как оказалось, вы можете сделать виджеты из нескольких разных материалов, но получающиеся виджеты в значительной степени одинаковы. Итак, у меня есть несколько реализаций WidgetFactory:

class GoldWidgetFactory : WidgetFactory {
    public GoldWidgetFactory(GoldMine goldmine) {
        //...
    }

    public Widget GetWidget() {
        Gold g = goldmine.getGold();
        //...
    }
}

class XMLWidgetFactory : WidgetFactory {
    public XMLWidgetFactory(XmlDocument xmlsource) {
        //...
    }

    public Widget GetWidget() {
        XmlNode node = //whatever
        //...
    }
}

class MagicWidgetFactory : WidgetFactory {
    public Widget GetWidget() {
        //creates widget from nothing
    }
}

У меня такой вопрос: должен ли WidgetFactory быть абстрактным классом или интерфейсом? Я вижу аргументы в обоих направлениях:

Базовый класс:

  • Реализации являются WidgetFactories
  • Возможно, они смогут совместно использовать функциональность (скажем, метод List<Widget> WidgetFactory.GetAllWidgets())

Интерфейс:

  • Реализации не наследуют никакие данные или функциональные возможности от родительского
  • Их внутренняя работа совершенно другая
  • Определен только один метод

Тем, кто отвечает, это (в настоящее время) не параллельно с какой-либо реальной проблемой, но если / когда мне нужно будет реализовать этот шаблон, было бы полезно знать. Кроме того, «это не имеет значения» является правильным ответом.

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

//create a widget factory
WidgetFactory factory = new GoldWidgetFactory(myGoldMine);

//get a widget for our own nefarious purposes
Widget widget = factory.GetWidget();

//this method needs a few widgets
ConsumeWidgets(factory);

Итак, метод GetGoldWidget() в WidgetFactory - не очень хорошая идея. К тому же, возможно, появление в технологии Widget позволит нам добавлять различные и более экзотические типы виджетов в будущем? Добавлять новый класс для обработки проще и проще, чем использовать метод в существующем классе.

Ответы [ 4 ]

5 голосов
/ 11 августа 2010

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

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

В целом реализация в порядке и действительно абстрактный фабричный шаблон , единственное добавление, которое я бы сделал, это IWidgetFactory:

public interface IWidgetFactory {
    Widget GetWidget();
}

abstract class WidgetFactory : IWidgetFactory {
    //common attributes and methods
}

//Defferent implementations can still inherit from the base abstract class
class GoldWidgetFactory : WidgetFactory {
    public GoldWidgetFactory(GoldMine goldmine) {
        //...
    }

    public Widget GetWidget() {
        Gold g = goldmine.getGold();
        //...
    }
}
3 голосов
/ 11 августа 2010

В этом случае я не вижу пользы в использовании абстрактного класса вместо интерфейса.

Я бы предпочел интерфейсы над абстрактными классами:

  • Они не расходуютсяВаша единственная возможность при наследовании классов
  • Им легче насмехаться
  • Они как-то "чище" (из интерфейса ясно, что должен предоставить разработчик; вам не нужнопроверьте каждый метод, чтобы увидеть, является ли он конкретным, абстрактным или виртуальным)

В этом случае, однако, вы можете легко использовать делегат, поскольку существует только один метод ... в основном Func<Widget>.

Я не согласен с идеей Ларри о том, чтобы просто использовать одну фабрику для непосредственного создания всех виджетов с отдельными методами - так как вы можете передать WidgetFactory как зависимость другому классу, который не нуждается вчтобы узнать об источнике, но ему нужно вызвать CreateWidget в другое время или, возможно, несколько раз.

Однако у вас может быть один виджетt фабрика с несколькими методами, каждый из которых возвращает Func<Widget>.Это дало бы преимущества наличия одного фабричного класса, в то время как также позволяло бы вводить понятие зависимости "фабрика" в зависимости.

0 голосов
/ 11 августа 2010

Честно говоря, что еще, кроме классов Concrete Factory, вы ожидаете наследовать от WidgetFactory? Что-нибудь? ... когда-нибудь? Если нет, то это, вероятно, не имеет значения. Если в будущем вы захотите добавить общий код между ними, то лучшим вариантом будет абстрактный класс. Также я не вижу необходимости в ваших фабричных методах для реализации какого-либо другого интерфейса, кроме вашего метода создания. Так что не имеет значения, является ли он абстрактным или интерфейсным. Все сводится к тому, захотите ли вы в будущем добавить дополнительные функции в будущем к абстрактному классу.

0 голосов
/ 11 августа 2010

Вам не нужно наследование или интерфейс или даже более одного класса.Одна фабрика должна создавать все виды виджетов;Вы можете просто передать материалы в качестве параметра методу создания.Идея состоит в том, чтобы скрыть аспекты различной конструкции объектов от вызывающей стороны - создавая кучу разных классов, вы выставляете это, а не скрываете.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...