Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.188.40.207] |
|
Страницы: (2) [1] 2 все ( Перейти к последнему сообщению ) |
Сообщ.
#1
,
|
|
|
Приветствую!
Только что закончил читать хорошую книжку по паттернам проектирования "Приемы объектно-ориентированного проектирования / паттерны проектирования". Э.Гамма, Р.Хелм, Р.Джексон, Дж.Влиссиденс, 2001г. Книженция крайне занимательная. Но возник вопрос, "как эмулировать в языке программирования понятие mixin без явной языковой поддержки?" Кому не лениво, прошу привести примеры. В примерах нужно отразить: 1) Реализацию 2) Альтернативный пример, где тот же вопрос решается ... но гораздо ущербнее без сабжа 3) Набор типовых примеров, где это привносит профит не в ущерб производительности Спасибо эдванс!!! |
Сообщ.
#2
,
|
|
|
Примеси, я использую в немного урезанном виде. Там где они дают профит, лично для меня.
Вообще, в C++ их фактически приходится эмулировать. Например, надо мне добавить в множество классов, какое-то одинаковое поле и набор связанных значений. В некоторых случаях, когда поле типизированное, то делаю обычный класс, в других случаях, ещё больше профита с шаблонного класса. class value_mixed { public: typedef value_type; value_type get_value()const { return m_value; } void set_value( const value_type& c_value ) { m_value = c_value; } void set_value( value-type&& r_value ) { m_value = move( r_value ); } protected: value_mixed(const value_type& c_value ): m_value( c_value ) { } ~value_mixed() { } private: value_type m_value; }; Иногда за вроде бы такими простыми вещами, когда надо просто хранить и предоставлять доступ к значению, тянутся всякие дополнительные фишечки. Например, несколько вариантов для получения и задания значения. Конструкторы копирования и переноса, а так же соответствующие операторы. А набивать повторяющийся код во-второй, третий и десятый раз уже задалбывает! Проще сделать отдельный клас, в который вынести некий повторяющийся функционал. Подключается всё так же, как обычный базовый класс. Тем более по сути, это и есть самый обыкновенный базовый класс. Гым. Может возникнуть вопрос, почему я это называю примесью? Есть правило, что наследники базового класса, должны сохранять суть своего предка. Это философия понятности кода. Но когда предок, делает какюу-то плюшку, некую плюшку, а у потомка вообще куча своих посторонних дел, то философия ломается. Чтобы сделать как хочется и обойти филосовскую проблему, просто называем примесью обычный базовый класс. Касательно производительности машинного исполнения, то просадки не будет или же она будет минимальной из-за промаха кэша. Весь профит в упрощении программирования, уменьшении копипасты кода. Но есть более хардкорные примиси. Я их обычно не использую, из-за неоднозначности реализации и даже из-за просадки производительности. Впрочем, бывают весьма интересные случаи. следующий шаблон может использоваться для добавления в класс оператора «!=» при наличии оператора «==»: template <typename T> struct AddNoEq { virtual bool operator==(const T &cmp) const = 0; bool operator!=(const T &cmp) const { return !static_cast<const T*>(this)->operator== (cmp); } }; Простой пример использования для класса комплексных чисел: #include <iostream> struct Complex : public AddNoEq<Complex> { Complex(int re, int im): re_(re), im_(im) { } virtual bool operator==(const Complex& cmp) const { return cmp.re_ == this->re_ && cmp.im_ == this->im_; } // ... private: int re_, im_; }; int main() { Complex a(1, 2), b(2, 3); if (a != b) std::cout << "Так и должно быть" << std::endl; return 0; } Данный метод в более развёрнутом виде используется в библиотеке «Boost operators». Это я с википедии упёр. Если пошаманить, то можно заоптимизировать, отказавшись от виртуализации, в замен на приведение указателя базового класса, на класс потомка, в базовом классе. Плюшки примиси в том, что какой-то код, пишется один раз и множество раз используется. Альтернативой будет многократное набивание одного и того же кода. |
Сообщ.
#3
,
|
|
|
Цитата JoeUser @ 1) Реализацию 2) Альтернативный пример, где тот же вопрос решается ... но гораздо ущербнее без сабжа 3) Набор типовых примеров, где это привносит профит не в ущерб производительности |
Сообщ.
#4
,
|
|
|
Eric-S и Qraizer, пасип, большое! Осознал!
Хочу дождаться ответа и от applegame. |
Сообщ.
#5
,
|
|
|
По мне так в плюсах классические миксины не особо нужны, так как аналогичный функционал можно получить путем наследования в том числе и множественного.
В D я очень редко пользуюсь шаблонными миксинами (строковые миксины - это несколько другое) и во всех таких случаях можно организовать аналогичный функционал при помощи множественного наследования, если бы оно было. |
Сообщ.
#6
,
|
|
|
Цитата applegame @ По мне так в плюсах классические миксины не особо нужны Классические примеси... У меня есть мнение. Не настаиваю на его истинности. Но думаю, что было бы удобно. Да, верно, большую часть примесей, можно эмулировать наследованием. И огромный респект, за множественное и виртуальное наследование. Без них было бы сложно проворачивать некоторые трюки. Но я сталкивался с моментами, когда приходилось делать дополнительную работу, которую в других языках, делают эти самые примеси. Насколько я понимаю, классическая примись, для компилятора, фактически дополняет класс. То есть примись может переопределять виртуальные функции. Но в C++ примись эмулируется базовым классом. А второй базовый класс не может переопределить функции первого базового класса. class worker: public i_worker, protected foo_mixed { public: // переопределение абстрактной функции из i_worker void foo() override { // фактически она реализована в эмуляторе примиси, но поскольку не видна, то её приходится вызывать руками return foo_mixed::foo(); } }; Теряется сахар. Приходится многое добавлять руками. Образуются классические проблемы с множественным и виртуальным наследованием... Попытки как-то разрулить ситуацию, вынуждают отказатся от эмуляции паттерна "примесь". И после такого, возникает вопрос, а не проще ли в этом случае использовать вложенный объект? Всё равно придётся писать руками проксирующие обёртки. Вообщем, мне бы хотелось в C++ настоящие примеси. А то, что есть сейчас, не даёт максимального профита. |
Сообщ.
#7
,
|
|
|
Можно ли утверждать, что паттерн проектирования Decorator полностью реализует функционал классических примесей? Или это все же разные вещи.
|
Сообщ.
#8
,
|
|
|
Цитата JoeUser @ Можно ли утверждать, что паттерн проектирования Decorator полностью реализует функционал классических примесей? Или это все же разные вещи. Вопрос поставлен некорректно. Примесь и декоратор, в конце концов, позволяют сделать объект с дополнительным функционалом. Но примесь расширяет функционал изначального класса. А декоратор, расширяет функционал объекта изначального класса. И тут уже надо смотреть на желаемый результат. В одном случае, только примесь. Во втором случае только декоратор. В третьем и то и другое. |
Сообщ.
#9
,
|
|
|
По сути декоратор "умеет" все то, что и примесь, плюс может еще в рантайме делать еще что-то? Не?
|
Сообщ.
#10
,
|
|
|
Цитата JoeUser @ Ага, только с оверхедом. По сути декоратор "умеет" все то, что и примесь, |
Сообщ.
#11
,
|
|
|
Цитата applegame @ с оверхедом А в чем оверхед и как его оценить в %? |
Сообщ.
#12
,
|
|
|
Цитата JoeUser @ Ну хотя бы в тмо, что декоратор нужно вешать на уже созданный объект, а в случае примеси объекты сами по себе создаются уже "декорированными". Процент, по очевидным причинам, не оценить никак. Тут действует принцип не заниматься преждевременной пессимизацией. Если нужно чтобы все объекты данного типа были декорированы, то имеет смысл задействовать примеси или наследование, а не декоратор. А в чем оверхед и как его оценить в %? |
Сообщ.
#13
,
|
|
|
Ну в принципе да. Одно на этапе компиляции, другое на этапе исполнения.
Хотя ... Если взять пример примесей из JavaScript (если я не ошибаюсь ECMAScript 5-й версии): // примесь var sayHiMixin = { sayHi: function() { alert("Привет " + this.name); }, sayBye: function() { alert("Пока " + this.name); } }; // использование: function User(name) { this.name = name; } // передать методы примеси for(var key in sayHiMixin) User.prototype[key] = sayHiMixin[key]; // User "умеет" sayHi new User("Вася").sayHi(); // Привет Вася Примесь подмешивается не декларативно, а в рантайме исполняемым кодом. |
Сообщ.
#14
,
|
|
|
Цитата JoeUser @ Это такая иммитация примесей. Фактически это декорирование прототипа. Но даже в этом случае все объекты User будут создаваться уже "декорированными", а в случае декоратора кроме создания придется применять "лишний" вызов функции для декорирования свежесозданного объекта. Хотя ... Если взять пример примесей из JavaScript (если я не ошибаюсь ECMAScript 5-й версии): |
Сообщ.
#15
,
|
|
|
Согласен.
|