これから、私たちのシステムの 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);
を呼ぶと、顧客ごとのクラスが取得できるようになります。
とりあえず、ここまで。それでは。