※本文最後に追記あり(2013-12-20)
Delphi の Interface が使えないとほざいているのは誰だぁっ!
ということで、とりあえずできることを、つらつらっと書いてみましたよ。
詳しくは、コメントを見てください!
program Project1;usesSystem.SysUtils;type// GUID を指定すると Interface と GUID を結びつけることができます。// これは主に COM をサポートするための機能です。// (COM は Interface を GUID で管理します)// にも関わらず GUID を付けることが推奨されています。// 例えば GUID をキーに Dictionary として管理したりするためです。// TPlatformServices.AddPlatformService のソースが参考になります。IFoo = interface['{174C7089-888D-4B3A-A348-DBAEC0AA70A5}']// Property も宣言できますが、Interface は変数を宣言できないので// reader / writer はメソッドのみ指定できますfunction GetBar: String;property Bar: String read GetBar;end;IDummy = interface['{7820C3D8-0DBC-4506-81FF-4FB9B21F6959}']function GetBar: String;end;// IFoo を実装するクラス// TAggregatedObject は後述する TInterfacedObject の Reference Counter を// 共有するクラスですTFooImpl = class(TAggregatedObject, IFoo)privatefunction GetBar: String;end;// IFoo を実装するクラス2// IDummy という実験のためのダミークラスも実装してみています。// 異なる Interface が同じメソッド名を持つ場合は、こんな風に解決できます。TFooImpl2 = class(TAggregatedObject, IFoo, IDummy)privatefunction IFooGetBar: String;function IDummyGetBar: String;function IFoo.GetBar = IFooGetBar; // IFoo と IDummy の GetBar にfunction IDummy.GetBar = IDummyGetBar; // それぞれの実装を代入しているend;// TFooImpl か TFooImpl2 に IFoo の実装を委譲してるクラス// 委譲すると自身は IFoo を実装しなくていい!// TInterfacedObject を継承すると RefCounter によって自動的に破棄されるよ// 流行りの ARC と同じ仕組みを随分前から実装してたんだよ!// (COM がそうなんだけど)TBaz = class(TInterfacedObject, IFoo)privateFFoo: IFoo;publicconstructor Create; reintroduce;property FooIntf: IFoo read FFoo implements IFoo;end;{ TFooImple }function TFooImpl.GetBar: String;beginResult := 'Bar';end;{ TFooImple2 }function TFooImpl2.IDummyGetBar: String;beginResult := 'Dummy !';end;function TFooImpl2.IFooGetBar: String;beginResult := 'Bar 2!';end;{ TBaz }constructor TBaz.Create;begininherited;Randomize;// (ここではランダムだけど)目的に応じて委譲先を変更できる!if (Random(2) = 0) thenFFoo := TFooImpl.Create(Self) // TAggregatedObject は RefCounter をelse // 共有するインスタンスを要求するよFFoo := TFooImpl2.Create(Self);end;{ Main }varFoo: IFoo;GUID: TGUID;Obj: TObject;begin// Inteface に代入, TBaz 自体は IFoo を実装していないのに代入できる!Foo := TBaz.Create;Writeln(Foo.Bar);// Inteface を TGUID に代入できる!GUID := IFoo;Writeln(GUIDToString(GUID));// Inteface から元の型を調べてみるWriteln((Foo as TObject).ClassName); // Implements からでも元の型が取れる!// Inteface から元の型を取りだして、再生成したりもできちゃう// (まあこれは Inteface 関係ないけど…)Obj := (Foo as TObject).ClassType.Create;Writeln(Obj.ClassName);Readln;(* 実行結果 - Bar 2! と出ているところは Bar と出ることもあるよBar 2!{174C7089-888D-4B3A-A348-DBAEC0AA70A5}TBazTBaz*)end.
個人的に面白いなあと思うのは「委譲」の仕組みと「Interface から元の型を取り出せる」ところかな-。
※ヘルプには「委譲は Win32 のみ」と書いてあるけど、普通に Win/OSX/iOS/Android で動作しました。
あと、今回の iOS / Android 対応で TinterfacedObject の仕組みが非常に活きているのが感慨深い…
COM のために実装した様々なことがここに来てすごく活きている!
同じく COM のために実装した dispinterface は、プラットフォーム依存ですと警告が出るようになってたよ…
ちなみに、Interface は FireMonkey でも使われまくっていてるんですよ!!
各 Platform 依存部と、それを一般化する部分では Interface 無しには実装できませんぜ!
追記:0213-12-20
Java でも、次のようにすれば Interface から元の Class を取り出せるよ!と教えていただきました。
MyInterface intf = new MyClass(); System.out.println(((Object)intf).getClass().getName());僕の Java スキルもまだまだです……
0 件のコメント:
コメントを投稿