スポンサーサイト

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

F#によるデザインパターン入門 ストラテジー パターン(1)

ストラテジー パターンというのは、大雑把にいうと、何らかの問題に対する担当者(やり方、アルゴリズム)を切り替えるパターンです。
まずは今回はJava風の解決方法から紹介します。
 
「1からnまでの数の和を計算せよ」という問題に対して担当者Aはバカ正直に1からnまでを足すとします。担当者Bはn*(n+1)/2を計算するとします。
この場合、担当者に必要なのは「問題を受け取り答えを導いて返す」能力です。
これをインターフェイスで表します。
 
>type ISoloveThatProblemable =
    abstract SolveProblem : int -> int //引数のintが問題,返り値のintが答え;;
    
type ISoloveThatProblemable =
  interface
    abstract member SolveProblem : int -> int
  end
 
(注意)すべてabstractで(defaultの実装なし!)プロパティとメソッドのみ(フィールド、コンストラクタなし!)を定義した型を書けば、自動的にインターフェイスの定義となります。
 
担当者は上のインターフェイスを実装することにより、問題に対処する能力を身に付けたことになります。
 
使う側では、誰を雇うかの参照を保持します。(これは、問題に対処できる能力をもったISovleThatProblemalbe型となります。)
 
例えば使う方を次のように定義してみます。
 
type Superviser (inSolver:ISoloveThatProblemable) =
    let mutable numOfProblem = 0 //解かせる問題のnの値
    let mutable solver:ISoloveThatProblemable = inSolver    //どれを雇って解かせるか
    member this.NumOfProblem //問題設定のプロパティ 
        with get() = numOfProblem
        and set(n) = numOfProblem <- n 
    member this.Solver //どれを雇って解かせるかを設定するプロパティ
        with set(sol) = solver <- sol
    member this.Solve() = //解かせて答えを表示
            printfn "%d" (solver.SolveProblem(numOfProblem))
 
解く方の型を2つ準備しておきます。
 
type SmartKind() =
    interface ISoloveThatProblemable with
        member this.SolveProblem (n) =
            printfn "%d * (%d + 1) / 2 を計算して答えを返します。" n n
            n * (n + 1) / 2
 
 
type FoolKind ()=
    interface ISoloveThatProblemable with
        member this.SolveProblem (n) =
            printfn "1 + 2 + ... + %d をまともに計算して答えを返します。" n
            List.fold (+) 0 [for i in 1 .. n  -> i ]
   
 
解く方のインスタンスを生成しておきます。
let sk = new SmartKind()
let fk = new FoolKind()
監督者クラスのインスタンスを生成しておきます。(解く方は、まずはskの方を登録しておきます。)
let sv = new Superviser(sk)
 
問題のnの値を設定します。
sv.NumOfProblem <- 100
 
skに問題を解かせます。
> sv.Solve();;
100 * (100 + 1) / 2 を計算して答えを返します。
5050
val it : unit = ()
 
解答者をfkにチェンジします。
> sv.Solver <- fk;;
val it : unit = ()
 
問題を解かせます。
> sv.Solve();;
1 + 2 + ... + 100 をまともに計算して答えを返します。
5050
 
なおF#ではObject Expressionを用いると、インターフェイスから直接、そのインターフェイスをインプリメントした、オブジェクトが作成できるので、SmartKind,FoolKindというクラス定義なしで処理することもできます。

let sk = {
            new ISoloveThatProblemable with
              member this.SolveProblem (n) =
                let n = solver.NumOfProblem
                printfn "%d * (%d + 1) / 2 を計算して答えを返します。" n n 
                n * (n + 1) / 2}
 
 
さてインターフェイスの型は上の例題では問題解決の為の材料がint,問題解決結果の値がintなので
int->intとなりましたが、複雑な問題では、引数にSuperviser型を渡す必要があるかもしれません。
この場合は、ISoloveThatProblemableと Superviserが型として相互参照となるので、次のようになります。
 
type ISoloveThatProblemable =
    abstract SolveProblem : Superviser -> int
 
and Superviser (inSolver:ISoloveThatProblemable) = //andになっている部分に留意!
    let mutable numOfProblem = 0 //解かせる問題のnの値
    let mutable solver:ISoloveThatProblemable = inSolver    
    member this.NumOfProblem  
        with get() = numOfProblem
        and set(n) = numOfProblem <- n 
    member this.Solver 
        with set(sol) = solver <- sol
    member this.Solve() = 
            printfn "%d" (solver.SolveProblem(this))//ここがthisとなります
 
 
type SmartKind() =
    interface ISoloveThatProblemable with
        member this.SolveProblem (solver) =
            let n = solver.NumOfProblem
            printfn "%d * (%d + 1) / 2 を計算して答えを返します。" n n
            n * (n + 1) / 2
 
 
type FoolKind() =
    interface ISoloveThatProblemable with
        member this.SolveProblem (solver) =
            let n = solver.NumOfProblem
            printfn "1 + 2 + ... + %d をまともに計算して答えを返します。" n
            List.fold (+) 0 [for i in 1 .. n  -> i ]
 
ただこうなると、Interfaceとそれを使うクラスの関係が密になりすぎるのと、デバッグがしにくいという問題点が発生します。できる限り避けた方がよいかと思います。
スポンサーサイト

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

コメントの投稿

非公開コメント

プロフィール

T GYOUTEN

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

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

この人とブロともになる

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