25
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

沈思黙考:デザインパターン(Abstract Factory パターン)

Last updated at Posted at 2016-09-23

Abstract Factory パターンとは

通常アプリケーションは、複数のコンポーネント(クラス)を利用していて、それらのインスタンスを得るのに、

fig1.アプリの例
class MyApp {
 public void execute() {
  CompA a = new CompA();
  CompB b = new CompB();

のようにしている。これを、Factoryクラスを作り、そのメソッドを経由してコンポーネントのインスタンスを得るようにする。たとえば、

fig2.Factory経由でインスタンスを得るアプリ
class MyApp {
 public void execute() {
  Facory f = new Factory()
  CompA a = f.createCompA();
  CompB b = f.createCompB();

のようにすると、コンポーネントの生成処理がFactoryクラスにまてめて記述されるようになる。このようにするには、Factoryクラスは、

fig3.抽象クラスにする前のFactory
class Factory {
 public CompA createCompA() {
  return new CompA();
 }
 public CompB createCompB() {
  return new CompB();
 }

のように定義すればよい。
これで、コンポーネントのインスタンスの生成処理がアプリケーションから独立したことになり、アプリケーションに変更を加えることなく、インスタンスの生成処理を変更することができるようになるということである。
さらに、Factoryを抽象クラスにして、実際にコンポーネントのインスタンスを生成するメソッドをFactoryの具象クラスに記述するように変更すると、アプリケーションで利用しているコンポーネントをごっそり入れ替えることができることになる、というのがAbstract Factory パターンである。

fig4.コンポーネントの構成の例(CompA,CompBを抽象クラスにする)
abstract class CompA {
  abstract public void somethingA();
}
abstract class CompB {
  abstract public void somethingB();
}
class CompA1 extends CompA {
 :
}
class CompA2 extends CompA {
 : 
}
class CompB1 extends CompB {
 : 
}
class CompB2 extends CompB {
 : 
}
fig5.Factoryを抽象クラスにし、具象クラスFactoryX,FactoryYを定義する
abstract class Factory {
 abstract public CompA createCompA();
 abstract public CompB createCompB();
}
class FactoryX extends Factory {
 public CompA createCompA) {
  return new CompA1();
 }
 public CompB createCompB() {
  return new CompB1();
 }
class FactoryY  extends Factory {
 public CompA createCompA() {
  return new CompA2();
 }
 public CompB createCompB() {
  return new CompB2();
 }
fig7.Factoryの具象クラスを切り替えるアプリ例
class MyApp {
 /*
  * flagでFactoryの具象クラスを指定する
  */
 public void execute(flag) {
  if(flag == 0) {
   Facory f = new FactoryX()
  } else {
   Facory f = new FactoryY()
  }
  execute(f);
 }
 /*
  * 具象クラスを意識しないコードになる
  */
 public void execute(Factory f) {
   CompA a = f.createCompA();
   CompB b = f.createCompB();
  } 

Abstract Factory パターンは、コンポーネントを抽象化することを要求しているわけではないが、
コンポーネントを入れ替える、という目的がある場合には必要になる

Abstract Factory パターンのご利益(アプリ製作者編)

デザインパターンのメリットを考えるには、誰が恩恵を受けるのか、という視点が必要だ。
まずは、MyApp製作者の受けるメリットを考える。
通常アプリケーション製作者は、使っているコンポーネントをごっそり入れ替えたいという要求を最初から持ち合わせていることはないだろう。よって、Abstract Factory パターンを使っても、コード量が増えるだけであまりメリットはない。
でも、たとえば、テストをしているときに、一部のコンポーネントを置き換えたいとか、デバック用のコンポーネントと一時的に置き換えたい、といった要望が出た場合には、このパターンは有効だ。
と考えると、コードは増えるとはいえ、非常に単純なパターンなので、最初から設計に組み入れておくのも有用かもしれない。

Abstract Factory パターンのご利益(コンポーネント製作者編)

次にコンポーネント製作者の受けるメリットを考える。
ここでいうコンポーネントとは、アプリのサブシステムとしてのコンポーネントではない。ここでいうコンポーネントとは、ある目的のためのクラス群といったもので、たとえば、DAO(Data Access Object)とか、Web Tool Kit といったものを想定している。
この場合、コンポーネント自身が抽象化されているので、正しいコンポーネントの具象クラスを選択することが、利用者の課題となる。Abstract Factory パターンを使うと、コンポーネントの生成方法がFactory経由に限定されるので、利用者は正しいコンポーネントを常に選択することができるようになる。これはコンポーネント開発において、大きな設計上のメリットになるといえるだろう。
では、一般のアプリ開発では、置き換え可能なコンポーネントを設計した上でアプリでそれを使うというケースはどのような場合か考えてみよう。
たとえば、料金体系がユーザーのステータスによって異なるようなケースはどうだろう。料金表をFactoryから得るようにすれば、ユーザーごとにFactoryを切り替えることで正しい料金計算がされる、といった利用が考えられる。表とか定義をFactoryから得るというのは、自然だと思う。

結論

Abstract Factory パターンは、newしている箇所をFactoryの具象クラスを経由して得る、というだけのアイデアであるが、適用できる場所はかなりある。

蛇足(factory methodパターンとの区別)

Abstract Factoryパターンとfactory methodパターンを混同している方が見受けられるが、Factoryクラスが存在する場合は、Abstract Factoryパターンである。factory methodパターンにはFactoryクラスはない。

GoF本でも、factory methodパターンの説明のクラス図は確かに似ているけれど
クラス名を良くみるとFactoryクラスではなくCreatorクラスと書かれている。
適当に名前をつけているのではない。名前には意味がある。
クラス名がCreatorで、メソッド名がFactoryMethod()になっている。

  • 参考文献
    オブジェクト指向における再利用のためのデザインパターン 改訂版
    1999/11/1 初版
25
19
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
25
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?