2012年12月13日木曜日

publishedなメソッドを隠す

前回の続きです。

TObjectのMethodAddressメソッドを使用すれば関数名(文字列)でメソッドをコールできます。

しかし、 MethodAddressメソッドでメソッドのアドレスを取得するには、可視性をpublishedに
する必要があり、 publishedにした瞬間にメソッドの存在が丸わかりになってしまいます。

前回の例のような単純な演算であればまあ良いかと思うのですが、いわゆるFactoryメソッド
とかだと、使用者にその存在を隠したい場合があり、このままではちょっと不完全です。

さて、どうしようか?というのが今回のテーマになります。(2010以降の拡張Rttiを使えば良い
というのはあるのですが、今回は別の方法を考えます。)

ところで、DelphiのUnitのimplementationセクションで定義した型(クラスを含む)は別のユニット
から参照できない仕様となっています。

したがって、クラスの定義を  implementation で行えば、外部から処理の存在を隠したうえで
 MethodAddressメソッド が使用可能なクラスができそうです。

で、実際に、作ってみました。

先ずは、【メソッドポインタ経由で呼び出されるクラスのサンプル】
  1. unit Unit2;  
  2.   
  3. interface  
  4.   
  5.   function CallCalc(CalcName : string; a, b: double) : double;  
  6.   
  7. implementation  
  8.   
  9.   type TMyCalc = class  
  10.   published  
  11.     function Add(a,b : double) : double;  
  12.     function Subtract(a,b : double) : double;  
  13.   end;  
  14.   
  15.   type TMyCalcFunc = function(a,b : double) : double of object;  
  16.   
  17. { TMyCalc }  
  18.   
  19. function TMyCalc.Add(a, b: double): double;  
  20. begin  
  21.   Result := a + b;  
  22. end;  
  23.   
  24. function TMyCalc.Subtract(a, b: double): double;  
  25. begin  
  26.   Result := a - b;  
  27. end;  
  28.   
  29.   
  30. function CallCalc(CalcName : string; a, b: double) : double;  
  31. var  
  32.   MyCalc     : TMyCalc;  
  33.   MyCalcFunc : TMyCalcFunc;  
  34.   MethodVar : TMethod;  
  35. begin  
  36.   
  37.   MyCalc := TMyCalc.Create;  
  38.   try  
  39.     MethodVar.Data := MyCalc;  
  40.     MethodVar.Code := MyCalc.MethodAddress(CalcName);  
  41.   
  42.   
  43.     if Assigned(MethodVar.Code) then  
  44.     begin  
  45.       MyCalcFunc := TMyCalcFunc(MethodVar);  
  46.       Result := MyCalcFunc(a,b);  
  47.     end;  
  48.   finally  
  49.     MyCalc.Free;  
  50.   end;  
  51.   
  52. end;  
  53.   
  54. end.  

publishedの可視性を持つクラスをimplementationセクションに定義しています。
但し、そのままでは、外部のユニットからメソッドをコールできませんので、
外部からのアクセスようにラッパー関数を定義しています。

次に、【関数名を文字列で指定して処理を呼び出すサンプル】

  1. interface  
  2.   
  3. uses  
  4.   Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,  
  5.   Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;  
  6.   
  7. type  
  8.   TForm1 = class(TForm)  
  9.     Button1: TButton;  
  10.     Button2: TButton;  
  11.     LabeledEdit1: TLabeledEdit;  
  12.     LabeledEdit2: TLabeledEdit;  
  13.     StaticText1: TStaticText;  
  14.     StaticText2: TStaticText;  
  15.     procedure OnCalcBtnClick(Sender: TObject);  
  16.   private  
  17.     { Private 宣言 }  
  18.   public  
  19.     { Public 宣言 }  
  20.   end;  
  21.   
  22. var  
  23.   Form1: TForm1;  
  24.   
  25. implementation  
  26.   
  27. {$R *.dfm}  
  28.   
  29. uses Unit2;  
  30.   
  31. procedure TForm1.OnCalcBtnClick(Sender: TObject);  
  32. begin  
  33.   StaticText2.Caption := FloatToStr(CallCalc(  
  34.                                     TButton(Sender).Caption,  
  35.                                     StrToFloat(LabeledEdit1.Text),  
  36.                                     StrToFloat(LabeledEdit2.Text)));  
  37. end;  
  38.   
  39. end.  

unit2のinterfaceセクションに定義したラッパーを使って処理を呼び出しています。


implementationセクションにクラスを定義することで、外部からその存在を隠しつつ
メソッド名を文字列で指定してメソッドをコールすることが可能です。

但し、この方法ですとクラスそのものも隠れてしまいすのでもう少しなんとかしたい
ところです。

ネストした型宣言が可能なバージョンのDelphiであれば、何とかできそうな気が
しますが、その検証はまた後日・・・

0 件のコメント: