次は、メソッドの追加だ。クラスベースのオブジェクト指向言語で、メソッドを追加する構文を比較する。
新しいクラス B を宣言して、メソッドを追加する場合。普通の継承だね。
class B : public A {
public:
void method1();
};
void B::method1() {}
|
class B extends A {
public void method1() {}
}
|
@interface B : A
{}
- (void)method1;
@end
@implementation B
- (void)method1 {}
@end
|
A subclass: #B
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Category-Name'!
!B methodsFor: 'message category'!
message1
^ self! !
|
define class <b> (<a>)
end class <b>;
define method method-1( b :: <b> ) => ()
end method;
|
class B < A
def method1
end
end
|
{ package B;
our @ISA = qw/A/;
sub new {
my $class = shift @_;
return bless({}, $class);
}
sub method1 {}
}
|
globals _AddSlotsIfAbsent: (| b = () |).
b _Define: (| parent* = a. slot2 = (^self) |)
|
prootB := {_proto: protoA,
method1: func() begin /* 処理 */ end};
|
AppleScriptのスクリプトオブジェクトは、parentプロパティにより親オブジェクトを指定する。
厳密に言うと、これは特定のインスタンスに対する動的な機能追加である。
on B()
script B
property parent : A()
on method1()
end method1
end script
end B
|
継承を使わないで、既存のクラスにメソッドを追加するもの。フレームワークがある場合は、その内部のクラスも拡張できる。
|
|
|
カテゴリを使う。
@interface A (additionalMethod)
- (void)method2;
@end
@implementation A (additionalMethod)
- (void) method2 {}
@end
|
!A methodsFor: 'message category'!
message2
^ self! !
|
define method method-1( a :: <a> ) => ()
end method;
|
class A
def method2
end
end
|
{ package A;
sub method2 {}
}
|
a _AddSlots: (| slot2 = (^self) |)
|
protoA.method2:= func() begin /* 処理 */ end;
|
on handler3()
end handler3
set handler3 of a to handler3
|
実行時に、動的にクラスにメソッドを追加するものだ。
|
|
|
直接はできないけど、forwardInvocation: と methodSignatureForSelector: をオーバーライドすることで、実現できる。
|
| code |
code := 'message3',
String cr,
String tab,
'^ self'.
A compile: code classified: 'message category'
notifying: nil
|
Generic Function を作成する。局所変数に代入してみた。
let method-2 = make(
<generic-function>, required: list(<a>));
local method m0 (a :: <a>)
a.slot-0 := "Goodbye";
end;
add-method(method-2, m0);
|
class A
def method2
end
end
または、
a.instance_eval("def_method\nend")
この場合、特異メソッドになる。
|
my $code = sub { print "Hellow.\n" };
*A::method3 = $code;
|
a _AddSlots: (| slot3 = (^self) |)
|
imp := func() begin /* 処理 */ end;
SetSlot(protoA, intern("method3"), imp);
|
on handler3()
end handler3
set handler3 of a to handler3
|
あるクラスで、現在あるクラスを置き換えるもの。この機能は、確かに実際のプログラミングでは必要ないかもしれない。やり方によっては、とても凶悪な動作をすることもある。でも、動きを理解しているならばとても便利なものだし、クラスの拡張の柔軟性を比較するにはいいものかもしれない。
|
|
|
poseAsClass: を使う。この場合、PseudoA は A のサブクラスでなくてはいけなくて、インスタンス変数を追加してはいけない。これ以降、A へのメッセージは PseudoA に送られる。
@interface PseudoA : A
@end
@implementation PseudoA
+ (void)load {
[PseudoA poseAsClass:[A class]];
}
- (void)method0 {}
@end
|
become: を使う。この場合、PseudoA は A のサブクラスであってはいけない。
Object subclass: #PseudoA
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Category-Name'!
!PseudoA methodsFor: 'message category'!
message0
^ self! !
!PseudoA class methodsFor: 'class initialization'!
initialize
self become: A! !
PseudoA initialize!
|
|
|
|
PseudoA は普通に定義しておき、その後に package を丸ごと入れ替える。
{ package PseudoA;
sub new {
my $class = shift @_;
return bless({}, $class);
}
sub method0 {}
}
*A:: = \*PseudoA::;
ただし、上記の入れ替え処理の実行前に生成された、既存のインスタンスには無効。
もし、既存のインスタンスが判るのであれば、個別に以下のようにすることは可能。
|
クラスはないので、オブジェクトを置換する。
globals _AddSlotsIfAbsent: (| pseudoA = () |).
pseudoA _Define: (| slot0 = (^self) |).
a _Define: pseudoA
|
オブジェクト(プロト)を置換する。
ReplaceObject(protoA, protoB);
|
|