2016-08-01 3 views
2

Я недавно столкнулся с некоторыми примерами, которые используют абстрактные классы в качестве интерфейсов, но также добавляют заводские конструкторы к абстрактному интерфейсу, чтобы он мог в некотором смысле быть «новым». Например:Преимущества абстрактного класса с заводским конструктором?

abstract class WidgetService { 
    factory WidgetService() = ConcreteWidgetService; 

    Widget getWidget(); 
    void saveWidget(Widget widget); 
} 

class ConcreteWidgetService extends BaseWidgetService implements WidgetService { 

    WidgetService(); 

    Widget getWidget() { 
     // code to get widget here 
    } 

    void saveWidget(Widget widget) { 
     // code to save widget here 
    } 
} 

Обычаи этой службы было бы в какой-либо другой службы или компонента, например, так:

WidgetService _service = new WidgetService(); 

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

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

ответ

2

В вашем примере преимущества ограничены, но в более сложных ситуациях преимущества становятся более ясными.

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

Он может возвращать различные реализации конкретных на основе параметра конструктора:

abstract class WidgetService { 
    WidgetService _cached; 
    factory WidgetService(String type) { 
     switch(type) { 
     case 'a': return new ConcreteWidgetServiceA(); 
     case 'b': return new ConcreteWidgetServiceA(); 
     default: return _cached ??= new DummyWidgetServiceA(); 
     } 
    } 
    Widget getWidget(); 
    void saveWidget(Widget widget); 
} 

Вашего пример, как представляется, препарат будет продлен в конце концов к такому более гибкому подходу.

2

В Dart все классы также являются автоматически интерфейсами. Нет ничего странного в создании экземпляра - «newing up» - «интерфейса», потому что на самом деле он просто создает экземпляр класса.

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

Например:

void main() { 
    var v = new Map(); 
    print(v.runtimeType); 
} 

печатает: _InternalLinkedHashMap - (текущем) реализация Map по умолчанию.

Пользователи основной библиотеки могут создавать и использовать экземпляры Map, не зная и не заботясь о реализации, которую они фактически получают. Авторы библиотеки могут изменить реализацию, не нарушая чей-либо код.

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

Что касается вашего образца кода, WidgetService _service = new WidgetService(); не выводит предупреждение анализатора. См. Это example on DartPad (Примечание. Я исправил пару ошибок в вашем примере).

Я был первоначально смущен:

factory WidgetService() = ConcreteWidgetService; 

вместо:

factory WidgetService() => new ConcreteWidgetService(); 

, но это похоже на работу.

Смежные вопросы