Не секрет, что ActionScript 3 не поддерживает перегрузки методов. Иногда это может привести к затруднительным ситуациям. Например, у нас есть два интрефейса, в которых объявлены одноименные методы, но с различной сигнатурой, и требуется создать класс, который должен реализовывать оба этих интерфейса. При попытке реализовать классом оба таких интерфейса мы совершенно законно столкнемся с ошибкой. Чтобы быть более конкретным приведу пример.
У нас есть два интерфейса IHuman и IMonster, в которых объявлен метод kill, только в первом случае метод в качестве аргумента принимает IMonster-жертву, а во втором — в роли жертвы должен выступать IHuman. При попытке создать класс Werewolf, который должен являться как и IHuman, так и IMonster мы окажемся в затруднительной ситуации, поскольку как я уже сказал ранее ActionScript 3 не поддерживает перегрузки методов.
В сложившейся ситуации у нас есть два варианта наших действий. Первый вариант предполагает изменение условий задачи, проще говоря мы можем переименовать методы, например killMonster для IHuman и killHuman для IMonster. Если у вас есть возможность пойти по этому пути, то можете не колебаться и смело выбирать его. Второй вариант, исходит из того, что вы не можете изменить имена методов. В этом случае, можно создать два отдельных класса, каждый из которых реализует необходимый интерфейс и использовать их вместе в составе третьего класса Werewolf. Здесь есть маленькая тонкость. Как между двумя этими классами расшарить внутреннюю логику Werewolf. Например, в методе kill этих классов вызвать приватный метод prepareToKill класса Werewolf. В этой ситуации я для себя нашел небольшой workaround — создание класса оборотня.
Для начала нам потребуются выше обзначенные интерфейсы.
-
package
-
{
-
public interface IHuman
-
{
-
function kill (victim:IMonster):void;
-
}
-
}
-
package
-
{
-
public interface IMonster
-
{
-
function kill (victim:IHuman):void;
-
}
-
}
Переходим к созданию класса Werewolf, содержащий в себе как класс реализующий IHuman, так и класс реализующий IMonster. Оба эти классы возможны только как составляющие Werewolf, соответственно их можно смело сделать вложенными, то есть объявить за скобками пакета в том же файле, что описывает собственно сам класс Werewolf.
-
package
-
{
-
public class Werewolf
-
{
-
private var _monster:IMonster = null;
-
private var _human:IHuman = null;
-
-
public function Werewolf()
-
{
-
_monster = new Monster();
-
_human = new Human();
-
}
-
-
public function asHuman ():IHuman
-
{
-
return _human;
-
}
-
-
public function asMonster ():IMonster
-
{
-
return _monster;
-
}
-
}
-
}
-
-
-
internal class Monster implements IMonster
-
{
-
public function Monster ()
-
{
-
}
-
-
public function kill (victim:IHuman):void
-
{
-
trace("human killed");
-
}
-
}
-
-
internal class Human implements IHuman
-
{
-
public function Human ()
-
{
-
}
-
-
public function kill (victim:IMonster):void
-
{
-
trace("monster killed");
-
}
-
}
И так мы имеем класс Werewolf, который может быть использован как IMonster, так и IHuman, для этого необходимо вызвать соответсвующий метод. Теперь вернемся к условиям, а именно, кроме реализации классов Monster и Human, нам требуется расшарить для них внутреннюю логику Werewolf. Так как эти классы могут существовать только в составе Werewolf мы могли бы смело дать им возможность вызывать приватные методы Werewolf. Однако, как мы знаем приватный метод, он на то и приватный, чтобы быть доступным только в рамках своего класса. Но нам никто не мешает создать собственную область видимости. А помогут нам в этом пространтсва имен. Чтобы создать неймспейс, видимый только в пределах наших классов, объявим его как и классы Monster и Human вне пакета. Таким образом методы, объявленные в этом неймспейсе, могут быть вызваны, любым из наших трех классов.
-
package
-
{
-
public class Werewolf implements IWerewolf
-
{
-
private var _monster:IMonster = null;
-
private var _human:IHuman = null;
-
-
public function Werewolf()
-
{
-
super();
-
_monster = new Monster(this);
-
_human = new Human(this);
-
}
-
-
werewolf_private function prepareToKill ():void
-
{
-
trace("prepared for kill");
-
}
-
-
public function asHuman ():IHuman
-
{
-
return _human;
-
}
-
-
public function asMonster ():IMonster
-
{
-
return _monster;
-
}
-
}
-
}
-
-
internal namespace werewolf_private;
-
-
internal class Monster implements IMonster
-
{
-
private var _owner:Werewolf = null;
-
-
public function Monster (owner:Werewolf)
-
{
-
_owner = owner;
-
}
-
-
public function kill (victim:IHuman):void
-
{
-
_owner.werewolf_private::prepareToKill();
-
trace("human killed");
-
}
-
}
-
-
internal class Human implements IHuman
-
{
-
private var _owner:Werewolf = null;
-
-
public function Human (owner:Werewolf)
-
{
-
_owner = owner;
-
}
-
-
public function kill (victim:IMonster):void
-
{
-
_owner.werewolf_private::prepareToKill();
-
trace("monster killed");
-
}
-
}
Таким образом, мы получили метод prepareToKill видимый только в пределах наших классов. Аналогичным образом можно расшарить и прочую логику. На этом можно было бы и остановиться, но есть один момент, каждая из составляющих как Human, так и Monster не являются Werewolf-ом. Решается это достаточно просто. Введем интерфейс IWerewolf, где и обозначим соответствующий API.
-
package
-
{
-
public interface IWerewolf
-
{
-
function asHuman ():IHuman;
-
-
function asMonster ():IMonster;
-
}
-
}
Теперь остается только реализовать этот интерфейс всеми тремя классами.
-
package
-
{
-
public class Werewolf implements IWerewolf
-
{
-
private var _monster:IMonster = null;
-
private var _human:IHuman = null;
-
-
public function Werewolf()
-
{
-
super();
-
_monster = new Monster(this);
-
_human = new Human(this);
-
}
-
-
werewolf_private function prepareToKill ():void
-
{
-
trace("prepared for kill");
-
}
-
-
public function asHuman ():IHuman
-
{
-
return _human;
-
}
-
-
public function asMonster ():IMonster
-
{
-
return _monster;
-
}
-
}
-
}
-
-
internal namespace werewolf_private;
-
-
internal class Monster implements IMonster, IWerewolf
-
{
-
private var _owner:Werewolf = null;
-
-
public function Monster (owner:Werewolf)
-
{
-
_owner = owner;
-
}
-
-
public function kill (victim:IHuman):void
-
{
-
_owner.werewolf_private::prepareToKill();
-
trace("human killed");
-
}
-
-
public function asHuman ():IHuman
-
{
-
return _owner.asHuman();
-
}
-
-
public function asMonster ():IMonster
-
{
-
return this;
-
}
-
}
-
-
internal class Human implements IHuman, IWerewolf
-
{
-
private var _owner:Werewolf = null;
-
-
public function Human (owner:Werewolf)
-
{
-
_owner = owner;
-
}
-
-
public function kill (victim:IMonster):void
-
{
-
_owner.werewolf_private::prepareToKill();
-
trace("monster killed");
-
}
-
-
public function asHuman ():IHuman
-
{
-
return this;
-
}
-
-
public function asMonster ():IMonster
-
{
-
return _owner.asMonster();
-
}
-
}
Проверим что у нас получилось.
-
package
-
{
-
import flash.display.Sprite;
-
-
public class World extends Sprite
-
{
-
public function World()
-
{
-
var werewolf:Werewolf = new Werewolf();
-
-
// "Кастим" werewolf как IMonster и как IHuman
-
trace(werewolf.asMonster() is IMonster);
-
trace(werewolf.asHuman() is IHuman);
-
-
// Проверяем является ли каждая составляющая IWerewolf
-
trace(werewolf.asMonster() is IWerewolf);
-
trace(werewolf.asHuman() is IWerewolf);
-
-
// Ну а здесь уже мистика
-
trace((werewolf.asMonster() as IWerewolf).asHuman() is IHuman);
-
trace((werewolf.asHuman() as IWerewolf).asMonster() is IMonster);
-
-
// Попробуем убить нашим оборотнем как человека так и монстра
-
var knight:Knight = new Knight();
-
var zombie:Zombie = new Zombie();
-
-
werewolf.asMonster().kill(knight);
-
werewolf.asHuman().kill(zombie);
-
}
-
}
-
}
Напоследок напомню, при возможности изменить условия, а именно устранить конфликт путем смены сигнатуры методов, не раздумывайте и выбирайте этот вариант. Описанный прием является скорее крайней мерой и способом сделать код менее прозрачным.












