メソッドの拡張

メソッドの拡張


次は、メソッドの追加だ。クラスベースのオブジェクト指向言語で、メソッドを追加する構文を比較する。


継承を用いた、新しいクラスへのメソッドの追加


新しいクラス B を宣言して、メソッドを追加する場合。普通の継承だね。



C++

class B : public A {
public:
 void method1();
};

void B::method1() {}

Java

class B extends A {
 public void method1() {}
}

Objective-C

@interface B : A
{}
- (void)method1;
@end

@implementation B
- (void)method1 {}
@end

Smalltalk

A subclass: #B
 instanceVariableNames: ''
 classVariableNames: ''
 poolDictionaries: ''
 category: 'Category-Name'!

 !B methodsFor: 'message category'!
message1
    ^ self! !



Dylan

define class <b> (<a>)
end class <b>;

define method method-1( b :: <b> ) => ()
end method;

Ruby

class B < A
 def method1
 end
end

Perl

{ package B;
our @ISA = qw/A/;
sub new {
 my $class = shift @_;
 return bless({}, $class);
 }
 sub method1 {}
}

Self

globals _AddSlotsIfAbsent: (| b = () |).
b _Define: (| parent* = a. slot2 = (^self) |)

NewtonScript

prootB := {_proto: protoA,
 method1: func() begin /* 処理 */ end};

オブジェクト(プロト)を作成する。

AppleScript

on B()
 script B
  property parent : A()
  on method1()
  end method1
 end script
end B

AppleScriptのスクリプトオブジェクトは、parentプロパティにより親オブジェクトを指定する。
厳密に言うと、これは特定のインスタンスに対する動的な機能追加である。

継承を用いないメソッドの追加


継承を使わないで、既存のクラスにメソッドを追加するもの。フレームワークがある場合は、その内部のクラスも拡張できる。



C++

不可。

Java

不可。

Objective-C

カテゴリを使う。

@interface A (additionalMethod)
- (void)method2;
@end

@implementation A (additionalMethod)
- (void) method2 {}
@end

Smalltalk

!A methodsFor: 'message category'!
message2
  ^ self! !

Dylan

define method method-1( a :: <a> ) => ()
end method;

Ruby

class A
 def method2
 end
end

Perl

{ package A;
 sub method2 {}
}

Self

a _AddSlots: (| slot2 = (^self) |)

Perl

メソッド(サブルーチン)を呼び出す。

$a = new A();
$a->method0();

Self

a slot1




実行時のメソッドの追加


実行時に、動的にクラスにメソッドを追加するものだ。



C++

不可。

Java

不可。




Objective-C

直接はできないけど、forwardInvocation: と methodSignatureForSelector: をオーバーライドすることで、実現できる。

Smalltalk

| code |
code := 'message3',
 String cr,
 String tab,
 '^ self'.
A compile: code classified: 'message category'
 notifying: nil

Dylan

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);

Ruby

class A
def method2
end
end

または、

a.instance_eval("def_method\nend")

この場合、特異メソッドになる。

Perl

my $code = sub { print "Hellow.\n" };
*A::method3 = $code;

Self

a _AddSlots: (| slot3 = (^self) |)

クラスの置換


あるクラスで、現在あるクラスを置き換えるもの。この機能は、確かに実際のプログラミングでは必要ないかもしれない。やり方によっては、とても凶悪な動作をすることもある。でも、動きを理解しているならばとても便利なものだし、クラスの拡張の柔軟性を比較するにはいいものかもしれない。

NewtonScript

imp := func() begin /* 処理 */ end;
SetSlot(protoA, intern("method3"), imp);

AppleScript

on handler3()
end handler3
set handler3 of a to handler3

NewtonScript

perform(a, intern("method0"), [])

AppleScript

不可。




メソッドオブジェクトに対する、起動メッセージの送信


これは、メソッドオブジェクトを使う場合だ。まず、メッセージかメッセージの名前を指定して、メソッドオブジェクトを取得する。そして、このメソッドオブジェクトに、起動のメッセージを送ってやるんだ。そのときに、ターゲットのオブジェクトや引数も指定する。



C++

不可。

Java

不可。

Objective-C

poseAsClass: を使う。この場合、PseudoA は A のサブクラスでなくてはいけなくて、インスタンス変数を追加してはいけない。これ以降、A へのメッセージは PseudoA に送られる。

@interface PseudoA : A
@end

@implementation PseudoA
+ (void)load {
  [PseudoA poseAsClass:[A class]];
}
- (void)method0 {}
@end

Smalltalk

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!

Dylan

不可。

Ruby

不可。

Perl

PseudoA は普通に定義しておき、その後に package を丸ごと入れ替える。

{ package PseudoA;
 sub new {
  my $class = shift @_;
  return bless({}, $class);
 }
 sub method0 {}
}
*A:: = \*PseudoA::;

ただし、上記の入れ替え処理の実行前に生成された、既存のインスタンスには無効。もし、既存のインスタンスが判るのであれば、個別に以下のようにすることは可能。

bless( $a, q/PseudoA/ );

Self

クラスはないので、オブジェクトを置換する。

globals _AddSlotsIfAbsent: (| pseudoA = () |).
pseudoA _Define: (| slot0 = (^self) |).
a _Define: pseudoA

NewtonScript

オブジェクト(プロト)を置換する。

ReplaceObject(protoA, protoB);

AppleScript

不可。