CodeZine(コードジン)

特集ページ一覧

ラムダ式でTemplate Methodパターン+Factory Methodパターンで実装されたコードをシンプルにする ~ 継承から委譲、そして単なる手続きへ

デザインパターンを置き換えよう! Javaラムダ式によるシンプルコーディング 最終回

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2015/03/27 14:00

 これまでの連載では、Commandパターン、Strategyパターン、Observerパターンのプログラムについて、プログラムの基本的な構造を変えずにラムダ式に置き換えてきました。既存のプログラムの構造を大きく変えずに使えることはラムダ式の利点の1つですが、時には構造を大きく変えてラムダ式を活用することで、よりよい設計が実現できる場合があります。連載最終回となる本稿では、そのような例を取り上げます。

目次

 具体的には、前回題材としたジョブコントローラを再度用いて、Template MethodパターンとFactory Methodパターンの組み合わせをAbstract Methodパターンに変換し、さらにラムダ式を活用するように書き換えます。

Template Methodパターンのおさらい

 まずはTemplate Methodパターンについておさらいしましょう。Template Methodパターンは、スーパークラスに共通処理を集約し、さまざまに差し替えられる処理の細かい部分をサブクラスに任せるパターンです。典型的なTemplate Methodパターンのプログラムには、図1に示すようなクラスが登場します。

図1:Template Methodパターンクラス図
図1:Template Methodパターンクラス図

 

 それぞれのクラスの役割は次のとおりです。

  • AbstractClass:共通処理を表すテンプレートメソッドを実装するクラス。差し替えられる処理を抽象メソッドとして宣言する。テンプレートメソッドは差し替え可能な処理を呼び出す
  • ConcreteClass:AbstractClassに宣言された差し替え可能な処理を実装するクラス

 Template Methodパターンを使っている例としては、Java標準のコレクションフレームワークが挙げられます。たとえば、AbstractList クラスと ArrayList クラスは図2のような継承関係を持ちます。

図2:AbstractList-ArrayListクラス図
図2:AbstractList-ArrayListクラス図

 

 ここでは AbstractList クラスがAbstractClassに、ArrayList クラスがConcreteClassに該当します。AbstractList クラス(およびそのスーパークラスである AbstractCollection クラス)では、isEmptyindexOfequals などのメソッドが共通的に実装されており、iteratorgetsize の3メソッドが抽象メソッドとして宣言されています。したがって、ArrayList のように具体的なリストの実装クラスは、最低限 iteratorgetsize の3メソッドだけを実装すれば、リストの機能を提供できます1

 Template Methodパターンは、クラス同士の継承関係が現れる多くのプログラムで用いられています。筆者の経験では、Template Methodパターンを用いていないクラス同士の継承関係は、大抵の場合、まずい設計です。このようなプログラムは、本稿の後半で解説する「継承から委譲へ」の方針を適用することで、設計を改善できます。

[1] 実際には、ArrayList クラスは性能最適化のために isEmpty メソッドや indexOf メソッドなどをオーバーライドしています。

Factory Methodパターンのおさらい

 Factory MethodパターンはTemplate Methodパターンの特殊な例といえます。具体的には、Template Methodパターンで差し替えられる処理が、オブジェクトを生成する処理である場合に、Factory Methodパターンが用いられていると考えられます。

 典型的なFactory Methodパターンのプログラムには、図3に示すようなクラス、インタフェースが登場します。

図3:Factory Methodパターンのクラス図
図3:Factory Methodパターンのクラス図

 

 それぞれのクラス、インタフェースの役割は次のとおりです。

  • Creator: ファクトリメソッドを差し替え可能な処理として宣言する抽象クラス。Template MethodパターンのAbstractClassに相当する
  • ConcreteCreator: ファクトリメソッドを実装するクラス。Template MethodパターンのConcreteClassに相当する
  • Product: 生成されるオブジェクトのインタフェース
  • ConcreteProduct: 実際に生成されるオブジェクトのクラス

 ファクトリメソッドを差し替え可能とする動機は、Creatorに相当するクラスで新しいConcreteProductを生成・利用したくなったときに、Creatorクラス自体に手を入れずに済むようにすることです。

 また、Factory Methodパターンを用いることで、テストが容易になる場合があります。たとえば、リスト1のWarblerクラスは、Twitter4jライブラリを利用してTwitterにさえずり声を投稿するウグイスのシミュレータです。Warbler クラスを単体テストする際、いちいちTwitterにさえずり声が投稿されるのは望ましくないでしょう。リスト1では、Twitterへの投稿を行う Twitter クラスのインスタンスを、ファクトリメソッドである makeTwitter メソッドに切り出していますから、単体テストの際は、実際には投稿を行わないスタブやモックを生成するように、makeTwitter メソッドをオーバーライドすればよいのです。

リスト1: Warbler1.java
import twitter4j.*;

/** ウグイス. */
public class Warbler1 {

    /** さえずる. */
    public void warble() {
        Twitter twitter = makeTwitter();
        try {
            twitter.updateStatus("ケキョケキョケキョ");
        } catch (TwitterException exception) {
            throw new WarblerException("さへずらざりけり", exception);
        }
    }

    /** Twitterクライアントのファクトリメソッド. */
    protected Twitter makeTwitter() {
        TwitterFactory twitterFactory = new TwitterFactory();
        return twitterFactory.getInstance();
    }
}

 先にも述べたように、前回題材としたジョブコントローラプログラムはFactory Methodパターンを採用しています。図4のクラス図を見てください。

図4:ジョブコントローラのクラス図(再掲)
図4:ジョブコントローラのクラス図(再掲)

 

 このジョブコントローラプログラムでは、Factory Methodパターンの各役割はそれぞれ次のように担われています。

  • JobDefinitionクラス: ジョブを生成して利用するCreator
  • BackupJobDefinitionクラス: バックアップジョブを生成するConcreteCreator
  • Jobクラス: Product
  • BackupJobクラス: ConcreteProduct

 これは特にひどい設計というわけではありません。しかし、ジョブのクラスを作るごとに、いちいち対応するジョブ定義のクラスを作る必要があるというところに、若干の野暮ったさが感じられます2BackupJobDefinition クラスは makeJob メソッドを呼び出しているだけなのですから、なんとかラムダ式に置き換えられないか、と考えるところですが、残念ながら直接的にラムダ式を使うことはできません。JobDefinition クラスは抽象クラスであって、関数型インタフェースではないからです。

 ラムダ式を使ってよりよい設計を実現するためには、まずFactory MethodパターンのプログラムをAbstract Factoryパターンに変更する必要があります。

[2] Martin Fowler et al. 1999 "Refactoring: Improving the Design of Existing Code" では、この種の野暮ったさに“Parallel Inheritance Hierarchies”と名前を付け、「コードの臭い」として取り上げています。


  • LINEで送る
  • このエントリーをはてなブックマークに追加

あなたにオススメ

著者プロフィール

  • 宮川 拓(ミヤカワ タク)

    日本Javaユーザーグループ幹事。東京のシステムインテグレータに勤務。Java VM上で動作する言語である「Kink」を開発中。相撲とアメリカ文学とスコティッシュポップを愛する。 ・ブログ: http://d.hatena.ne.jp/miyakawa_taku/ ・Twitter: @...

バックナンバー

連載:デザインパターンを置き換えよう! Javaラムダ式によるシンプルコーディング
All contents copyright © 2005-2021 Shoeisha Co., Ltd. All rights reserved. ver.1.5