PDA

Просмотр полной версии : Архитектура. Заменить if-ы на паттерн.


callme
13.05.2015, 22:33
class Bullet
class Arrow extends Bullet
class Ball extends Bullet

В классе Main есть arrowsPool и ballsPool. Когда игрок стреляет, я беру из этих пулов пули и кладу в массив bullets. По энтерфрейму обрабатываю пули в этом массиве.

Когда пули попадают в цель, я вынимаю их из массива bullets и возвращаю в нужный пул:

var bullet:Bullet = _bullets[i];
var pool:Pool;
if (bullet is Arrow) {
pool = arrowsPool;
}
else if (bullet is Ball) {
pool = ballsPool;
}
pool.push(bullet);


1. Можете поучить меня архитектуре? Мне кажется здесь можно обойтись без ifов. Применить какой-нибудь паттерн. Но не могу придумать.

2. И еще: можно было бы заменить классы Arrow и Ball на createArrow():Bullet и createBall():Bullet. Но непонятно как потом определить в какой из пулов их вернуть.

udaaff
14.05.2015, 00:10
private static var _pools:Dictionary = new Dictionary();
...
public static function registerPool(pool:IPool, type:Class):void
{
if (!pool)
...

if (!type)
...

_pools[type] = pool;
}
...
var pool:IPool = _pools[Object(object).constructor];

callme
14.05.2015, 08:46
Спасибо за помощь!

package
{
import flash.utils.Dictionary;

public class PoolsStorage
{
static public function registerPool(pool:IPool, type:Class):void
{
if (!pool)
return;

if (_pools[type] == pool)
return;

_pools[type] = pool;
}

static public function unregisterPool(pool:IPool, type:Class):void
{
if (!pool)
return;

if (_pools[type] != pool)
return;

delete _pools[type];
}

static public function getPool(type:Class):IPool
{
return _pools[type];
}


static private const _pools:Dictionary = new Dictionary();
}
}


Покритикуете?

И еще вопрос. Почему в классе PoolsStorage методы статические? В этом есть такой плюс как не нужно в Main в приватной переменой хранить экземпляр класса PoolsStorage. Но ведь PoolsStorage.unregisterPool() теперь можно вызвать откуда угодно.

В First.as

PoolsStorage.registerPool(pool001, BulletType.ARROW);

В Another.as

PoolsStorage.registerPool(pool001, BulletType.ARROW);
PoolsStorage.unregisterPool(pool001, BulletType.ARROW);

Теперь First.as не понимает почему отрегистрировался пул.

wvxvw
14.05.2015, 11:34
Можно пули создавать со ссылкой на массив, куда их нужно складывать. Или, еще лучше, чтобы не зависеть от имени названия поля / метода, можно сделать так:
class Bullet {
public function visit(pool:Pool):Array {
throw "not implemented";
}
}
class Arrow extends Bullet {
public override function visit(pool:Pool):Array {
return pool.arrows;
}
}
class Ball extends Bullet {
public override function visit(pool:Pool):Array {
return pool.balls;
}
}
class Pool {
public function get arrows():Array { return this._arrows; }
public function get balls():Array { return this._balls; }
public function putBack(bullets:Array):void {
for each (var bullet:Bullet in bullets) bullet.visit(this).push(bullet);
}
}
Можно сказать что это "посетитель", но в очень свободной интерпретации.
Смысл в том, что таким образом мы избегаем повторяющихся проверок (в нашем коде, на самом деле они никуда не уходят).

Korchy
14.05.2015, 11:43
class Bullet
class Arrow extends Bullet
class Ball extends Bullet

В классе Main есть arrowsPool и ballsPool.

Если класс Arrow и класс Ball наследуют класс Bullet - зачем нужно два массива, чтобы хранить шары и стрелы отдельно? Все объекты (шары и стрелы) должны быть одного типа Bullet и храниться в одном массиве. И не будет трудности с разными массивами при возвращении.

callme
14.05.2015, 11:55
wvxvw, спасибо за ответ, он сложный для меня. Я обдумаю его и отпишусь.

Korchy, шары и стрелы визуально отличаются. Когда игрок нажимает кнопку X - вылетает шар, когда кнопку Y - вылетает стрела. Если я сделаю например 100 шаров и 100 стрел, и засуну их в один пул, то как мне из этого пула вынуть именно стрелу когда игрок нажмет Y.

Korchy
14.05.2015, 12:08
Korchy, шары и стрелы визуально отличаются. Когда игрок нажимает кнопку X - вылетает шар, когда кнопку Y - вылетает стрела. Если я сделаю например 100 шаров и 100 стрел, и засуну их в один пул, то как мне из этого пула вынуть именно стрелу когда игрок нажмет Y.

Ну и что, что отличаются? Пулл один для объектов Bullet. Из него и будет извлекаться объект типа Bullet. А визуальные различия прописываются в классах Arrow и Ball, наследующих от Bullet. Классический пример полиформизма.

callme
14.05.2015, 12:11
Я не понимаю. Вытащил я из этого массива пулю array.pop() . И полетела вместо шара стрела.

udaaff
14.05.2015, 13:20
И еще вопрос. Почему в классе PoolsStorage методы статические? В этом есть такой плюс как не нужно в Main в приватной переменой хранить экземпляр класса PoolsStorage. Но ведь PoolsStorage.unregisterPool() теперь можно вызвать откуда угодно.
Можно, ровно как и выпрыгнуть из окна, так как на нем решеток нету. Иногда достаточно соблюдать правила, не обременяя себя защитой от дурака. Если целесообразно вам ограничить доступ для этого метода по каким-то причинам, делайте.

Добавлено через 8 минут
Выкидывайте ошибку, если пул для требуемого объекта не был определен. Этого, имо, должно быть достаточно.

callme
14.05.2015, 13:31
udaaff
Как вы определяете эту грань, когда глобальные данные допустимы, а когда нет?

wvxvw
чтобы не зависеть от имени названия поля / метода

Я не смог понять о каких именно полях идет речь. Можете сказать чем хуже следующий вариант, чем вариант с посетителем?

public class Main extends Sprite {
public function Main() {
var pool:BulletPool = new BulletPool(100, createArrow);
arrow.pool = pool;
_bullets.push(arrow);

var bullet:Bullet = _bullets[0];
bullet.pool.push(bullet);
}
}

public class Bullet {
public function Bullet() {

}
public var pool:BulletPool;
}

public function createArrow():Bullet {

}

udaaff
14.05.2015, 16:26
Как вы определяете эту грань, когда глобальные данные допустимы, а когда нет?
Исходя из целесообразности. Себя при этом в качестве обезьяны с гранатой не рассматриваю. Стараюсь не усложнять архитектуру, там где достаточно не совать пальцы в розетку.

Писал одно приложение, старался следовать канонам жанра, протяжка данных по ссылка во вью, все дела, чтобы упаси боже никто не изменил ничего из другого места. И что в итоге, след. программист, когда стал работать с этим прилом после меня, без всякого стеснения менял данные прямо из вида. И как тут быть? Ридонли все сделать? Возможно ли такое, вообще.

Поэтому надо соблюдать правила прежде всего. А ограничения исходя из целесообразности.

wvxvw
14.05.2015, 16:28
В рамках поставленой задачи разницы нет. Разница между моим решением и вашим кодом в следующем:

В вашем коде если Main поменят pool (которым он управляет) ссылка на pool в Bullet устареет, и arrow.pool.push() будет работать неправильно потому что будет добавлять объект в неправильный (устаревший) массив.
В моем коде такой проблемы не существует, т.как ссылка на массив каждый раз запрашивается у объекта, который этим массивом управляет.

В чем это практически может выразиться? Например, мы хотим использовать несколько массивов для хранения разных наборов стрел и шаров. Для нас может быть не критично, что один и тот же шар или стрела будут в разных массивах (например, эти массивы отображают две независимые друг от друга карты, или уровни игры). В таком случае имея возможность хранить только одну ссылку на pool в шарах / стрелах становится препядствием.

callme
14.05.2015, 16:58
Понятно, спасибо обоим за разъяснения!

Rzer
14.05.2015, 17:14
var arrowPool:Array = [];
var ballPool:Array = [];

function createPools(){

while(arrowPool.length < 10) {
var arrow:Arrow = new Arrow();
arrow.pool = arrowPool;
arrowPool.push(arrow);
}

//По аналогии с ballPool

}


//Получаем стрелу
function getArrow():Bullet{
return arrowPool.pop();
}

//Возвращаем пулю в пул (стрелу или шар)
bullet.pushBack();


class Bullet{
public var pool:Array;

function pushBack():void{
pool.push(this);
}
}

callme
14.05.2015, 17:24
Rzer, на самом деле я уже привел почти такое же решение в 10 (http://www.flasher.ru/forum/showpost.php?p=1182342&postcount=10)-ом посте, просто опустил незначимые для wvxvw моменты.

Но все равно спасибо за помощь.

Korchy
14.05.2015, 19:23
Я не понимаю. Вытащил я из этого массива пулю array.pop() . И полетела вместо шара стрела.

Да, наверное я не прав. Я забыл, что в пулле объекты хранятся в виде предсозданных экземпляров.