スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

F#によるデザインパターン入門 Proxy パターン(2)

前回は防御Proxyを紹介しました。今回はまずはリモートProxyの概要のみを紹介します。
これは前回のたとえ話でとらえると、政治家が、どっかに雲隠れしてしまい、連絡手段は秘書の電話のみという感じです。政治家に用事がある時は、秘書に用件を伝えて、秘書はどっかにいる政治家に連絡をとり用件を処理します。
リモートというのは、「離れた」という意味合いで、実際には政治家は別のサーバ上のウェブサービス等であったりします。秘書がウェブサービスとの連絡役をつとめるという仕組みです。
秘書(Proxy)は、用件をネットワークを用いて送信し、政治家(外部サーバのサービス等)からの返答を受信したらそれを、用件の依頼者に伝えます。
サブジェクト(政治家)が用件処理に専心し、秘書(Proxy)がデータを運ぶことに専心するという仕組みです。
 
次に仮想 Proxy(virtual proxy)を紹介します。
これは本当に必要になるまで政治家は登場せず、秘書が代理をつとめ、どうしても必要となったときに、政治家オブジェクト(サブジェクト)が登場して(生成されて)用件を処理するというパターンです。
基本的にサブジェクトの生成に時間がかかる場合に用いられます。(例えばプログラムの最初に政治家が大量に必要になる場合、ここですべてを生成しているとプログラムがなかなか始まらなくなります。そこで秘書群を生成し(もちろんこれらの生成は軽くないと意味がありません。)、政治家が必要になるタイミングで、政治家を生成していくという仕組みです。
 
ではサンプルを。
 
まずはインターフェイスです。
 
> type IWhatToDo  =
    abstract do_something1 : unit -> unit;;
 
type IWhatToDo =
  interface
    abstract member do_something1 : unit -> unit
  end
 
次にRealPlayer(政治家、サブジェクト)です。
 
> type MyRealPlayer () =
    do printfn "実際には時間かけて生成してます。" //コンストラクタで実行される部分
    interface IWhatToDo with
      member this.do_something1 ()=
                printfn "RealPlayer do something1";;
 
type MyRealPlayer =
  class
    interface IWhatToDo
    new : unit -> MyRealPlayer
  end
 
最後にProxy(秘書、代理、プロキシー)です。
 
> type MyProxyPlayer () =
    let mutable rp : MyRealPlayer option = None
    //RealPlayerが生成されてなければ生成し、参照を保持させるunit->unit型の関数
    let init () =
        match rp with
        | None -> rp <- Some(new MyRealPlayer())
        | _    -> ()
    
    interface IWhatToDo with
      member this.do_something1 () =
                init()
                (rp.Value:>IWhatToDo).do_something1 ();;
 
type MyProxyPlayer =
  class
    interface IWhatToDo
    new : unit -> MyProxyPlayer
  end
 
前回のと異なる点はサブジェクトの生成がproxy任せになり、proxyのメソッドが呼ばれた時点で、サブジェクトの生成ができているかどうかをチェックし、できてなければその時点で生成を行う部分です。
 
では使ってみます。
 
> let myProxyP1 = new MyProxyPlayer();;
 
val myProxyP1 : MyProxyPlayer
 
この時点では、サブジェクトの生成はなされていません。
次のdo_something1を呼び出した時点でサブジェクトが生成されます。
 
> (myProxyP1:>IWhatToDo).do_something1();;
実際には時間かけて生成してます。
RealPlayer do something1
val it : unit = ()
 
なおMyProxyPlayerは次のように書き換えてもよいかと思います。
 
type MyProxyPlayer () =
    let mutable rp : MyRealPlayer option = None
    let getSubject () =
        match rp with
        | None -> rp <- Some(new MyRealPlayer())
        | _    -> ()
        rp.Value :> IWhatToDo
    
    
    interface IWhatToDo with
      member this.do_something1 () =
                (getSubject()).do_something1 ()
スポンサーサイト

テーマ : プログラミング
ジャンル : コンピュータ

F#によるデザインパターン入門 Proxy パターン(1)

まずは政治家とその秘書を想像してください。
政治家に何か用件を頼むときは、まず普通は秘書に連絡します。
秘書は内容によって、政治家に連絡を取ります。(とらない場合もあり得ます。)
 
このように、あるオブジェクト(上の場合政治家)への呼び出しを、直接するのではなく、代理のオブジェクト(上の場合秘書)を窓口におこなうようなパターンをProxy(プロキシー,代理)パターンといいます。ここでは3種類のProxy(代理)パターンの利用方法を順次紹介していきたいとおもいます。
 
一つ目は秘書が用件を政治家に伝えたり伝えなかったりする(つまりアクセスを制御する)例です。
 
まずはオブジェクトへの要請(オブジェクトの持つべきメソッド)をインターフェイスで定義しておきます。
 
> type IWhatToDo  =
    abstract do_something1 : unit -> unit
    abstract do_something2 : int -> unit;;
 
type IWhatToDo =
  interface
    abstract member do_something1 : unit -> unit
    abstract member do_something2 : int -> unit
  end
 
次は政治家オブジェクトの定義です。
 
> type MyRealPlayer () =
    interface IWhatToDo with
      member this.do_something1 ()=
                printfn "RealPlayer do something1"
      member  this.do_something2 i =
                printfn "RealPlayer do something2 with i = %d" i;;
 
type MyRealPlayer =
  class
    interface IWhatToDo
    new : unit -> MyRealPlayer
  end
  
次に秘書オブジェクトの定義です。
 
> type MyProxyPlayer (rp : MyRealPlayer) =
    interface IWhatToDo with
      member this.do_something1 () =
                (rp:>IWhatToDo).do_something1 ()
      member this.do_something2 i =
                if i >=10000 then
                   (rp:>IWhatToDo).do_something2 i
                else
                    ();;
 
type MyProxyPlayer =
  class
    interface IWhatToDo
    new : rp:MyRealPlayer -> MyProxyPlayer
  end
  
暗黙的クラス定義なので少しわかりにくいかもしれませんが、コンストラクタで政治家オブジェクトを渡すようにしているので、秘書オブジェクトは暗黙裡に政治家オブジェクトへの参照を持つことになります。
(政治家オブジェクトのほうをサブジェクト(subject)というそうです。)
 
では使ってみます。
 
> let myRealP = new MyRealPlayer()
let myProxyP = new MyProxyPlayer(myRealP);;
 
val myRealP : MyRealPlayer
val myProxyP : MyProxyPlayer
 
proxy経由でdo_something1を呼び出す
 
> (myProxyP:>IWhatToDo).do_something1();;
RealPlayer do something1
val it : unit = ()
 
proxy経由でdo_something2を呼び出す(myRealPへのアクセスが拒否される場合)
 
> (myProxyP:>IWhatToDo).do_something2(100);;
val it : unit = ()
 
proxy経由でdo_something2を呼び出す(myRealPへのアクセスが許可される場合)
 
> (myProxyP:>IWhatToDo).do_something2(100000);;
RealPlayer do something2 with i = 100000
val it : unit = ()
 
このように、本物のオブジェクトと用件の間に入って、アクセスの制限をつかさどるProxyを防御proxy(protection proxy)といいます。

テーマ : プログラミング
ジャンル : コンピュータ

F#によるデザインパターン入門 Command パターン(3)

今回はundo(元にもどす)という機能を実現できるような、Command パターンを紹介したいと思います。
例えばCommandオブジェクトのExecuteメソッドで、ファイルの削除等の何らかの処理がなされたとします。
このオブジェクトにその処理の逆の作用をする処理を定義しておけば(ファイルの削除の場合であれば、削除する場合にファイルの内容をオブジェクト内に保存しておき、逆の作用としてファイルを生成し、保存しておいた内容をもたせる。)簡単にundo処理ができます。
それでは、例を紹介します。これは、「テキストファイルの生成」「テキストファイルの削除」「テキストファイルのコピー」を扱う例です。
前回はインターフェイスを用いて処理しましたが、今回は各クラスに共通のデータを持たせたいと思うのでクラスで処理します。
 
まずは基底クラスの定義です。
 
> [<AbstractClass>] 
type MyCommand (in_desc :string) =
    let mutable desc = in_desc
    abstract member Execute : unit -> unit
    abstract member UnExecute : unit -> unit
    abstract member Desc :string
    default this.Desc = desc;;
 
type MyCommand =
  class
    new : in_desc:string -> MyCommand
    abstract member Execute : unit -> unit
    abstract member UnExecute : unit -> unit
    abstract member Desc : string
    override Desc : string
  end
 
コンストラクタの引数のdescはdescriptionの略で、どんなことをするかを文字列で保持します。
(例 "create s:\mydata01.txt")
 
次にファイル生成クラスを定義してみます。
 
> type MyCreateFile(path:string,contents:string) =
    inherit MyCommand ("Create File :" + path)
    override this.Execute () =
        let f =  System.IO.File.CreateText(path)
        f.Write(contents)
        f.Close() 
    override this.UnExecute() =
        System.IO.File.Delete(path);;
 
type MyCreateFile =
  class
    inherit MyCommand
    new : path:string * contents:string -> MyCreateFile
    override Execute : unit -> unit
    override UnExecute : unit -> unit
  end
 
コンストラクタはでpath(例えば"s:\mydata01.txt"等)と、ファイルの内容の文字列を渡します。
 
ちょっと使ってみます。
 
> let myCF00 = new MyCreateFile(@"s:\mydata00.txt","おはようございます");;
val myCF00 : MyCreateFile
 
> myCF00.Desc;;
val it : string = "Create File :s:\mydata00.txt"
 
> myCF00.Execute();;
val it : unit = ()
でsドライブにファイルmydata00.txtが生成され
 
> myCF00.UnExecute();;
val it : unit = ()
で元の状態にもどります。
 
同様にファイルの削除を担当するクラスも定義しておきます。
 
> type MyDeleteFile(path:string) =
    inherit MyCommand ("Delete File :" + path)
    let mutable contents = ""
    override this.Execute () =
        if System.IO.File.Exists(path) then
            //UnExecuteのために内容を保持しておく
            //(一般には一時ファイルを使うが今回はメモリ上に保存しておく)
            contents <- System.IO.File.ReadAllText(path)
            System.IO.File.Delete(path)
         
    override this.UnExecute() =
        if contents <> "" then
            let f =  System.IO.File.CreateText(path)
            f.Write(contents)
            f.Close()
 
type MyDeleteFile =
  class
    inherit MyCommand
    new : path:string -> MyDeleteFile
    override Execute : unit -> unit
    override UnExecute : unit -> unit
  end
 
それでは使ってみます。
 
まずはmyCF00を利用して、s:\mydata00.txtというファイルを作成しておきます。
 
> myCF00.Execute();;
val it : unit = ()
 
MyDeleteFileクラスのインスタンスをひとつ生成します。
 
> let myDF00 = new MyDeleteFile(@"s:\mydata00.txt");;
val myDF00 : MyDeleteFile
 
Executeします。
 
> myDF00.Execute();;
val it : unit = ()
 
これでs:\mydata00.txtファイルが削除されます。
 
アンドゥします。
 
> myDF00.UnExecute();;
val it : unit = ()
 
これでこれでs:\mydata00.txtファイルが復活します。
 
同様にファイルの複写を担当するクラスも定義しておきます。
 
> type MyCopyFile(sourcePath:string,targetPath:string)=
    inherit MyCommand(sprintf "Copy %s to %s " sourcePath targetPath)
    let mutable contents = ""
    override this.Execute () =
        //コピー先ファイルが存在する場合はUnExecuteのために内容を保持しておく
        if System.IO.File.Exists(targetPath) then
            contents <-  System.IO.File.ReadAllText(targetPath)
        System.IO.File.Copy(sourcePath,targetPath,true)//最後のtrueは上書きを可能にするため
    override this.UnExecute()=
        if contents = "" then
            System.IO.File.Delete(targetPath)
        else
            System.IO.File.WriteAllText(targetPath,contents)
 
type MyCopyFile =
  class
    inherit MyCommand
    new : sourcePath:string * targetPath:string -> MyCopyFile
    override Execute : unit -> unit
    override UnExecute : unit -> unit
  end
 
現在s:\mydata00.txtファイルが一個存在する状態ですので、ここで、このクラスのインスタンスを生成して使用してみます。
 
> let myCyF00 = new MyCopyFile(@"s:\mydata00.txt",@"s:\mydata01.txt");;
val myCyF00 : MyCopyFile
 
> myCyF00.Execute();;
val it : unit = ()
 
> myCyF00.UnExecute();;
val it : unit = ()
 
でs:\mydata00.txtファイルが一個存在する状態にもどります。
 
最後に合成コマンドを表すクラスを定義します。
 
> type MyComposit() =
    inherit MyCommand("")
    let mutable lst : List< MyCommand> = []
    member this.Add(ele:MyCommand) =
                lst <- ele :: lst
    override this.Execute() =
        (List.rev lst) |>
        List.iter (fun e -> e.Execute()) 
    override this.UnExecute() =
        lst |>
        List.iter (fun e -> e.UnExecute())
 
    override this.Desc =
        (List.rev lst) |>
        List.fold (fun s d -> s + d.Desc + "\n") "";;
 
type MyComposit =
  class
    inherit MyCommand
    new : unit -> MyComposit
    member Add : ele:MyCommand -> unit
    override Execute : unit -> unit
    override UnExecute : unit -> unit
    override Desc : string
  end
  
  
 使ってみます。
 
 > let myCp00 = new MyComposit()
myCp00.Add(new MyCreateFile(@"s:\mydata10.txt","現在部屋の気温はは36度"))
myCp00.Add(new MyCopyFile(@"s:\mydata10.txt",@"s:\mydata11.txt"))
myCp00.Add(new MyDeleteFile(@"s:\mydata10.txt"));;
 
val myCp00 : MyComposit
 
Descプロパティを調べてみます。
 
> myCp00.Desc;;
val it : string =
  "Create File :s:\mydata10.txt
Copy s:\mydata10.txt to s:\mydata11.txt 
Delete File :s:\mydata10.txt
"
実行してみます。
 
 > myCp00.Execute();;
val it : unit = ()
 
これでsドライブにはmyData11.txtが生成された状態になります。
 
> myCp00.UnExecute();;
val it : unit = ()
でもとに戻ります。
 
これらを、リスト等に保存する仕組みを作れば、簡単にアンドゥ可能な履歴を作ることができます。

テーマ : プログラミング
ジャンル : コンピュータ

F#によるデザインパターン入門 Command パターン(2)

前回はCommandパターンの導入として次のようなインターフェイスとそれを実装したクラスを定義して使ってみました。
 
//インターフェイス
type Icommand =
  abstract Execute : unit -> unit  
 
//クラスの定義
type ShowNum (i:int) =
    interface Icommand with
        member this.Execute () =
            printfn "%d" i
 
type ShowTwoNum (i:int,j:int) =
    interface Icommand with
        member this.Execute () =
            printfn "%d %d" i j
 
type ShowStr (st :string) =
    interface Icommand with
        member this.Execute () =
            printfn "%s" st
 
type CompShow () =
    let mutable (cmds : list<Icommand>) = []
 
    interface Icommand with
        member this.Execute () =
            List.iter (fun (com:Icommand) -> com.Execute ()) cmds
 
    member this.Add (com:Icommand) =
        cmds <- com :: cmds
 
上のような単純な振る舞いを実行するだけのコマンドを取り扱う場合なら、F#では関数を使う方が普通ですので、今回は上の例を関数を使って書き直してみます。
 
まずはCompShow以外を取り扱います。
 
> let showNum i () =
    printfn "%d" i
 
let showTwoNum i j () =
    printfn "%d %d" i j
 
let showStr st () =
    printfn "%s" st;;
 
val showNum : int -> unit -> unit
val showTwoNum : int -> int -> unit -> unit
val showStr : string -> unit -> unit
 
部分適用して、「unitを与えると実行できる状態の関数」をいくつか作っておきます。
 
> let sn1 = showNum 3
let sn2 = showNum 7
let stn1 = showTwoNum 1 2
let ss1 = showStr "one"
let ss2 = showStr "two";;
 
val sn1 : (unit -> unit)
val sn2 : (unit -> unit)
val stn1 : (unit -> unit)
val ss1 : (unit -> unit)
val ss2 : (unit -> unit)
 
使用してみます。
 
> sn1 ();;
3
val it : unit = ()
> sn2 ();;
7
val it : unit = ()
> stn1 ();;
1 2
val it : unit = ()
> ss1 ();;
one
val it : unit = ()
> ss2 ();;
two
val it : unit = ()
 
CompShowの代わりに関数の合成を使います。
 
> let sc1 = sn1 >> stn1 >> ss2;;
 
val sc1 : (unit -> unit)
 
> sc1 ();;
3
1 2
two
val it : unit = ()

テーマ : プログラミング
ジャンル : コンピュータ

F#によるデザインパターン入門 Command パターン(1)

Commandパターンは、動作用のコードをオブジェクトとして取り扱うというパターンです。
構造としては共通のインターフェイス(下の例ではメソッドExecuteを実装)を共有する多数のクラスから構成されます。
 

 
> //インターフェイス
type Icommand =
  abstract Execute : unit -> unit  
 
//クラスの定義
type ShowNum (i:int) =
    interface Icommand with
        member this.Execute () =
            printfn "%d" i
 
type ShowTwoNum (i:int,j:int) =
    interface Icommand with
        member this.Execute () =
            printfn "%d %d" i j
 
type ShowStr (st :string) =
    interface Icommand with
        member this.Execute () =
            printfn "%s" st;;
 
type Icommand =
  interface
    abstract member Execute : unit -> unit
  end
type ShowNum =
  class
    interface Icommand
    new : i:int -> ShowNum
  end
type ShowTwoNum =
  class
    interface Icommand
    new : i:int * j:int -> ShowTwoNum
  end
type ShowStr =
  class
    interface Icommand
    new : st:string -> ShowStr
  end
 
Icommand型にキャストする補助関数toIcを定義しておきます。
 
> let toIc (x:obj) = 
        x :?> Icommand;;
 
val toIc : obj -> Icommand
 
Icommand型のインスタンスをいくつか生成してみます。
 
> //インスタンスの生成
let sn1 = new ShowNum (3) |> toIc
let sn2 = new ShowNum (7) |> toIc
let stn1 = new ShowTwoNum (1,2) |> toIc
let ss1 = new ShowStr ("one") |> toIc
let ss2 = new ShowStr ("two") |> toIc;;
 
val sn1 : Icommand
val sn2 : Icommand
val stn1 : Icommand
val ss1 : Icommand
val ss2 : Icommand
 
こうしておけば、Icommand型のオブジェクトは配列等に保存可能なので、実行の履歴を保存したりする用途に使用可能です。例えばWindowsアプリケーションで複数のボタンを配置し、それぞれのボタンにIcommand型のオブジェクトを対応させておき、ボタンが押されたときに、そのオブジェクトのExecuteメソッドを実行し、同時にリスト等に保存しておけば、実行の履歴を残すことができます。
 
例えば、あるプログラムで、履歴が
 
> let his00= [stn1;ss1;ss2;sn1];;
 
val his00 : Icommand list =
  [FSI_0002+ShowTwoNum; FSI_0002+ShowStr; FSI_0002+ShowStr; FSI_0002+ShowNum]
  
というようにリストに保存されているのであれば、同じ動作を次のように再現できます。
 
> List.iter (fun (com:Icommand) -> com.Execute ()) his00;;
1 2
one
two
3
val it : unit = ()
 
さて、上の例では履歴全体も実行できる、ひと塊のコードでしたので、これもクラス化して、Icommandインターフェイスを備えさせてみます。(いわゆるCompositパターンです。)
> type CompShow () =
    let mutable (cmds : list<Icommand>) = []
 
    interface Icommand with
        member this.Execute () =
            List.iter (fun (com:Icommand) -> com.Execute ()) cmds
 
    member this.Add (com:Icommand) =
        cmds <- com :: cmds;;
 
type CompShow =
  class
    interface Icommand
    new : unit -> CompShow
    member Add : com:Icommand -> unit
  end
 
使用してみます。
 
> let sc1 = new CompShow()
sc1.Add(sn1)
sc1.Add(stn1)
(sc1:>Icommand).Execute();;
1 2
3
 
val sc1 : CompShow
 
これでsc1自身もIcommandインターフェイスを備えるオブジェクトですので、さらに入れ子にもできます。
 
> let sc2 = new CompShow()
sc2.Add(sc1)
sc2.Add(sn1)
(sc2:>Icommand).Execute();;
3
1 2
3
 
val sc2 : CompShow

テーマ : プログラミング
ジャンル : コンピュータ

プロフィール

T GYOUTEN

Author:T GYOUTEN
F#と英単語とフリーソフトと読書に興味があります。
ホームページでフリーソフトも公開しています。どぞ御贔屓に。

最新記事
最新コメント
最新トラックバック
月別アーカイブ
カテゴリ
フリーエリア
フリーエリア
blogram投票ボタン
検索フォーム
RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

QRコード
QRコード
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。