これに関する解決策はありませんでしたので、いくつかの方法を調べました。基本オブジェクトのプロトタイプに他のオブジェクトで定義されたメソッドを追加することにより、ECMA スクリプト スタイルの mixinが存在します。しかし、これは静的型付けの利点がなくなったことを意味します。
静的型システムを回避しないソリューションを探していました。ASMockがバイトコード インジェクションを使用してプロキシ クラスを作成していたことは知っていました。私は過去数日間、 ASMockをハックして、(バイトコード インジェクションを介して) 構成されたクラスでクラスを作成することによって実装できる解決策を思いつきました。
ユーザーの観点から見ると、これには、多くのインターフェースを介してミックスインを使用するオブジェクトを定義することが含まれます。
public interface Person extends RoomObject, Moveable
public interface RoomObject
{
function joinRoom(room:Room):void
function get room():Room
}
public interface Moveable
{
function moveTo(location:Point):void
function get location():Point
}
次に、これらのインターフェイスを表すクラスを定義します。
public class MoveableImpl implements Moveable
{
private var _location:Point = new Point()
public function get location():Point { return _location }
public function move(location:Point):void
{
_location = location.clone()
}
}
public class RoomObjectImpl implements RoomObject
{
private var _room:Room
public function get room():Room { return _room }
public function joinRoom(room:Room):void
{
_room = room
}
}
クラスを作成したい通常の状況では、次のように記述します。
public class PersonImpl implements Person
{
private var _roomObject:RoomObject = new RoomObjectImpl()
private var _moveable:Moveable = new MoveableImpl()
public function get room():Room { return _roomObject.room }
public function joinRoom(room:Room):void { _roomObject.joinRoom(room) }
public function get location():Point { return _moveable.location }
public function move(location:Point):void { _moveable.move(location) }
}
これは、通常のレイアウトであるため、コードを使用して簡単に記述できます。そして、それはまさに、同等のバイトコードを新しいクラスに注入することによって、私のソリューションが行うことです。このバイトコード インジェクション システムを使用すると、次のように Person オブジェクトを作成できます。
public class Main
{
private var mixinRepo:MixinRepository = new MixinRepository()
public function Main()
{
with(mixinRepo)
{
defineMixin(RoomObject, RoomObjectImpl) // associate interfaces with concreate classes
defineMixin(Moveable, MoveableImpl)
defineBase(Person)
prepare().completed.add(testMixins) // the injection is a async process, just liek in ASMock
}
}
private function testMixins():void
{
var person:Person = mixinRepo.create(Person)
var room:Room = new Room('room you can play in')
person.joinRoom(room)
trace('person.room:', person.room)
person.move(new Point(1, 2))
trace('person.location:', person.location)
}
}
現時点では、このシステムは概念実証であるため、非常に基本的で脆弱です。しかし、Scala mixin/traits スタイル システムに AS3 を近づけることが可能であることを示しています。誰かがソリューションを実行して、それがどのように行われたかを調べることに興味がある場合は、コードを保持するgithubプロジェクトを作成しました。
より完全な例は、プロジェクトのwikiにあります。