dorivenの日記

気がついたら社会人。気になる技術的なことについて少しずつ書いていけたらと思っております。

【デザインパターン】デザインパターンの振り返り【前篇】

最近の近況。
ジャーナルと戯れています。
リファクタリングしたコードがまたリファクタリングが必要なくらい汚くなってきたので、リファクタリングしようか考え中。
でも、自分の研究を引き継いでくれる人が今のところ居ないんだよな~。
さて、今回はデザインパターンについての振り返りです。
次の回ではデザインパターンを終わりにしようと思っています。
次のテーマは何にしようか現在思案中。

参考書



さらっと読める「OREILLY Head First デザインパターン」を参考にしながら書いています。
しばらくはこいつを参考にデザインパターンについて書いていくよ!

概要



前回の「【デザインパターン】パターンの有効利用 - datchの日記」で今までのデザインパターンについて触れましたが、
今回は振り返りということで本で紹介された(最後の章で軽く紹介しているパターンは除く)について一覧して記述していきたいと思います。
復習ということなので、自分の言葉でデザインパターンについて書くので間違ったことを書いていたらすみません。

Strategy



Strategyパターンはinterfaceを通して作った複数のクラスを入れ替えることで動作に柔軟をもたせるパターンです。

interface DoSomething
{
  public do();
}

class StrategyOne implements DoSomething
{
  public do(){ /* 行動A */ };
}

class StrategyTwo implements DoSomething
{
  public do(){ /* 行動B */ };
}

// public static int main とクラス定義を省略
{
  DoSomething doObj;

  // ここで代入するクラスによって処理を柔軟に切り替えることが可能になる
  // もちろん、interfaceを使っているので呼び出しの部分は同じ
  // つまり、このオブジェクトを使っているコードを変更することなく、
  // クラスの変更で処理に対応することができるようになるよ!
  public initDo(int type) // enum使えよというツッコミはなしで
  {
    if(type == 1)
    {
      doObj = new StrategyOne();
    }
    else if(type == 2)
    {
      doObj = new StrategyTwo();
    }
  }

  public callDo()
  {
    doObj.do();
  }
}

Observer



このパターンは、監視者(Observer)と被監視側(Subject)が存在しており、Subjectの変更をObserverが知ることで特定の処理を実行します。
特定の処理をした結果を画面に表示させる、といった場合に使ったりします。
注意として「Subjectの方がObserverに対して通知をする必要がある」、ということです。
他にもイベントリスナーを登録している、といえばピンとくる人もいるかも?
監視者というからにSubjectの状態が変わったら自動的に動作して欲しい!、と思うでしょうがプログラミング言語はそこまで人間の意図を汲み取ってくれません。
だからこそデザインパターンってものが必要なのですが :-)
ちなみにこのパターンはJavaの組み込みクラス(java.util.Observer/Observable)で事前に定義されてます!
詳しくは公式リファレンスを参照してください。
Observable (Java Platform SE 6)

class Subject extends Observable
{
  public void do()
  {
    /*
     * ここに何かしたい処理を書く
     */
    setChange(); // setChangeは状態が変わったことを示す関数です。これが呼び出されないとObserverに通知をしても動作しません。
    notifyObservers(); // 上記のsetChangeとセットで呼び出します。これで登録しているObserverが処理を開始します
  }

  // これらの関数はObservableですでに定義済みです!
  public void addObservers(); // Observerの追加
  public void deleteObservers(); // Observerの削除
  public void notifyObservers();
  public void setChange();
}

class ObserverOne implements Observer
{
  public void update(Observable obs, Object arg){
    /*
     * ここで何かしたい処理を書く
     */
  }
}

Decorator



Decoratorは特定の処理に上乗せして処理を付け加えたいときに使うパターンです。
メソッドを追加するのと違う点は、機能の拡張か新規追加という点で異なります。
なので、まったく異なるinterfaceのものをDecoratorで機能追加するという事は意図が変わってしまいますね。
ちなみに、JavaではInputStreamやOutputStreamなどの拡張にDecoratorが使わています。

話は変わるが、torとterってなんで違うんだろうって調べたらこんなの出てきた。
こういうのってみんな習ったりするんだろうか?
tor と ter があるのはなぜ?/アルコムワールド

interface Component
{
  public void do();
}

class BaseFunction implements Component
{
  public void do()
  {
    // 基本処理
  }
}

abstract class Decorator implements Component
{
  protected Component component;
  // コンストラクタで拡張したいコンポーネントを取得する
  Decorator(Component component)
  {
    this.component = component;
  }
}

class DecoratorFunctionA extends Decorator
{
  public void do()
  {
    /*
     * 追加機能A
     */
    this.component.do(); // 格納済みのコンポーネント(基本処理)を呼び出す

  }
}

class DecoratorFunctionB extends Decorator
{
  public void do()
  {
    /*
     * 追加機能B
     */
    this.component.do(); // 格納済みのコンポーネント(基本処理)を呼び出す

  }
}

// public static int main とクラス定義を省略
{
  new DecoratorFunctionA(new DecoratorFunctionB(new BaseFunction())).do();
  /**
   * 実行順序は 「基本処理 追加機能A → 追加機能B → 基本処理」という順番で実行される
   */
}

Factory



Factoryはクラスのインスタンスを一元管理してくれるありがたいパターンだ。
このパターンを使えば、Strategyパターンでインスタンス生成時に動的に処理を変更したい時に役に立つ。
先ほどの上のコードでもif(type==X)となっている部分を抜き出していあげれば、インスタンスの生成に関しての処理を切り出すことが可能になる。
単純にインスタンスの生成だけを肩代わりするなら、パターンというよりはイディオムである。
FactoryにはFactoryMethodAbstractFactoryが存在する。

FactoryMethodとAbstratFactoryの違いですが、どのようなインスタンスを生成するかの詳細を知っているか、知っていないかの違いである。
例でも紹介したが、ピザがあるとしてFactoryMethodが生成するピザ(チーズ、野菜、ペパロニ、etc)についてを知っている。
逆にAbstractFactoryはピザ自身がどのような素材が必要かについて知っています。
これは、それを生成する側が主体になっているか、生成される側が主体になっているか、の違いです。

下はFactoryMethodソースコードです。
Factoryがピザの具体的な型を知っていることが分かります。

class PizzaFactory
{
  
  public orderPizza(String type)
  {
    Pizza pizza = createPizza(type);

    pizza.prepare();
    pizza.bake();
    pizza.cut();
    pizza.box();
  }

  abstract createPizza(String type);
}

class JapanPizzaFactory extends PizzaFactory
{
  public Pizza createPizza(String type)
  {
    
    if(type == "cheese")
    {
      return new JapanCheesePizza();
    }
    else if(type == "vegetable")
    {
      return new JapanVegetablePizza();
    }
    ///
  }
}

こちらがAbstractFactoryソースコードです。
FactoryMethodと違い、どのようなピザを作るかの詳細(どの地方に向けたピザか)を知っているのはピザ自身です。

// PizzaFactoryは同様

class JapanPizzaFactory extends PizzaFactory
{
  public Pizza createPizza(String type)
  {
    PizzaIngredientFactory ingredientFactory = new JapanPizzaIngredientFactory();
    if(type == "cheese")
    {
      return new CheesePizza(ingredientFactory);
    }
    else if(type == "vegetable")
    {
      return new VegetablePizza(ingredientFactory);
    }
    ///
  }
}

AbstractFactoryインスタンスの生成工程を行うのがインスタンス化を行いたい基底クラスです。
つまり、AbstractFactoryはPizzaクラスの中でFactoryMethodが行われているということになります。

Singleton



シングルトンパターンはプログラムのプロセスでインスタンスがひとつのみ存在することを保証してくれるパターンです。
シングルトンはクラスフィールドとメソッドを使用して実装するので、グローバルにアクセスが可能になります。
グローバルなことにより、インスタンスが永続して残るのでテストが難しくなるという副作用も抱えています。

いかがそのコードです。

class Singleton
{
  private static Singleton uniqueInstance = new Singleton();
  private Singleton(){};
  public static Singleton getInstance()
  {
    if(uniqueinstance  == null)
    {
      // マルチスレッドの場合で複数のインスタンスが生成されないようにする解決手法
      synchronized (Singleton.class)
      {
        if(uniqueInstance == null)
        {
          uniqueInstance = new Singleton();
        }
      }
    }
    return uniqueInstance;
  }
}

おわりに



本を再度読みなおしてもう一度理解を深めていけたので良かった。
特にDecoratorとFactoryについては大分忘れていた部分もあったので、色々と思い出す事が出来た。
次回でデザインパターンは終わりにしたいので、頑張って行きたい。