スポンサーサイト

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

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

プログラムで同じように処理できるオブジェクト群を考えます。大雑把に次のような場合があるかと思います。
(1)それらがすべて同一クラスのインスタンスである。
(2)それらがすべて同一クラスの派生クラスのインスタンスである。
(3)それらがすべて同じインターフェイスを実装したクラスのインスタンスである。
 
ここで、同じように処理Aができるオブジェクトには頭にobTypeAをつけ、obTypeA***という命名をすることにします。(同様に同じように処理BができるオブジェクトはobTypeB***です。)
 
さてオブジェクトのグループ分けには上のようなグループ分け以外に「どういう文脈で使われるオブジェクト」かというグループ分けがあります。
例えば「SqlDataBaseに接続して様々な処理をするオブジェクト群」と「OracleDataBaseに接続して様々な処理をするオブジェクト群」は、混合して使われることはありません。(というより混合して使っちゃダメです)
 
このように同じ文脈△で使われるオブジェクトには共通して末尾にCntxt△という名前を付けることにします。
 
例えばobTypeACntxtαとobTypeBCntxtαは同じ文脈αで使われるが、型は同じとは限らない。
obTypeACntxtαとobTypeACntxtβは同じ振る舞いをする(型が同じものとして扱える)が、異なる文脈で使われる。すなわち混合して使われることはないということになります。
 
さて次の疑似コードブロックを見てください。
 
何らかの処理1
(new obTypeACntxt?()).methodA() //obTypeA...はmethodA:unit->unitを持つとする
何らかの処理2
(new obTypeBCntxt?()).mehodB(3) //obTypeB..はmethodB:int -> unitをもつとする
何らかの処理3
let lst1 = ((new obTypeCCntxt? ()):> %%%) :: [] //obTypeC..は同じ型%%%にキャスト可能とする
..............
 
 
さてob...部分に実際のオブジェクトを入れ込んでプログラムを走らせるときには色々な場合が考えられます。
 
(文法的にも、文脈的にも正しい例)
 
何らかの処理1
(new obTypeACntxtβ()).methodA() 
何らかの処理2
(new obTypeBCntxtβ()).mehodB(3) 
何らかの処理3
let lst1 =  ((new obTypeCCntxtβ()) :> %%%) :: [] 
 
(文法的には正しいが、文脈的には正しくない例(異なる文脈で使うオブジェクトが混じってしまってます。))
 
何らかの処理1
(new obTypeACntxtα()).methodA() 
何らかの処理2
(new obTypeBCntxtβ()).mehodB(3) 
何らかの処理3
let lst1 =  ((new obTypeCCntxtβ()) :> %%%) :: [] 
 
下のような場合が起こらないようにするために、文脈別にインスタンスの組を生成する工場を考えるパターンをAbstractFactoryパターンといいます。(生成するインスタンス群に矛盾が起こらないように、インスタンス生成にワンクッションおいて、縛りを与えるという仕組みです。)
 
では例です。
まずは型を3つ定義しておきます。(ここでは最初の2つはインターフェイス、最後の一つは抽象クラスにします。)
> type MyTypeA  =
    abstract member methodA : unit -> unit
 
type MyTypeB  =
    abstract member methodB : int -> unit
 
[<AbstractClass>]
type MyTypeC (init_j)  =
     abstract member methodC : unit -> unit
     member this.J = init_j;;
 
//F#Interaciveの返答略
 
文脈αで使われる、オブジェクトの型であるクラスを定義しておきます。
 
> type MyTypeACntxtα () =
    interface MyTypeA with
        member this.methodA () =
            printfn "文脈αで用いられるobjectがmethodAを実行します。"
 
type MyTypeBCntxtα () =
    interface MyTypeB with
        member this.methodB (i) =
            printfn "文脈αで用いられるobjectが引数%dに対しmethodBを実行します。" i
 
type MyTypeCCntxtα (init_j) =
    inherit MyTypeC (init_j) with
       override  this.methodC () =
            printfn "文脈αで用いられるobjectがプロパティ%dに対しmethodCを実行します。" init_j
;;
//F#Interaciveの返答略
 
 
文脈βで使われる、オブジェクトの型であるクラスを定義しておきます。
 
> type MyTypeACntxtβ () =
    interface MyTypeA with
        member this.methodA () =
            printfn "文脈βで用いられるobjectがmethodAを実行します。"
 
type MyTypeBCntxtβ () =
    interface MyTypeB with
        member this.methodB (i) =
            printfn "文脈βで用いられるobjectが引数%dに対しmethodBを実行します。" i
 
type MyTypeCCntxtβ (init_j) =
    inherit MyTypeC(init_j) with
        override this.methodC () =
            printfn "文脈βで用いられるobjectがプロパティ%dに対しmethodCを実行します。" init_j
;;
 
//F#Interaciveの返答略
 
次に文脈別の工場の基底クラスです。(これを派生させて各文脈別のインスタンス工場を作ります。)
 
次のように定義します。
 
> [<AbstractClass>]
type MyBaseFactory (cntxtName :string) =
    do printfn "%sで用いられるオブジェクト群を生成するFactoryが生成されました" cntxtName
    abstract member CreateTypeAObj : unit -> MyTypeA
    abstract member CreateTypeBObj : unit -> MyTypeB
    abstract member CreateTypeCObj : int  -> MyTypeC 
;;
 
//F#Interaciveの返答略
 
では文脈αで用いられるobject群を生成するfactoryを定義します。
 
> type CntxtαFactory () =
    inherit MyBaseFactory("α") with
    override this.CreateTypeAObj () =
                (new MyTypeACntxtα()) :> MyTypeA
    override this.CreateTypeBObj () =
                (new MyTypeBCntxtα()) :> MyTypeB
    override this.CreateTypeCObj (i) =
                (new MyTypeCCntxtα(i)) :> MyTypeC
;;
 
//F#Interaciveの返答略
 
同様に文脈βで用いられるobject群を生成するfactoryを定義します
 
> type CntxtβFactory () =
    inherit MyBaseFactory("β") with
    override this.CreateTypeAObj () =
                (new MyTypeACntxtβ()) :> MyTypeA
    override this.CreateTypeBObj () =
                (new MyTypeBCntxtβ()) :> MyTypeB
    override this.CreateTypeCObj (i) =
                (new MyTypeCCntxtβ(i)) :> MyTypeC
;;
 
//F#Interaciveの返答略
 
では準備完了なので使ってみます。
 
factoryを受け取ってインスタンスを生成しながら、なんらかの処理をする関数を次のように定義します。
 
> let doSomething (cf :MyBaseFactory) =
    printfn "何らかの処理1"
    cf.CreateTypeAObj().methodA() 
    printfn "何らかの処理2"
    cf.CreateTypeBObj().methodB(3) 
    printfn "何らかの処理3"
    let lst1 =  (cf.CreateTypeCObj(5)) :: []    
    let lst2 =  (cf.CreateTypeCObj(7)) :: lst1
    lst2 |>
        List.iter (fun x -> x.methodC());;
 
val doSomething : MyBaseFactory -> unit
 
文脈αで用いられるobject群を生成するfactoryを渡してみます。
 
>doSomething (new CntxtαFactory ());;
αで用いられるオブジェクト群を生成するFactoryが生成されました
何らかの処理1
文脈αで用いられるobjectがmethodAを実行します。
何らかの処理2
文脈αで用いられるobjectが引数3に対しmethodBを実行します。
何らかの処理3
文脈αで用いられるobjectがプロパティ7に対しmethodCを実行します。
文脈αで用いられるobjectがプロパティ5に対しmethodCを実行します。
val it : unit = ()
 
文脈βで用いられるobject群を生成するfactoryを渡してみます。
 
> doSomething (new CntxtβFactory ());;
βで用いられるオブジェクト群を生成するFactoryが生成されました
何らかの処理1
文脈βで用いられるobjectがmethodAを実行します。
何らかの処理2
文脈βで用いられるobjectが引数3に対しmethodBを実行します。
何らかの処理3
文脈βで用いられるobjectがプロパティ7に対しmethodCを実行します。
文脈βで用いられるobjectがプロパティ5に対しmethodCを実行します。
val it : unit = ()
 
このようにfactoryの選択をするだけで、使われるオブジェクトがごっそり入れ替わります。
 
(注意)一般には文脈αか文脈βかの選択は外部ファイルから読み込んだり、ユーザーに入力してもらう場合が多いようです。
 
ごっそり、矛盾のないようにオブジェクト群を入れ替えるときに活躍するAbstract Factoryパターンの紹介でした。
スポンサーサイト

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

コメントの投稿

非公開コメント

プロフィール

T GYOUTEN

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

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

この人とブロともになる

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