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

Articles