2010-10-29 2 views
1

У меня есть следующий класс:Кто должен строить объекты в этом сценарии?

class PluginLoader 
{ 
public: 
    PluginLoader(Logger&, PluginFactory&, ConflictResolver&); 
    //Functions. 
private: 
    //Members and functions 
}; 

Logger, PluginFactory и ConflictResolver классы являются все интерфейсные классы, которые будут реализованы в приложении. Я создаю единственный объект PluginLoader на верхнем уровне в основной программе. Logger может быть известен на этом уровне. Но, PluginFactory и ConflictResolver используются только внутри класса PluginLoader. Поэтому создание их объектов на верхнем уровне кажется уродливым. Что мне делать? Я могу изменить конструктор, если он понадобится, хотя я бы хотел сохранить его таким, каким он есть. Любые комментарии приветствуются. Благодарю.

+0

Я смущен, что вы спрашиваете и что делаете. Если вам не нравится Injection Dependency (похоже, вот что вы здесь делаете, но я могу ошибаться?), Просто используйте шаблон посредника: http://en.wikipedia.org/wiki/Mediator_pattern. Я не оставлю это в качестве ответа, потому что я немного смущен. –

+0

Можете ли вы уточнить, что вы пытаетесь сделать. Правило большого пальца: не создавайте ненужный объект, если это необходимо. и более того, работая на C++, где нет сборщика мусора, в отличие от других языков. –

+0

@ITion Я не согласен. OO - это балансировка работы над разными объектами. Вы не должны бояться использовать больше классов! И в C++ вы не привязаны к использованию динамического распределения, и даже когда вы его используете, вы должны полагаться на интеллектуальные указатели. –

ответ

2

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

  • Вы должны инкапсулировать как можно больше
  • Вы не должны создавать проводные из необходимых объектов в вашей реализации.

В вашем случае вы сделали правильно (по крайней мере, с видимых интерфейсов), придерживаясь второго принципа. Это хороший пример инъекции зависимостей. Приятно повторно подключить PluginLoader от ответственности за создание PluginFactory и ConflictResolver.

Теперь вы считаете, что нарушаете первый принцип, и это верно в некоторой степени. Итак, вернитесь и более подробно рассмотрите проблему дизайна.

Что именно требуется PluginLoader? Ему просто нужна ссылка на Объект PluginFactory и ConflictResolver. Но им не нужно создавать их самостоятельно. Как решить эту проблему?

Есть два способа я могу думать:

  • есть еще один уровень абстракции, как это было сделано в builder pattern. Здесь комплекс (или , очевидно, комплекс в вашем случае) здание и процесс проводки перемещается в объект-строитель. Основная программа - не беспокоиться о том, как это делается. Он просто запускает процесс .
  • Есть еще одна фабрика, которая может создавать вспомогательные объекты, такие как PluginFactory и ConflictResolver, и пусть PluginLoader знает только об интерфейсе этой новой фабрики. Основная программа может создать конкретный экземпляр этой фабрики и перейти к PluginBuilder.

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

Все самое лучшее.

+0

Спасибо. Это информативно. – Lijo

0

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

Я бы не использовал необработанный указатель, но auto_ptr, scoped_ptr или unique_ptr должен работать.

Если вы создадите эти объекты в конструкторе или даже позже при первом использовании (ленивая конструкция), вам не нужно передавать что-либо в конструктор, и вы можете удалить эти параметры.

Edit:

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

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

+0

Итак, ответственность за создание конкретных объектов для интерфейсов лежит на верхнем уровне? – nakiya

+0

@nakiya: Ну, объясните, где еще могут создать эти объекты? –

+0

Да, я больше ничего не вижу. Хотелось бы, чтобы класс 'PluginLoader' мог создавать объекты, которые он сам использует сам. Возможно, передав 'MainClass' в качестве параметра конструктору' PluginLoader', что опять-таки означало, что должен быть способ узнать, что создать из 'MainClass'. Наверное, я бы предпочел пойти с этим. – nakiya

0

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

0

... классы все интерфейсные классы, которые будут реализованы в приложении ...

... PluginFactory и ConflictResolver используются только внутренне в классе PluginLoader ...

Это в основном означает, что, хотя они используются только внутри, вы должны выявить существование PluginFactory и ConflictResolver, так как вам нужно, чтобы клиент реализовал их, а затем передал пользовательские реализации до PluginLoader в некотором роде (в данном случае через конструктор).

Однако объявление об именованном объекте на верхнем уровне (как статическое, так и автоматическое внутри main) можно избежать (не уверен, почему это нужно было бы сделать), заменяя ссылки указателями (желательно интеллектуальный вид, unique_ptr будет только инструментом для работы):

class PluginLoader 
{ 
public: 
    PluginLoader(
     std::unique_ptr<Logger> logger, 
     std::unique_ptr<PluginFactory> plugin_factory, 
     std::unique_ptr<ConflictResolver> conflict_resolver 
    ) 
     : logger(std::move(logger)) 
     , plugin_factory(std::move(plugin_factory)) 
     , conflict_resolver(std::move(conflict_resolver)) 
    { 
    } 
private: 
    std::unique_ptr<Logger> logger, 
    std::unique_ptr<PluginFactory> plugin_factory, 
    std::unique_ptr<ConflictResolver> conflict_resolver 
}; 

и создать PluginLoader так:

PluginLoader plugin_loader(
    std::unique_ptr<CustomLogger>(new CustomLogger(/* ... */)) 
    std::unique_ptr<CustomPluginFactory>(new CustomPluginFactory(/* ... */)) 
    std::unique_ptr<CustomConflictResolver>(new CustomConflictResolver(/* ... */)) 
); 

где CustomLogger, CustomPluginFactory и CustomConflictResolver являются реализаций Logger, PluginFactory и ConflictResolver соответственно.

1

Я думаю, что решение должно использовать механизм Dependency Injection: вы можете выбрать конкретный PluginFactory и ConflictResolver, изменив только строку, возможно, загрузив ее из файла конфигурации.

Я разработал библиотеку инъекций зависимостей в C++ с именем Wallaroo.В приложении вы можете объявить класс PluginLoader, используя этот код:

REGISTERED_CLASS(PluginLoader, void, void) 
{ 
    ... 
private: 
    Plug<Logger> logger; 
    Plug<PluginFactory> pluginFactory; 
    Plug<ConflictResolver> conflictResolver; 
}; 

то, что вы можете решить конкретные классы для использования и их проводки в файле XML:

<catalog> 

    <!-- build the objects of your app --> 

    <object> 
    <name>loader</name> 
    <class>PluginLoader</class> 
    </object> 

    <object> 
    <name>logger</name> 
    <class>MyConcreteLogger</class> 
    </object> 

    <object> 
    <name>factory</name> 
    <class>MyConcretePluginFactory</class> 
    </object> 

    <object> 
    <name>resolver</name> 
    <class>MyConcreteResolver</class> 
    </object> 

    <!-- wiring of the objects: --> 

    <relation> 
    <source>loader</source> 
    <dest>logger</dest> 
    <role>logger</role> 
    </relation> 

    <relation> 
    <source>loader</source> 
    <dest>factory</dest> 
    <role>pluginFactory</role> 
    </relation> 

    <relation> 
    <source>loader</source> 
    <dest>resolver</dest> 
    <role>conflictResolver</role> 
    </relation> 

</catalog> 

или с помощью этого кода :

catalog.Create("loader", "PluginLoader"); 
catalog.Create("logger", "MyConcreteLogger"); 
catalog.Create("factory", "MyConcretePluginFactory"); 
catalog.Create("resolver", "MyConcreteResolver"); 

wallaroo_within(catalog) 
{ 
    use("logger").as("logger").of("loader"); 
    use("factory").as("pluginFactory").of("loader"); 
    use("resolver").as("conflictResolver").of("loader"); 
} 
0

Мы использовали аналогичный проект для приложения, использующего созданную dll. Регистратор известен в основном приложении и используется в dll. Однако есть объекты, которые используются только внутри DLL. Я все еще не понимаю, почему они вообще являются частью интерфейса для PluginLoader, если они используются только внутренне. Зависит ли какой-либо элемент от других объектов от внешних интерфейсов?

Что я понимаю, почему бы не использовать фабрики для обоих параметров, PluginFactory, ConflictResolver и не передавать их как параметры вообще?

например. PluginLoader (регистратор &);

Затем в реализации

PluginLoader(Logger& logger){ 
    Factory::PluginFactory::GetInstance().useInstance;// example 
    Factory::ConflicResolver::GetInstance().useInstance;// exmaple 
} 

Или, может быть, вы можете уточнить?

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