Deep Side of Java〜Java 言語再入門 第3回 〜 クラス設計とデザインパターン

やさしいデザインパターン

Factory Method と Abstract Factory






やさしいデザインパターン

デザインパターンは標準の Java クラスライブラリでも頻繁に使われている。基本的なものをまず実例によって示す。

Factory Method と Abstract Factory

これらはオブジェクト生成についてのデザインパターンである。「Factory」という名がついた標準クラスライブラリは JDK 1.3 で 19個もあった。このように「Factory(工場)」の概念は、オブジェクト生成を実際のクラス名をハードコーディングせずに、1クッションを置いてやることで再利用性を高めるというアイデアである。たとえば、次のコードは生成するクラスを決め打ちしているため、生成するクラスに手を加えたいためにクラスを派生させたら、クラス名を変更しなくてはならない。

     SomeClass instance = new SomeClass();
     instance.someMethod();

単にメソッドに分割するだけでも、随分再利用性は上がる。

class App {
    ..............
        SomeClass instance = createSomeClass();
        instance.someMethod();
    }

    public SomeClass createSomeClass( ) { 
        return new SomeClass(); 
    }
}

なぜなら、このクラスを継承して新しいクラスを作り、この createSomeClass() メソッドだけを上書きすれば、実際には SomeClass から派生したクラスを使うことができるからである。

class NewApp extends App {
    public SomeClass createSomeClass( ) {
        return (SomeClass)new SubOfSomeClass();
    }
}

この時、ひょっとしたら App クラスは抽象クラスで、createSomeClass は抽象メソッドかもしれない。それでも良いのである。これを「Factory Method」と呼ぶ。

また、これは観点を変えると、「複数の部品」を作る「工場」があり、「どの工場か」を指定することで、複数の関連する具体的な部品の実装が異なるようなこともできる。この観点から見た時には「Abstract Factory」と呼ばれる。たとえば、

    Factory f = new Factory();
    PartsA pa = f.createPartsA();
    PartsB pb = f.createPartsB();
    PartsC pc = f.createPartsC();
................

class Factory {
    public PartsA createPartsA() { ............ }
    public PartsB createPartsB() { ............ }
    public PartsC createPartsC() { ............ }
}

当然、この Factory クラス自体を抽象クラスにして、それぞれの creator を抽象メソッドにすることも出来る。これだと、Fatory Method と合体したパターンになる。

    Factory f = getFactory( "SONY" );
    TV tv = f.createTV();
    Video vid = f.createVideo();
    ..................
    
    public Factory getFactory( String maker ) {
        if( maker.equals( "SONY" ) ) {
            return (Factory)new SonyFactory();
        } else if( maker.equals( "Sharp" ) ){
            return (Factory)new SharpFacotry();
        } else if .............
    }
}

class SonyFactory implements Factory {  /* extends Factory かも.. */
     public TV createTV( ) { ............. }
     public Video createVideo( ) { ....... }
}

このように、オブジェクト生成に関して抽象レベルを導入することで、再利用性を向上することができる。このようなテクニックでは Java がインタプリタである利点を利用するとさらに強力な方法をとることが可能である。つまり、「動的ローディング」である。

class myApp {
    public static void main( String [] args ) {
        ...............
        Factory f = getFactory( args[0] );  /* 引数は String で */
        TV tv = f.createTV();
        Video vid = f.createVideo();
        ..................
    }
    
    public Factory getFactory( String maker ) {
        try {
            Class c = Class.forName( maker );
            Factory ret = (Factory)c.newInstance();
        } catch( Exception e ) {
            System.out.println( maker + "では Factory クラスの" +
                                "インスタンスを製造できません" );
            /* 要するに引数で渡したクラスが Factory 派生クラスで
               なければキャストによる例外が発生する */
            System.exit( 1 );
        }
        return ret;
    }
}

この例では、起動引数で生成すべき Factory から派生した(あるいはFactory interface を implements した)クラス名を渡し、その Factory から具体的なインスタンスを生成している。かなり重要で使いでのあるテクニックである。もちろん何かのプロパティファイルからこの生成すべき Factory 名を取得しても構わないのである。



copyright by K.Sugiura, 1996-2006