Комментарии 32
Я бы хотел узнать больше про разработку плагинов.
честно говоря в пример хорошо вписывается шаблон «декоратор», миксы непонятно как будут взаимодействовать между собой, потому что та же версионность потребует модификации CRUD логики
миксы удобны для расширения одинаковой логики на разные объекты, и самое главное — для селектирования логики
т.е. грубо говоря есть объект — запись, в который мы можем подмешать допустим нужную функцию экспорта на лету, будь то сохранение как json, xml или что то ещё
в последнем кейсе делегаты не рулят
на моё ИМХО делегаты вообще тунца любят, ибо работают как декоратор, но через седалищное место и с высоким коэфициентом уныния
за эту весёлую мысль можете вываливать помидоры, с удовольствием буду отстаивать свою точку зрения
миксы удобны для расширения одинаковой логики на разные объекты, и самое главное — для селектирования логики
т.е. грубо говоря есть объект — запись, в который мы можем подмешать допустим нужную функцию экспорта на лету, будь то сохранение как json, xml или что то ещё
в последнем кейсе делегаты не рулят
на моё ИМХО делегаты вообще тунца любят, ибо работают как декоратор, но через седалищное место и с высоким коэфициентом уныния
за эту весёлую мысль можете вываливать помидоры, с удовольствием буду отстаивать свою точку зрения
честно говоря в пример хорошо вписывается шаблон «декоратор»
Каким образом с помощью декоратора можно реализовать написание плагинов для модели сторонними разработчиками? Ведь для этого потребуется изменение кода самой модели.
миксы удобны для расширения одинаковой логики на разные объекты
Да, кстати. В статье я об этом явно не упомянул, но предполагал, что, скажем, древовидность можно и к другой модели прицепить. Впрочем, делегирование тоже катит.
А что вы имеете ввиду под делегатом, если говорить о PHP? Делегат ведь — функтор. Это немного из другой области, мне кажется. Или вы про другой язык?
с делегатом я перегнул, я имел ввиду делегирование
декорирование на примере «расширение CRUD логированием»
на самом деле можно реализовать более чистый декоратор используя интерфейсы, наследование и тонны одинакового кода
P.S.
тема плагинов пробудила интерес
декорирование на примере «расширение CRUD логированием»
< code class="php"> class Logger { protected $crud; function __construct(MyCRUD $o) { $this->crud = $o; // ничего хитрого,запоминаем объект который декорируем } function save() { $this->addToLog(); // сохраняем данные $this->crud->save(); //и запускаем родной метод } function __call($method,$args) { call_user_func_array(array($this->crud,$method),$args); //собственно проксируем вызовы } } //пример юзания $data = new MyCRUD(); $data->setField(42); $data->save(); //сохранили без записи в логе $log = new Logger($data); $log->setField(100500); $log->save(); //а вот теперь данные упадут в лог < /code>
на самом деле можно реализовать более чистый декоратор используя интерфейсы, наследование и тонны одинакового кода
P.S.
тема плагинов пробудила интерес
Если тема не сильно заезжена, расскажите, пожалуйста, о разнице в реализациях примесей в статически и динамически типизированных языках. Я могу ошибаться, но в C# 3.0 очень популярно использовать для этих целей методы-расширения. Поправьте, если не так. Меня больше всего интересуют примеси именно в статически типизированных языках: много ли реализаций, в чём их особенности и т. д.
Спасибо за хорошую статью!
Offtop
Спасибо за хорошую статью!
Offtop
В серьезном, не учебном проекте следует использовать только ту технику, которая вам наиболее знакома и привычна в профессиональном плане (или ту, которая наиболее знакома и привычна большинству программистов, которые в будущем будут работать над проектом)Эх, как же хочется, чтобы побольше людей так считало. Разумеется, это касается не только mixin'ов.
советую посмотреть реализацию Doctrine_Template
От попёрло то тебя, сильно видимо тебя затроллили…
Я почему-то подумал о Behavior из Doctrine или, к примеру, Yii. В код реализации непосредственно расширения я не лазал, но на свое на Yii можно написать так (взято из фреймворка):
В модели:
И в самом классе, расширяющем модель:
Здесь Owner — непосредственно модель. На Doctrine подобное www.doctrine-project.org/projects/orm/1.2/docs/manual/behaviors/en#simple-templates, выбор встроенных там побогаче, чем Timestampable — NestedSet, Searchable, Sluggable, Versionable и т.д.)
В модели:
public function behaviors() {
return array(
'CTimestampBehavior' => array(
'class' => 'zii.behaviors.CTimestampBehavior',
'createAttribute' => 'createDate',
'updateAttribute' => 'updateDate',
'setUpdateOnCreate' => true,
),
);
}
И в самом классе, расширяющем модель:
class CTimestampBehavior extends CActiveRecordBehavior {
// много кода
public function beforeSave($event) {
if ($this->getOwner()->getIsNewRecord() && ($this->createAttribute !== null)) {
$this->getOwner()->{$this->createAttribute} = $this->getTimestampByAttribute($this->createAttribute);
}
if ((!$this->getOwner()->getIsNewRecord() || $this->setUpdateOnCreate) && ($this->updateAttribute !== null)) {
$this->getOwner()->{$this->updateAttribute} = $this->getTimestampByAttribute($this->updateAttribute);
}
}
// много кода, где на основе модели вычисляется, какой таймстемп отдать
}
Здесь Owner — непосредственно модель. На Doctrine подобное www.doctrine-project.org/projects/orm/1.2/docs/manual/behaviors/en#simple-templates, выбор встроенных там побогаче, чем Timestampable — NestedSet, Searchable, Sluggable, Versionable и т.д.)
А примесь не использует делегирования? Зачем сравнивать одно и тоже?
Вот Вам немного переделанный Ваш же пример, если несколько делегатов то это будут как раз ваши примеси
Не важно ведь как Вы их добавляете, важно как оно работает. А принцип работы у обоих подходов один — передача полномочий
Вот Вам немного переделанный Ваш же пример, если несколько делегатов то это будут как раз ваши примеси
Не важно ведь как Вы их добавляете, важно как оно работает. А принцип работы у обоих подходов один — передача полномочий
class Mixin1 { public function doSomething1() {} } class Mixin2 { public function doSomething2() {} } class Aggregator { private $mixins = array(); function __construct() { $this->mixins[] = new Mixin1(); $this->mixins[] = new Mixin2(); } public function __call( $method, $params ) { $result = null; for( $q = 0; $q < count($this->mixins); $q++ ) { if( method_exists( $this->mixin ) ) { // находим первого делегата, отдаем ему полномочия и завершаем выполнение метода $result = call_user_func_array( array( $this->mixins[$q], $method), $params ); break; } } return $result; } } $a = new AggregatorMixed (); $a->doSomething();
Не доисправлял немного =) там еще можно Exception добавить если метода нет ни в классе ни в делегатах
$a = new AggregatorMixed ();
$a->doSomething1();
$a->doSomething2();
$a->doSomething3(); // тут вернет нул, но надо бы ругаться
$a = new AggregatorMixed ();
$a->doSomething1();
$a->doSomething2();
$a->doSomething3(); // тут вернет нул, но надо бы ругаться
Если вы посмотрите на статью немного внимательнее, то делегирование там использует только статическую типизацию. Исходя из этого я и делал сравнение. А в вашем примере для делегирования требуется __call().
Согласен с вами.
Не важно ведь как Вы их добавляете, важно как оно работает. А принцип работы у обоих подходов один — передача полномочий
Согласен с вами.
Делегирование редко использует статическую передачу, обычно это дополнительная «фича», дополнение существующих методов.
у тебя каша в голове. при чём тут типизация?
это легко решаемо про помощи __callStatic
А причем тут __callStatic? Это все равно динамический вызов.
Не согласен:
хотя вызов статических методов вообще фича абсолютно лишняя. Разницы между
Aggregator::doStatic() и Mixin2::doStatic() нет. Просто лишний воркараунд абсолютно никому не нужный.
class Mixin1 {
public function doSomething1() { echo "1\n"; }
}
class Mixin2 {
public function doSomething2() { echo "2\n"; }
public static function doStatic() { echo "static\n"; }
}
class Aggregator {
private $mixins = array();
private static $staticMixins = array();
function __construct() {
$this->mixins[] = new Mixin1();
$this->mixins[] = new Mixin2();
self::$staticMixins[] = 'Mixin2';
}
public function __call( $method, $params ) {
$result = null;
for( $q = 0; $q < count($this->mixins); $q++ )
{
if( method_exists( $this->mixins[$q], $method ) )
{
// находим первого делегата, отдаем ему полномочия и завершаем выполнение метода
$result = call_user_func_array( array( $this->mixins[$q], $method), $params );
break;
}
}
return $result;
}
public static function __callStatic( $method, $params) {
$result = null;
for ($i = 0, $s = sizeof( self::$staticMixins); $i < $s; $i++) {
$class = self::$staticMixins[$i];
if (method_exists( $class, $method)) {
$result = call_user_func_array(
array( $class, $method),
$params
);
break;
}
}
return $result;
}
}
$a = new Aggregator();
$a->doSomething1();
$a->doSomething2();
Aggregator::doStatic();
хотя вызов статических методов вообще фича абсолютно лишняя. Разницы между
Aggregator::doStatic() и Mixin2::doStatic() нет. Просто лишний воркараунд абсолютно никому не нужный.
изменили внутреннюю реализацию класса и поведение плагинов стало непредсказуемым. так держать.
Вы хотите сказать, что такие языки как Ruby вообще не имеют права на существование?
Я не имею ввиду, что я ратую за отсутствие private/protected элементов, но если рассуждать следуя вашей логике, то, скажем, из Propel срочно нужно убрать вызовы вроде findXXXbyYYYAndZZZ потому что они обеспечиваются __call()
Поясните, если не трудно, что есть «мягко-удаляемость».
Я имел ввиду «пометку на удаление» вроде как в 1С. Т.е. материал помечается на удаление, на сайте не показывается, но в базе пока есть. Вроде как удаление файлов в корзину Windows.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Примеси VS делегирование: преимущества и недостатки при реализации «плагинов»