2012年10月30日火曜日

deprecated 指令

Delphi-ML で紹介したテクニックなんですけど、たまに役に立つのでご紹介。

Delphi は言語仕様として一度広げた可視性を狭めることはできません
つまり、
type
TFoo = class
public
procedure Test; virtual;
end;
 
TBar = class(TFoo)
protected
procedure Test; override;
end;
XE3 で確認した所、可視性が低くなった事を示すヒントが出なくなっていました。
ヒントのデフォルトスイッチが変わった可能性があります。
……と思ったら、このヒントは「unit」の「interface 部」にのみ出力されるとリンク先にありました。

としても、Test メソッドは依然として public のままです。

そのため、クラスを継承した際に不都合がでる場合があります。
例えば、既存のライブラリクラスを継承して、新たに配布するためのクラスを作る場合などです。
自分やチームだけが使うクラスなら「このメソッドではなく、こちらを使ってください」と伝えるだけで済みます。
しかし、広範に配布するライブラリなどを作る場合では、利用者全員が利用方法を読んでくれる訳ではありません。

では、どうすれば良いかというと、使って欲しくないメソッドを Override しつつ deprecated を付けてしまえ、という事です。

deprecated 指令を付けると、コンパイラは「非推奨」という警告を出すようになります。
警告を一切気にしないユーザーには効果がありませんが、一般的なユーザーは、この警告に気づくでしょう。

具体的には以下のようにします。

type
TFoo = class
public
procedure Test; virtual;
end;
 
TBar = class(TFoo)
public
// TFoo.Test を override しつつ 非推奨化
procedure Test; override; deprecated 'Test2 メソッドを使って下さい';
procedure Test2;
end;
 
{ TFoo }
 
procedure TFoo.Test;
begin
Writeln('Hello, TFoo !');
end;
 
{ TBar }
 
procedure TBar.Test;
begin
inherited; // 元の TFoo.Test を呼ぶようにする
end;
 
procedure TBar.Test2;
begin
Writeln('Hello, TBar !');
end;
 
var
Baz: TBar;
begin
Baz := TBar.Create;
try
//「W1000 シンボル 'Test' を使用することは推奨されていません」が発生する
// 追加メッセージ 'Test2 メソッドを使って下さい' も表示される
Baz.Test;
finally
Baz.Free;
end;
 
Readln;
end.

ただし、静的メソッドの場合と final が指定されている場合は対処できません。
隠蔽で対策できる様に思いますが、下記の様に継承元の型が指定されている場合に隠蔽の効果が無くなるためです。

type
TFoo2 = class
public
procedure Sample; // 静的メソッド
end;
 
TBar2 = class(TFoo2)
public
// TFoo2.Sample を隠蔽する
procedure Sample; deprecated 'Sample2 メソッドを使って下さい';
procedure Sample2;
end;
 
var
Baz: TFoo2; // TFoo2 型で定義する
begin
Baz := TBar2.Create; // TBar2 を生成する
try
// Baz の中身は TBar2 のインスタンスなのに TFoo2.Sample が呼ばれる!
Baz.Sample;
finally
Baz.Free;
end;
 
Readln;
end.
Delphi 言語では、継承元と同じメソッド名を定義することで、継承元のメソッドを見ることができなくなります。
これを「隠蔽」と呼びます。
virtual, dynamic を隠蔽する場合 reintroduce 指令が必要です。

final 指定はまだしも、静的メソッドに deprecated を付けられないので汎用性は下がりますが、依然として有効な時もあるのでは無いでしょうか?

なお、静的メソッドや final を隠したい場合は、ラッパークラスを作る事で回避します。

小ネタでした。

0 件のコメント:

コメントを投稿