Здравствуйте, IB, Вы писали:
A>>Определяет интерфейс для создания объекта, но оставляет подклассам решение о том, какой класс инстанцировать. Фабричный метод позволяет классу делегировать инстанцирование подклассам.
IB>....
A>>Учи матчасть, порождать объекты реализующие определенный интерфейс очень даже объязательно. Цитаты выше.
IB>Я прям даже теряюсь... Ты сам-то хоть понял, что процитировал и что сказал? В твоей цитате речь про фабричный метод, то есть, про порождающую сущность, а пишешь ты про интерфейс порождаемой сущности... Это даже не теплое с мягким, это просто винегрет какой-то.
Боже... ты неисправим... Винигрета никакого нет, просто я с дуру не так расставил ударения, а ты прям весь расцвёл. Зря я тебе такую зацепку дал. Классы реализующие фабричный метод (порождающие) организованы в иерархию, но и порождаемые классы тоже. Вот вобщем самая правильная цитата, тут ты уже не отвертишься.
Используйте паттерн фабричный метод, когда: классу заранее неизвестно, объекты каких классов ему нужно создавать; класс спроектирован так, чтобы объекты, которые он создает, специфицировались подклассами; класс делегирует свои обязанности одному из нескольких вспомогательных подклассов, и вы планируете локализовать знание о том, какой класс принимает эти обязанности на себя.
IB>Еще раз, по буквам, это если в танке люк заварен. Есть порождающая сущность (фабрика), есть порождаемая (объект). Смысл конкретно паттерна "фабричный метод" предоставить интерфейс фабрики для создания объекта. Об этом же и говорит второе название "виртуальный конструктор", понимаешь, конструктор виртуальный, а не объект.
см цитату выше.
A>>На мой взгляд излишней связности нет, а если есть то покажи.
IB>Доступ к приватным полям тебя не смущает? А кто тут дефирамбы инкапсуляции пел?
template <typename T>
class singleton
{
public:
static const T & get_instance()
{
static T instance;
return instance;
}
};
class worker
{
friend singleton<worker>;
private:
worker()
{
}
};
singleton<worker> s;
const worker & w = s.get_instance();
Где тут доступ к приватным
полям? Тут полей вообще нет. Доступ из singleton<worker> к приватному конструктору worker::worker() это фича, а не недостаток.
IB>Ок, буквоед ты наш, назови это "архитектурный принцип", на вопрос-то можешь ответить?
Если по простому, IoC придумали чтобы избежать циклических зависимостей, для разрешения которых приходится, обычно, порождать лишние модули (попутно, там, обычно, особенности реализации наружу торчат). IoC бывает двух видов, dependecy injection и колбеки.
Например если у меня есть некоторая коллекция (скажем,
реализованная как связанный список, но логически, по смыслу хранимых данных, списком не являющаяся), я могу интерфейс перечисления описать как
node * get_first();
node * get_next();
data * get_data(node *);
породив помимо модулей linked_list и application, модуль linked_list_declarations описывающий тип node от которого зависят linked_list и application, а могу как
enumerate_nodes(bool (* enum_proc)(data *));
и это очевидно лучше, потому что
Клиент больше не зависит от типа node.
Нет вспомогательных модулей.
Клиент больше не доступны несущественные детали реализации.
Dependency injection ИМХО зло, я уже говорил, что кортежи внедрённых зависимостей в параметрах каждого метода (запоминать нельзя, мало ли...) ни к чему хорошему не приводит. Настроили журналирование в файл на самом высоком уровне (функция main если угодно) и давай этот объект тащить до низу. Особенно весело, если раньше журналирования не было и надо весь проект переписать, чтобы оно вдруг было. Так как весь проект переписывать лень, появляются хаки (всякие thread local переменные, описанные Cyberax). В этом смысле singleton выглядет куда привлекательнее, так как позволяет добавлять в нижние уровни функциональность контроллируемую в верхних уровнях, не протаскивая её через все промежуточные уровни.
Callback'и на мой взгляд более приемлемое решение (особенно делегатами), но практическая реализация нередко затруднена. Кроме того этот самый указатель/делегат надо опять таки передать через все уровни и реальная разница в сложности реализации и поддержки между Dependency injection и Callback стирается.
В целом вырисовывается следующая картина:
// singleton
void level3()
{
singleton<logger>::log("level 3!");
}
void level2()
{
level3();
}
void level1()
{
level2();
}
int main()
{
singleton<logger>::initialize(bla-bla-bla);
level1();
}
// dependency injection
void level3(logger log)
{
log.log("level 3!");
}
void level2(logger log)
{
level3(log);
}
void level1(logger log)
{
level2(log);
}
int main()
{
logger log;
log.initialize(bla-bla-bla);
level1(log);
}
Нередко level1 и level2 написаны вообще не мной, это может быть, например, код какой-то библиотеки которая вызывает level3 как callback. В такой ситуации синглтон оказывается очень кстати.
Сервисы это круто, особенно их .Net реализация с автоматическим приведением типов, но эти сервисы надо где-то регистрировать и откуда-то брать. А когда начинаешь думать где регистрировать, откуда брать... Хорошо если есть главный объект-контейнер, а если нет такого объекта?