Pull to refresh

Comments 32

Я бы хотел узнать больше про разработку плагинов.
Спасибо, об этом будет моя следующая статья. Я пока эту писал, мне кое-какие идеи в голову пришли :) Хочу с вами поделиться. Через день-два постараюсь опубликовать.
честно говоря в пример хорошо вписывается шаблон «декоратор», миксы непонятно как будут взаимодействовать между собой, потому что та же версионность потребует модификации CRUD логики

миксы удобны для расширения одинаковой логики на разные объекты, и самое главное — для селектирования логики
т.е. грубо говоря есть объект — запись, в который мы можем подмешать допустим нужную функцию экспорта на лету, будь то сохранение как json, xml или что то ещё
в последнем кейсе делегаты не рулят

на моё ИМХО делегаты вообще тунца любят, ибо работают как декоратор, но через седалищное место и с высоким коэфициентом уныния
за эту весёлую мысль можете вываливать помидоры, с удовольствием буду отстаивать свою точку зрения
честно говоря в пример хорошо вписывается шаблон «декоратор»

Каким образом с помощью декоратора можно реализовать написание плагинов для модели сторонними разработчиками? Ведь для этого потребуется изменение кода самой модели.

миксы удобны для расширения одинаковой логики на разные объекты

Да, кстати. В статье я об этом явно не упомянул, но предполагал, что, скажем, древовидность можно и к другой модели прицепить. Впрочем, делегирование тоже катит.

А что вы имеете ввиду под делегатом, если говорить о PHP? Делегат ведь — функтор. Это немного из другой области, мне кажется. Или вы про другой язык?
с делегатом я перегнул, я имел ввиду делегирование

декорирование на примере «расширение 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
В серьезном, не учебном проекте следует использовать только ту технику, которая вам наиболее знакома и привычна в профессиональном плане (или ту, которая наиболее знакома и привычна большинству программистов, которые в будущем будут работать над проектом)
Эх, как же хочется, чтобы побольше людей так считало. Разумеется, это касается не только mixin'ов.
советую посмотреть реализацию Doctrine_Template
От попёрло то тебя, сильно видимо тебя затроллили…
Я почему-то подумал о Behavior из Doctrine или, к примеру, Yii. В код реализации непосредственно расширения я не лазал, но на свое на Yii можно написать так (взято из фреймворка):

В модели:
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(); // тут вернет нул, но надо бы ругаться
Если вы посмотрите на статью немного внимательнее, то делегирование там использует только статическую типизацию. Исходя из этого я и делал сравнение. А в вашем примере для делегирования требуется __call().

Не важно ведь как Вы их добавляете, важно как оно работает. А принцип работы у обоих подходов один — передача полномочий

Согласен с вами.
Делегирование редко использует статическую передачу, обычно это дополнительная «фича», дополнение существующих методов.
у тебя каша в голове. при чём тут типизация?
Да, да, я уже знаю, что вы — тролль.
это легко решаемо про помощи __callStatic
А причем тут __callStatic? Это все равно динамический вызов.
Не согласен:

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() нет. Просто лишний воркараунд абсолютно никому не нужный.
Под динамическим вызовом в данном случае я имел ввиду вызов метода по имени. Т.е. динамическую типизацию. В моем примере у делегирования типизация статическая, а у примесей — динамическая.
изменили внутреннюю реализацию класса и поведение плагинов стало непредсказуемым. так держать.
своей головы на плечах не имеем?
Да, да, я уже знаю, что вы тролль.
так и запишем: отсутствует
UFO just landed and posted this here
Вы хотите сказать, что такие языки как Ruby вообще не имеют права на существование?
Я не имею ввиду, что я ратую за отсутствие private/protected элементов, но если рассуждать следуя вашей логике, то, скажем, из Propel срочно нужно убрать вызовы вроде findXXXbyYYYAndZZZ потому что они обеспечиваются __call()
Поясните, если не трудно, что есть «мягко-удаляемость».
Я имел ввиду «пометку на удаление» вроде как в 1С. Т.е. материал помечается на удаление, на сайте не показывается, но в базе пока есть. Вроде как удаление файлов в корзину Windows.
Ясно. Сами такое используем, а вот такое название — встретил впервые.
Название кривое :) Но как это называется по-умному, я что-то не знаю :)
Sign up to leave a comment.

Articles