2003年
前回、EJB Strategyパターンのクライアントからの問題点として
以下のようなものがありました。
1.メソッドの引数をEventオブジェクトにしなければならない。
2.その為、コンパイラの型チェックが利かない。
3.メソッド名がhandle〜の為、コードがわかりにくくなる。
今回はこれらを解決します。
その為に
”クライアントとStrategyServerDelegateとの間に、個別のクラスを
用意して、StrategyとEventをクライアントから隠蔽する。”
を行います。
それではソースを見てみましょう。前回から変更・追加したクラスのみ掲載します。
package sample; import ejbstrategy.StrategyServerDelegate; import ejbstrategy.Event; /** * クライアントとStrategyServerDelegateとを仲介して * StrategyとEventをクライアントから隠蔽する。 */ public class SampleService { private StrategyServerDelegate delegate; public SampleService() { this.delegate = new StrategyServerDelegate(); SampleStrategy stategy = new SampleStrategy(); this.delegate.setStrategy(stategy); } /** * 顧客データを取得する。 * @param customerCd * @return 顧客データ */ public String getCustomerData(String customerCd) { Event event = new Event(); event.setData(customerCd); event.setName("getCustomerData"); try { return (String)this.delegate.handleSupports(event); } catch (Exception ex) { throw new RuntimeException(ex); } } /** * 注文データを取得する。 * @param customerCd * @param orderDate * @return 注文データ */ public OrderVO[] getOrderDatas(String customerCd, String orderDate) { OrderVO data = new OrderVO(); data.setCustomerCd(customerCd); data.setOrderNo(orderDate); Event event = new Event(); event.setData(data); event.setName("getOrderData"); try { return (OrderVO[])this.delegate.handleSupports(event); } catch (Exception ex) { throw new RuntimeException(ex); } } /** * 注文データを更新する。 * @param customerCd * @param orderDate */ public void updateOrderData(String customerCd, String orderDate) { OrderVO data = new OrderVO(); data.setCustomerCd(customerCd); data.setOrderNo(orderDate); Event event = new Event(); event.setData(data); event.setName("updateOrderData"); try { this.delegate.handleRequired(event); } catch (Exception ex) { throw new RuntimeException(ex); } } } package sample; import ejbstrategy.*; /** * パターン説明の為のサンプルStrategy */ public class SampleStrategy extends EJBStrategyAdapter { public Object handleRequired(Event event) { //DBの注文データを更新する等の処理をする。サンプルなので適当な値を返す Object result = "executed updateOrderData"; return result; } public Object handleSupports(Event event) { Object result = null; String eventName = event.getName(); if (eventName.equals("getCustomerData")) { //DBから顧客データを読み込む等の処理をする。サンプルなので適当な値を返す result = "executed getCustomerData"; } else if (eventName.equals("getOrderData")) { //DBから注文データを読み込む等の処理をする。サンプルなので適当な値を返す OrderVO[] array = new OrderVO[20]; array[0] = new OrderVO(); array[0].setOrderNo("001"); result = array; } return result; } } package sample; import java.io.*; /** * 注文情報を格納するオブジェクト */ public class OrderVO implements Serializable { private String customerCd; private String orderDate; private String orderNo; public String getCustomerCd() { return customerCd; } public void setCustomerCd(String customerCd) { this.customerCd = customerCd; } public String getOrderDate() { return orderDate; } public void setOrderDate(String orderDate) { this.orderDate = orderDate; } public String getOrderNo() { return orderNo; } public void setOrderNo(String orderNo) { this.orderNo = orderNo; } } package sample; import javax.servlet.*; import javax.servlet.http.*; import java.io.*; import java.util.*; import ejbstrategy.*; /** * パターン説明の為のサンプルServlet */ public class SampleServlet extends HttpServlet { private static final String CONTENT_TYPE = "text/html; charset=Shift_JIS"; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Object result1 = null; Object result2 = null; try { //利用する側からはEJBStrategyパターンを意識させなくなった。 //Eventオブジェクトを使わず、普通のメソッド呼び出しになっている。 SampleService service = new SampleService(); //(1) result1 = service.getCustomerData("00405"); //(2) OrderVO[] array = service.getOrderDatas("00405", "20030903"); result2 = array[0].getOrderNo(); service.updateOrderData("00405", "20030903"); } catch (Exception ex) { throw new ServletException(ex); } response.setContentType(CONTENT_TYPE); PrintWriter out = response.getWriter(); out.println("<html>"); out.println("<head><title>SampleServlet</title></head>"); out.println("<body bgcolor=\"#ffffff\">"); out.println("result1=" + result1); out.println("result2=" + result2); out.println("</body></html>"); } }
SampleServiceクラスを見てみましょう。
コンストラクタでStrategyServerDelegateを生成してSampleStrategy
をセットしてフィールドに保持しています。
そして、 public OrderVO[] getOrderDatas(String customerCd, String orderDate)
等のわかりやすいメソッド名 & シグニチャのpublicメソッドを提供しています。
クライアントはSampleServiceクラスを生成して、publicメソッドを呼び出すだけで
良いのです。EventやStrategyオブジェクト等を扱うことはありません。
それをSampleServletのコードで確認できます。
(1)でSampleServiceクラスを生成して、
(2)でpublicメソッドを呼び出しています。通常のJavaクラスを扱うのと同じです。
これにより、クライアントのコードがとてもわかりやすくシンプルになりました。
前回のSampleServletと見比べてみてください。
それでは最後に、普通にEJBを開発する場合とのクラス構成を比べてみます。
[普通にEJBを開発する場合]
2.リモートインターフェース
3.ホームインターフェース
4.Business Delegate
5.ejb-jar.xml
[EJB StrategyパターンでEJBを開発する場合]
2.リモートインターフェース(StrategyServerを使用する)
3.ホームインターフェース(StrategyServerHomeを使用する)
4.Business Delegate(StrategyServerDelegateを使用する)
5.ejb-jar.xml(StrategyServerEJB用のDDを使用する)
以下が新規に作成が必要
6.EJBStrategyインターフェースを実装、またはEJBStrategyAdapterクラスを
継承したクラス(SampleStrategyのようなクラス)
7.StrategyServerDelegateを隠蔽するラップクラス(SampleServiceのようなクラス)
普通にEJBを開発する場合、1〜5を開発しなければならないですが、
EJB StrategyパターンでEJBを開発する場合、6・7を開発するだけで済むと
いうわけです。
開発するクラスの数は減るわけですが、普通にEJBを開発する場合に比べ
クラス構成が複雑になってしまうというのが、残念な所です。
しかし、6のクラスは普通Javaクラスなので、テストするのが容易で
あるというメリットがあります。6のクラスにビジネスロジックを記述するので
EJBとしてデプロイしなくとも、JUnit等でビジネスロジックを十分にテスト
することが可能です。バグが見つかっても、コードを修正してコンパイル
するだけで、テストを繰り返すことが出来ます。これは生産性において
とても大きなメリットをもたらします。
いかがだったでしょうか?欠点は抱えていながらも、大きな可能性を持って
いそうな気がします。もう少し改良や、昨日の限定をすれば、きっと大きな
メリットをもたらすのではないでしょうか?
とはいえ、EJBを開発して経験のない人にとっては、ピンとこなくて、退屈
だったかもしれません。私の説明も足りなくて分りにくかったかも知れません。
次回は軽い話題にしようということで、「Javaを学ぶ理想的な道のり」について
考えてみたいと思います。
んじゃ
やまろう