TSでポリシーモードを学習する

5089 ワード

ポリシーモード(Strategy Pattern)
定義:ポリシー・モードは、一連のアルゴリズムを定義し、各アルゴリズムをカプセル化し、アルゴリズムを使用する顧客から独立して独立して変化させることができます.
参考書は<>
私たちは短い物語を読むことで、戦略モデルが解決した問題を学びます.
まず簡単なアヒルのシミュレーションから始めましょう
Joeが勤めている会社はかなり成功したアヒルのシミュレーションゲームをしました:SimUDuck.ゲーム中にはいろいろなアヒルが現れ、水泳や水遊びをしながらゴーゴーと鳴く.このシステムの内部設計は標準的なOO技術を用いて、アヒルのスーパークラス(Superclass)を設計し、様々なアヒルにこのスーパークラスを継承させた.
abstract class Duck{ //      (    )
    constructor(){}
    quack(){/*   */} //               
    swim(){/*    */} //              
    public abstract display():void //                  
}

class MallardDuck extends Duck{ //     
    constructor(){
        super()
    }
    display(){
        //      
    }
}

class RedHeadDuck extends Duck{ //    
    constructor(){
        super()
    }
    display(){
        //      
    }
}

アヒルが飛べるように
昨年、会社の競争圧力が激化し、会社の責任者は革新する時だと考えています.彼らは「来週」の株主会議で「本当」の印象的なものを見せて人の心を奮い立たせる必要があります.--シミュレーションプログラムのアヒルが飛んで競争者を振り払うことができるようにします.
Joeのマネージャーは胸をたたいて主管たちに、Joeは1週間でできると言った.「結局、ジョーはOOプログラマーだ...何が難しいんだ?」
Joeは、Duckクラスにfly()メソッドを加えるだけで、すべてのアヒルがfly()を継承し、彼が活躍し、OOの才能を示す時だと思っています.
abstract class Duck { //      (    )
    // ...
    quack() { /*   */ }
    swim() { /*    */ }
    //    fly()   
    fly() { /*      */ }
    // ...
}

でも恐ろしい問題が起こりました...
株主会議で多くの「ゴムアヒル」がスクリーンを飛び回った.
ジョーはすべてのアヒルが飛ぶわけではないことを無視したのか.
ジョーは継承を考えた
彼はゴムアヒル類のfly()方法を覆うことができます.
class RubbrDuck extends Duck { //    
    constructor(){
        super()
    }
    // ...
    fly(){ //    fly()   
        //      
    }
    quack(){/*   */}
}

やっとこのバグが解決したので、Joeは息を吐いた.
しかし、これは何か問題をもたらしますか?
例えばこれからカモ(DecoyDuck)を餌にしたいのですが、どうなりますか?餌アヒルは木の偽アヒルで、飛べないし鳴かない...
これは致命的なメンテナンス問題をもたらします.アヒル類がますます多くなると、変化は一発で全身を動かし、他のアヒルが望んでいない変化をもたらし、運行時の行為は容易に変わりません.
ソフトウェア開発の不変の真理
これはJoeのコードが死んだためで、ソフトウェア開発の1つの不変の真理は変化(CHANGE)で、ソフトウェアの設計がどんなに良くても、しばらくしてから、いつも成長と変化が必要で、さもなくばソフトウェアは“死亡します”.
だから私たちは拡張しやすいプログラムを設計して、よく変えるコードを抽出して抽象的に変数やクラスに変えて、書くことができません.
問題をゼロにする
継承は問題をうまく解決できない.アヒルの行為は子で絶えず変化しているため、継承されたコードは親で書くのではなく、子で書くのだ.
設計原則:アプリケーションの中で変更する必要がある可能性のある場所を見つけて、それらを独立して、変化するコードと混同しないでください.
変わる部分と変わらない部分を分けて
どこから始まりますか.私たちが現在知っている限りでは、fly()とquack()の問題を除いて、Duck類はすべて正常です.
「変化と変化しない部分」を区別するために、fly相関であり、quack相関であり、各クラスがそれぞれの動作を実現する2つのクラス(Duckクラスから完全に離れている)を構築する準備をしています.例えば、「ゴーゴー」を実現するクラスと、「ギシギシ」を実現するクラスと、「静か」を実現するクラスがあるかもしれません.
こんな風に
interface QuackBehavior{
    quack():void
}

class Quack implements QuackBehavior{
    quack(){/*     */}
}

class Squeak implements QuackBehavior{
    quack(){/*     */}
}

class MuteQuack implements QuackBehavior{
    quack(){/*     ,   */}
}

interface FlyBehavior{
    fly():void
}

class FlyWithWings implements FlyBehavior{
    fly(){/*    */}
}

class FlyNoWay implements FlyBehavior{
    fly(){
        return undefined //      ,   
    }
}

以上のコードは,実装プログラミングではなくインタフェースプログラミングに対する設計原則を用いた.
そうなると将来メンテナンスも拡張も楽になりますdemo体験を書いてみましょう
type FLYBEHAVIOR = FlyBehavior | undefined //       
type QUACKBEHAVIOR = QuackBehavior | undefined //        
/**
 *      (    )
 * @class Duck
 **/
abstract class Duck{
    public abstract flyBehavior:FlyBehavior //                   
    public abstract quackBehavior:QuackBehavior //                    
    public abstract display():void //              
    constructor(flyBehavior?:FlyBehavior,quackBehavior?:QuackBehavior){ //   Duck      2   
        this.setFlyBehavior(flyBehavior)
        this.setQuackBehavior(quackBehavior)
    }
    setFlyBehavior(flyBehavior:FLYBEHAVIOR){ //         
        this.flyBehavior = flyBehavior
    }
    setQuackBehavior(quackBehavior:QUACKBEHAVIOR){ //          
        this.quackBehavior = quackBehavior
    }
    performFly(){ //       
        this.flyBehavior.fly()
    }
    performQuack(){ //        
        this.quackBehavior.quack()
    }
}

/**
 *      (    )
 * @class MallardDuck
 **/
class MallardDuck extends Duck{ //     
    public flyBehavior:FlyBehavior
    public quackBehavior:QuackBehavior
    constructor(...rest){
        super(...rest)
    }
    display(){
        console.log('    ')
    }
}

let mallardDuck = new MallardDuck() //                 

let flyWithWings = new FlyWithWings() //   

mallardDuck.setFlyBehavior(flyWithWings) //       

let quack = new Quack() //      

mallardDuck.setQuackBehavior(quack) //        

//                            

//     

mallardDuck.performFly() //    

mallardDuck.performQuack() //    

//               
let squeak = new Squeak() //    

mallardDuck.setQuackBehavior(squeak) //            

mallardDuck.performQuack() //    

//                js

設計原則:組み合わせを多用し、継承を少なくする.「ある」は「ある」より良いかもしれません.
まとめ
1ソフトウェアの設計がどんなに良くても、しばらくしてから、いつも成長と変化が必要です.そうしないと、ソフトウェアは「死亡」します.だから、アプリケーションの中で変化が必要な場所を見つけて、独立して、変化が必要なコードと混同しないでください.
2抽出された頻繁に変化するコードは、実装プログラミングではなく、彼らのインタフェースに対してプログラミングしなければならない.
3グループを多用し、継承を少なくする.「ある」は「ある」より良いかもしれません.
関連コードdemo 01
興味のある方はjsをブラウザコンソールにコピーして実行してください.
(完)