Форум Flasher.ru
Ближайшие курсы в Школе RealTime
Список интенсивных курсов: [см.]  
  
Специальные предложения: [см.]  
  
 
Регистрация Блоги Правила Справка Пользователи Календарь Поиск рулит! Сообщения за день Все разделы прочитаны
 

Вернуться   Форум Flasher.ru > Блоги > Котяра

Оценить эту запись

Реализация Fluent interfaces

Запись от Котяра размещена 05.02.2010 в 15:55
Обновил(-а) Котяра 25.02.2010 в 09:49

Сегодня товарищ прислал ссылку на Fluent interfaces.

Вначале я не вкурил, но потом почитал еще и загорелся реализовать нечто подобное на AS3.

тестовый класс:
Код AS3:
package 
{
	import flash.display.Sprite;
	import flash.events.Event;
	import ru.k0t0vich.fluent.Unit;
	import ru.k0t0vich.fluent.UnitList;
 
	/**
	 * Тестоый пример
	 * @author k0t0vich
	 */
	public class Main extends Sprite 
	{
 
		public function Main():void 
		{
			if (stage) init();
			else addEventListener(Event.ADDED_TO_STAGE, init);
		}
 
		private function init(e:Event = null):void 
		{
			removeEventListener(Event.ADDED_TO_STAGE, init);
			// entry point
 
			//Создаем список юнитов
			var enemies:UnitList = new UnitList();
 
			// Заполняем
			for (var i:int = 0; i < 10; i++) 
			{
				var unit:Unit = new Unit(i,i*10);
				enemies.push(unit);
			}
			// весь список
			trace("TOTAL: " + enemies);
			// выполняем
			// все враги выше 2 уровня и высотой меньше 50 стреляют оружием 1 типа с частотой 1.
			enemies.isLevelGreater(2).isZLower(50).doShoot(1, 10);
 
			// выделяем группу врагов старше 8 уровня
			var bigEnemies:UnitList = enemies.isLevelGreater(8);
 
			// список больших врагов
			trace(" Big: " +bigEnemies);
 
		}
 
	}
 
}
Цитата:
TOTAL: [Unit level:0 z:0],[Unit level:1 z:10],[Unit level:2 z:20],[Unit level:3 z:30],[Unit level:4 z:40],[Unit level:5 z:50],[Unit level:6 z:60],[Unit level:7 z:70],[Unit level:8 z:80],[Unit level:9 z:90]
Unit.doShoot > level:3 z:30
Unit.doShoot > level:4 z:40
Big: [Unit level:9 z:90]
ЮнитЛист:
Код AS3:
package ru.k0t0vich.fluent 
{
 
	/**
	 * 
	 * @author k0t0vich
	 */
	public class UnitList
	{
		private var unitList:Array;
 
		public function UnitList($unitList:Array=null) 
		{
			if ($unitList) unitList = $unitList;
			else unitList = [];
 
		}
 
 
		/**
		 * 
		 *		Конкретные методы сортировки
		 */
 
 
		/**
		 * @param	z
		 * @return
		 */
		public function isZLower(z:int):UnitList
		{
 
			var retUnitList:UnitList = new UnitList();
			var len:int = unitList.length;
			for (var i:int = 0; i < len; i++) 
			{
				var unit:IUnit = unitList[i];
				/**
				 * выражение для добавления в список
				 */
				if (unit.z < z)
				retUnitList.push(unit)
			}
 
			return retUnitList;
 
		}
 
 
 
		public function isLevelGreater(level:int):UnitList
		{
			var retUnitList:UnitList = new UnitList();
			var len:int = unitList.length;
			for (var i:int = 0; i < len; i++) 
			{
				var unit:IUnit = unitList[i];
				/**
				 * выражение для добавления в список
				 */
				if (unit.level > level)
				retUnitList.push(unit)
			}
 
			return retUnitList;
		}
 
 
		/**
		 * Конкретный метод - окончание выражения
		 * @param	type
		 * @param	f
		 */
		public function doShoot(type:int,f:int):void
		{
			var len:int = unitList.length;
				for (var i:int = 0; i < len; i++) 
				{
					var unit:IUnit = unitList[i];
					unit.doShoot(type, f);
				}
		}
 
		/**
		 * Композиционные методы для массива
		 * TODO: добавить остальные
		 */
		public function push(unit:IUnit):UnitList
		{
			unitList.push(unit);
			return this;
		}
 
		public function pop(unit:IUnit):IUnit
		{
 
			return unitList.pop() as IUnit;
		}
 
 
		public function toString():String
		{
			return String(unitList);
		}
 
	}
 
}
Юниты
Код AS3:
package ru.k0t0vich.fluent 
{
	import ru.k0t0vich.fluent.IUnit;
 
	/**
	 * ...
	 * @author k0t0vich
	 */
	public class Unit implements IUnit
	{
		private var _level:int;
		private var _z:int=100;
 
		public function Unit($level:int=1,$z:int=0) 
		{
			level = $level;
			z = $z;
		}
 
		public function doShoot(type:int, f:int):void
		{
			trace("Unit.doShoot >  level:"+ this.level+" z:"+z);
		}
 
		public function get level():int { return _level; }
 
		public function set level(value:int):void 
		{
			_level = value;
		}
 
		public function get z():int { return _z; }
 
		public function set z(value:int):void 
		{
			_z = value;
		}
		public function toString():String
		{
			return ("[Unit  level:"+ this.level+" z:"+z+"]");
		}
	}
 
}
Интерфейс юнитов
Код AS3:
package ru.k0t0vich.fluent 
{
 
	/**
	 * ...
	 * @author k0t0vich
	 */
	public interface IUnit 
	{
		function doShoot(type:int, f:int):void;
		function get z():int;
		function get level():int;
	}
 
}
Можно добавить методы:
Код AS3:
greater(field:String,value)
lower(field:String,value)
with(field:String,value)
range(field:String,value1,value2)
where(????), select(???)
использовать
Код AS3:
enemies.greater(Unit.LEVEL,5)
но
Код AS3:
enemies.isLevelGreater(5)
выглядит покрасивше и не даёт гейм-дизайнеру лишних методов)

Использовать можно много где..
сейчас сразу пришло на ум: например зашивать логику уровней в отдельные подгружаемые swf.
Т.е. для каждого уровня есть некий класс CustomFluentScript расширяющий IFluentScript
конструктор:
Код AS3:
CustomFluentScript (hero,enemies)
и некий публичный метод
Код AS3:
render()
вызываемый в нужное время - а в нём уже описываем логику..

FDProject:
fluentInterface.zip
Размещено в ru.k0t0vich
Комментарии 7 Отправить другу ссылку на эту запись
Всего комментариев 7

Комментарии

Старый 05.02.2010 21:29 wvxvw вне форума
wvxvw
 
Аватар для wvxvw
Да, прикольная штука, мне тоже понравилась - я так аналог E4X разборки XML в HaXe сделал. А еще планирую на такую структуру пересадить события (или точнее - сигналы) в AS3.
Старый 06.02.2010 23:42 silin вне форума
silin
 
Аватар для silin
спасибо, очень интересно
Старый 08.02.2010 11:13 Котяра вне форума
Котяра
 
Аватар для Котяра
Некоторое время спустя посмотрел критически на код)
что не нравится:
1) Создание "лишних" объектов UnitList при каждой сортировке и проход по всем юнитам в списке, например в isLevelGreater.
думаю создать список фильтров и добавлять их при вызове сортирующих ф-ций, а в момент конкретного выполнения(doShoot) - применять их к объектам списка.
для вывода списка ввести ф-цию выполнения select() или group()
типа
Код AS3:
var bigEnemies:UnitList = enemies.isLevelGreater(8).select();
2) нужны фильтры типа ИЛИ/НЕ/ИЛИ-НЕ/И-НЕ - сейчас только И. над реализацией думаю.
что-то вроде:
Код AS3:
// для  врагов больше 5 уровня 
//и высотой ниже 50 или для врагов с типом не равным
// 1 и 2 вызвать ф-цию .
			enemies
			.or()
				.and()
					.where(Unit.LEVEL, ">", 5)
					.where(Unit.Z, "<", 50)
				.xor()
					.where(Unit.TYPE, "==", 1)
					.where("type", UnitList.EQ, 1) // альтернативный синтаксис записи
			.doShoot(1, 10);
 
 
 
			// Аналог:
			var len:int = enemies.length;
			for (var i:int = 0; i <len ; i++) 
			{
				var enemy:IUnit = enemies[i];
				if ((enemy.level > 5 && enemy.z < 50)
					||
					(!(enemy.type == 1) || !(enemy.type == 2) ))
				enemy.doShoot(1, 10);
			}
другой вариант:
Код AS3:
// более естественный порядок выражения 
//(нет блоков, но выражение должно быть приведено к ДНФ (дизъюнкция конъюнкций)
// здесь: (AB) || !C || !DE 
enemies 							// для всех врагов
	.where(UnitField.LEVEL, ">", 5)	        // где уровень больше 5
	.where(UnitField.Z, "<", 50)	               // и z < 50
		.or() 					// или 
	.where(UnitField.TYPE, "!=", 1)	// тип не равен 1
		.or() 						// или 
	.where(UnitField.TYPE, "!=", 2)  // тип не равен 2
	.where(UnitField.RANGE, "==", UnitRange.Captain)// и ранг равен капитану
.doShoot(1, 10);					// стрелять оружием 1 типа с частотой 10;
3) синатксис для фильтров можно взять SQL - по сути это и есть выборка из списков..
продолжение следует..
Обновил(-а) Котяра 08.02.2010 в 13:46
Старый 08.02.2010 11:35 Волгоградец вне форума
Волгоградец
 
Аватар для Волгоградец
Все гениальное просто. Интересная штука.
Старый 08.02.2010 17:40 wvxvw вне форума
wvxvw
 
Аватар для wvxvw
Я думаю, чем писать эвалюатор, лучше колбеки использовать, даже не смотря на то, что колбеки в AS медленные, эвалюатор все равно скорее всего будет таким же, если не более медленным. Т.е., например:
where(f:Object->Object->Condition->Boolean)
Что будет значить примерно следующее:
"для всех ранее отфильтрованых элементов применить колбек формата:"
Код AS3:
function(a:Object, b:Object, c:Condition):Boolean
где Condition - это энумератор типа:
Код AS3:
Condition.EQ, Condition.NE, Condition.GT, Condition.LT....
Ну, или по крайней мере я бы так делал.
ну и в зависимости от того, что вернет колбек, включать или не включать их в отфильтрованый список.
Старый 09.02.2010 10:09 Котяра вне форума
Котяра
 
Аватар для Котяра
во первых where - должен возвращать this или как минимум UnitList, а не Boolean.( или я что-то недопонял)
во 2-х я думал использовать энумераторы, и может и буду, просто мне кажется что
Код AS3:
where(UnitField.LEVEL,"<",8)
выглядит естественнее чем
Код AS3:
where(UnitField.LEVEL,Condition.LT,8)
вообще схема такая - создаем массив массивов для ДНФ,
Код AS1/AS2:
dnfArray = [[]];
or() создаёт следующую дизъюнкцию
Код AS3:
currСonjunctionArray = dnfArray.push([])
where(field,condition,value) добавляет в конъюнкцию выражение
Код AS1/AS2:
currСonjunctionArray.push(new Condition(field,condition,value))
при выводе ( конечная ф-ция флюент выражения, например group())
проходимся по списку ВСЕХ юнитов и применяем к каждому последовательно выражения из ДНФ ( конечно с сокращением, по дизъюнктам и конъюнктам)
В общем проще кодом показать.. сегодня как время будет чуток, сделаю.
у меня еще вопрос возник - как лучше поступить с расширением класса UnitList ?-
для потомка придётся переопределять тип всех флюент методов:
Код AS3:
public class CustomUnitList extends UnitList
{
     override public function where(..):CustomUnitList 
     {
             return super.where(...) as CustomUnitList 
     }
...
}
 
}
ну и конечно тип (или интерфейс) юнитов при расширении... передавать класс юнита в качестве параметра конструктора? или еще как?
И еще, имеет ли смысл на вектора перевести?
Обновил(-а) Котяра 09.02.2010 в 10:13
Старый 09.02.2010 17:20 wvxvw вне форума
wvxvw
 
Аватар для wvxvw
Не, недопонял. Применительно к where выглядело бы так:
Код AS3:
.where(userSuppliedCallback)
Где:
Код AS3:
function userSuppliedCallback(unit:Unit, condition:Condition):Boolean
{
    switch (condition)
    {
         case Condition.EQ:
               return unit.id == this._someID;
. . .
    }
    return false;
}
Таким образом оставляем фильтрацию на усмотрение разработчика. Ве равно перектыть все случаи фильтрации не получится, так лучше оставить это конкретному разработчику, чем плодить множество разных типов сравнения и т.п. Особенно еще и потому, что зачастую нужен только какой-то один тип сравнения, а остальные будут лежать мертвым грузом, ИМО.
 

 


Часовой пояс GMT +4, время: 08:21.


Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2019, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.