Как стать автором
Обновить

Комментарии 30

А не лучше ли использовать для «Может двигаться» примеси? «Moveable», «Draggable», «Droppable», etc.
любопытно. А можно подробнее?
Я сам не флешер, а Джаваскриптер, но языки похожие. В фреймворке LibCanvas есть такая штука, как поведение. Например, Moveable, Draggable, Droppable и достаточно примешать поведение, как объект приобретает соответсвующее свойство. Примешали draggable — его можно двигать мышкой, linkable — можно к нему прикреплять другие элементы и тому подобное. К примеру, какая-то кнопка может выглядеть так:
Button = atom.Class({
  Implements: [Drawable, Clickable, Draggable],
  draw: function () {
    // drawing
  }
});

Теперь она отрисовывается, кликается и тягается.
Это почти одно и то же. При явной композиции нет проблем со случайным перекрытием методов/свойств и с пониманием, откуда что взялось. Но больше писанины.
Логика — великая вещь.
Ошибка с ДвигающиймсПредметом в том, что если сделали уже разделение на Двигающийся и Предмет, то, значит, предметы не двигаются. Иначе не надо было вводить два разных класса — по этим признакам.

Декомпозиция это, знаете ли, сродни поэзии. Все пишут как им прийдет в голову, у ряда людей даже работает, хотя и совершенно по-разному. Парадигм в программировании (http://en.wikipedia.org/wiki/Programming_paradigm) много, и становится со временем все больше.

Я даже поделюсь крамольной, но вещью — чаще всего надо учитывать не только то, что, собственно, надо реализовать, но и кто это будет делать, как сопровождать и что ожидается в будующем.
при изначальном делении на Двигающийся и Предмет подразумевалось что предметы действительно не двигаются. Но с бизнес-правилам свойственна динамичность, и ДвигающийсяПредмет мог появится позднее.

Но суть даже не совсем в этом. В этом специально упрощенном примере показана проблема с сущностью, которая одновременно обладает свойствами нескольких логически обоснованых веток иерархии.
это в ответ на предыдущий комментарий, извиняюсь за путаницу
Бегло посмотрел уроки по pushbutton. Большим минусом данного фреймворка является то, что там слишком часто используются строковые идентификаторы. Стоит в одном месте описаться и работать будет не так как хочешь, но скомпилится все наура. А потом ищи эту «описку».
А разве не выбросится эксепшн «Неизвестный строковый аргумент»?
Может и выбросится, не знаю. Но все-равно было приятней отслеживать такие ошибки на этапе компиляции.
Logger бросит ворнинг в таком случае
Хочу еще добавить, что для этих строковых идентификаторов не будет работать автокомплит, и придется вспоминать: как же я назвал ту сущность?
Я довольно долго сидел на mvc фреймворке PureMVC. Там тоже используются строковые идентификаторы. Чтобы не путаться приходилось использовать строковые константы. Это решает проблему и с автокомплитом и с компиляцией, но все-равно напрягает.
а теперь на чем сидите?
Сейчас сижу на RobotLegs. Хотя мне их механизм инъекций не очень нравится. PureMVC'ый фасад (когда сам забираешь, что тебе нужно) мне больше нравится. Планирую свой велосипед сделать: из RobotLegs взять механизм событий, а из PureMVC глобальный фасад.
антипаттерн называется 'string typing'
Полностью согласен. Именно поэтому отказался от работы с PushButtonEngine, когда понял, что многое там «завязано» на строковых идентификаторах.

Как вариант решения проблемы — создавать классы для хранения статических констант с именами нужных объектов.
НЛО прилетело и опубликовало эту надпись здесь
но от такого константами не избавиться:
render.rotationProperty = new PropertyReference("@Spatial.rotation");
Я понимаю, что таких констант нужно будет создавать очень много и, на мой взгляд, это как бы не очень хорошо вообще, в принципе, что весь фреймворк держится на строковых идентификаторов, но что мешает строку "@Spatial.rotation" присвоить константе SPATIAL_ROTATION?
Ну тут просто большого выигрыша не будет. Константы помогают, когда конкретная строка (например, идентификатор) встречается много раз в проекте. Здесь же "@Spatial.rotation" будет употребляться от-силы 1-2 раза. Следовательно проще, где нужно, прописать строкой.
Можно конечно извратиться и сделать: "@" + SPATIAL + "." + ROTATION. Ну или немного украсить и обернуть это в утилитную функцию:
public static function getPropertyString(entityName:String, propertyName:String):String
{
return "@" + entityName + "." + propertyName;
}


тогда и с автокомплитом проблем не будет.
как бы вы на питоне писали, не представляю
невероятно, но все вызовы там разрешаются динамически
Терминология авторов фреймворка немного удивляет, потому что у этого уже есть несколько названий: aspect, trait, mixin. А слово компонент (которое вообще значит всё, что угодно) только создаёт путаницу.
Хотя нет, я туплю.
На самом деле для этой конкретной реализации «компонент» вполне ничего, потому что это явная аггрегация.
Изначально нельзя делать однотипными свойствами «красный» и «мотыга», если вы понимаете о чем я.
Сущность следовало бы сразу делить тогда на (двигающийся-статичный).
Не про PushButton, но по теме. На случай, если кому пригодится.
На текущий момент в своём фреймворке я пришёл к такой «композиции»:

class DisplayObject
{
	function compose(c:Function):Object
	{
		var target:IComposer = new c;
		target.host = this;
		return target;
	}
	
	// Указываем для класса любое количество объектов составного функционала.
	// Объект составного функционала заключает в себя все необходимые св-ва
	// и методы для управления этим функционалом.
	// Связь объекта составного функционала с "носителем" осуществляется через св-во "host".
	// Благодаря геттеру, экономим память для объектов, не использующих составной функционал.
	
	private var __listeners:Listeners;
	public function get listeners():Listeners
	{
		return this.__listeners = this.__listeners || this.compose(Listeners);
	}
	
	...
	
	private var __interaction:Interaction;
	public function get interaction():Interaction
	{
		return this.__interaction = this.__interaction || this.compose(Interaction);
	}
	
	...

	private var __drag:Drag;
	public function get drag():Drag
	{
		return this.__drag = this.__drag || this.compose(Drag);
	}
	
	...
	
	// И т.д.
	
	// Пример метода для понимания обратной связи
	public function applyState():void
	{
		if (this.__interaction && this.__interaction.highlight)
		// что-то делаем
	}
}

class Interaction implements IComposer
{
	// Носитель может имплементить нужный интерфейс для обращения к нему напрямую.
	public var host:*;
	
	// Примеры методов (геттеры и подробности реализации опущены):
	
	public function set focus(value:Boolean):void
	{
		Focus.instance.current = this.host;
		this.host.applyState();
	}

	...

	public function set highlight(value:Boolean):void
	{
		this.__highlight = value;
		this.host.applyState();
	}
	
	// И т.д.
}

// Применение

myClip.listeners.add(DragEvent.START, ...);
myClip.interaction.active = true;
myClip.drag.start();

// И т.д


Таким образом, закладываем допустимый составной функционал на уровне определения класса (бонусом получаем работающий автокомплит), соблюдаем инкапсуляцию составного функционала и защищаемся от конфликтов, возможных в mixin`ах.

НЛО прилетело и опубликовало эту надпись здесь
наследование — это лишь один из инструментов в руках архитектора.
На практике не всегда можно описать бизнес-логику стройной и непротиворечивой иерархией.
В геймдеве это случается довольно часто, поэтому и предложено использовать в числе прочих компонентный подход, и фреймворк стимулирует и упрощает это
НЛО прилетело и опубликовало эту надпись здесь
Код становится тяжело отлаживать, модифицировать и сопровождать.
Давайте на жабаскрипте писать, зачем вам флеш тогда:) /*troll mode off*/

Я бы за такие примеры по рукам надавал.

А так то да, просто набор инструментов, как хочь так и пользуйся. Меня мвц и наследование вполне устраивает, только моск иногда надо включать когда архитектура рисуется.
В любом случае за ссылочки спасибо, почитаем.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации