Code

2014年2月16日日曜日

Spring Framework で Strategy パタン

 これから、私たちのシステムの Generalisation をしますが、なかなかやり方が決まらなくて、どうするかを考えてみました。一応メモをとっておきます。
 まず、事情を説明すると、現行システムは一つの顧客しか対応してなくて、顧客が増えるたびに、同じクラスで関係ありそうなところを if... else... や、 switch 文を入れたりして、分枝を作っていました。それが良くないことだとみんながわかっています。顧客が増えるたびに、すべてのソースコードをいじりますし、他の顧客を影響しないようにテストの負担も大きいです。それで、今回で、もう Unit テストや、 E2E テストをすべて一回整理して、システムの Refactoring をやろうと決めたのです。最初はコストのかかる仕事ですけど、将来の開発を考えると、やらざるえない選択だと思います。
 いろいろ考えた結果、今使っている Spring Framework をベースにして、DI を使います。最初は共通処理をインターフィースとスーパークラスに抽出します。 Eclipse の Refactoring 機能を使って、簡単にできるはずです。次、各顧客独自の仕様を抽出して、チャイルドクラスに移ります。そうすると、顧客の間は影響しないようになります。もし何か特別な市よりがあったら、顧客の実現クラスにスーパークラスのメソッドを override したら、問題ない。システムの構造も理想です。将来開発担当者を変わっても、他のプログラマーもすぐ何所でどのように修正するかはすぐにわかります。
 肝心なところはどのように対象クラスを選択して、処理を Delegate するかになります。一つの方法は Factory クラスを作って、顧客の名前を渡して、対象クラスを返すようになります。ただ、そうすると、顧客が増えるたび、Factory クラスをいじらなければならないです。前よりかなり進んでますが、もっと良い方法がないかと考えました。
 どうせ顧客の名前や ID に基づいて選択するから、マップを作って、そこから選択しようと。
 まず、各顧客のクラス Bean を定義します。
<bean id="Customer1Strategy" class=" xxx.xxx.Customer1StrategyImpl" />
<bean id="Customer2Strategy" class=" xxx.xxx.Customer2StrategyImpl" />
<bean id="Customer3Strategy" class=" xxx.xxx.Customer3StrategyImpl" />
 次、 Strategy マップを作ります。
<bean id="CustomerStrategies" class="xxx.xxx.CustomerStrategies" >
   <property name="strategies">
     <map>
       <entry>
         <key>
           "Custromer1"
         </key>
           <ref bean="Customer1Strategy" />
        </entry>
       <entry>
         <key>
           "Custromer2"
         </key>
           <ref bean="Customer2Strategy" />
        </entry>
       <entry>
         <key>
           "Custromer3"
         </key>
           <ref bean="Customer3Strategy" />
        </entry>
    </map>
  </property>
</bean>
 まぁ、その後は簡単です。CustomerStrategies をサービスに Inject して、サービスを呼び出すとき、顧客の ID をセットして、
ICustomerStrategy strategy = (ICustomerStrategy)customerStrategies.get(custmoerID);
を呼ぶと、顧客ごとのクラスが取得できるようになります。
 
 とりあえず、ここまで。それでは。

0 件のコメント:

コメントを投稿