Time Flies

fckey's Tech Blog

オブジェクト指向がよくわからない人への特効薬としてのデザインパターン

オブジェクト指向はなんとなく知ってるがどう書いたら良いかわからないという人はとりあえずデザインパターンの勉強でもしてみたらというお話。

オブジェクト指向のイメージといえば実際の"モノ”を表すようにオブジェクトは作られること、設計の特徴としてカプセル化(encapsulation)や継承(inheritance)、多態性(polymorphism)を利用するといったものが定番だろうか。

このように言うは易しなオブジェクト指向ではあるが、概念を理解したところから実際のコードに落としこむところまでは少し距離があるように感じる。 実際、C言語などの手続き型言語を多少書けるようになった人が本や大学の授業で知識として得たばかりの”オブジェクト指向”を利用しようとしたものの、とりあえずクラスを定義しメソッドを書き、main関数でオブジェクトを一つ生成した後、メソッドたちを順番に手続き的に呼び出す状態に陥った例をみる。 このように、概念としてオブジェクト指向はわかるがどう表現するのか分からない人にはデザインパターンを学んでみる事をオススメしたい。

デザインパターンとは「 GoF (Gang of Four)と呼ばれる人達がよく使用されるプログラムのパターンに名前を与え整理した」ものである。詳しくは本を読むなりWikipediaを読むなりググるなりして欲しい。

以下ではCompositeパターンを用いて、デザインパターンによりオブジェクト指向のエッセンスを上手に利用した設計が可能なことを示す。 Composite パターンとは木構造などの入れ子になったデータ構造において、容器と中身を同一視することで再帰的な処理を容易にするものであり、定番の例はディレクトリとファイルの関係である。

ここでは以下の3つのクラスとmainクラスを用いてJavaによりComposite パターンを説明する。

  • 構造の基となる抽象クラスComponent
  • 容器となるクラスDirectory
  • 実際の中身であるクラスFile

以下のコードで示した通りアルゴリズムは至ってシンプルである。 Fileクラス、Directoryクラス共にComponentクラスを継承することでdisplayPathメソッドを持ち、このメソッドはFide,Directoryでそれぞれ異なる実装がされる。 このComponentクラスの継承が容器(Directory)と中身(File)の同一視を可能とする。 Directoryは自身の中にコンポーネント(File、Directory)をもち、それぞれのコンポーネントのdisplayPathメソッドを呼ぶ。これにより再帰的な処理が行われる。このようにCompositeパターンを用いることでファイル数、ディレクトリ数がいくつになっても処理に変更を加える必要なくそのパスを確認することが出来る。

また、以下のコードではnameのカプセル化やComponentの継承、displayPathの多態性の利用などオブジェクト指向を表現する上で必須の概念が使用されている。それに加え、Directoryクラス内のdisplayPathにおいて自身の処理を他オブジェクトに任せる委譲(delegation)という技術も使用されており、これもオブジェクト指向を書く上でよく使用されるので使えるようになるとよい。

基となるComponent

public abstract class Component {
    public abstract String getName();
    public abstract void displayPath(String prefix);
    public void displayPath(){
        displayPath("");
    }
}

自身の名前情報のみを持つFileクラス

public class File extends Component {
    private String name;
    
    public File(String name){
        this.name = name;
    }
    
    @Override
    public String getName() {
        return name;
    }

    @Override
    public void displayPath(String prefix) {
        System.out.println(prefix+"/"+getName());
    }
}

FileおよびDirecrtoryを保持可能なDirectoryクラス

public class Directory extends Component {
    private String name;
    private List<Component> components = new LinkedList<Component>();
    
    public Directory(String name) {
        this.name = name;
    }
    
    @Override
    public String getName() {
        return name;
    }
    
    public void addComponents(Component component) {
        components.add(component);
    }
    
    @Override
    public void displayPath(String prefix) {
        String currentPath = prefix + "/"+ getName();
        System.out.println(currentPath);
        for (Component component : components) {
            component.displayPath(currentPath);
        }
    }
}

mainクラス

public class CompositePattern {

    public static void main(String[] args) {
        File file1 = new File("file1");
        File file2 = new File("file2");
        File file3 = new File("file3");
        File file4 = new File("file4");
        Directory dir1 = new Directory("dir1");
        dir1.addComponents(file1);
        Directory dir2 = new Directory("dir2");
        dir2.addComponents(file2);
        dir2.addComponents(file3);
        dir1.addComponents(dir2);
        dir1.addComponents(file4);
        
        dir1.displayPath("~");
    }
}
出力結果
~/dir1
~/dir1/file1
~/dir1/dir2
~/dir1/dir2/file2
~/dir1/dir2/file3
~/dir1/file4

(※必要最小限の機能だけ書いたためListのremoveがないとかnameだけのファイルはいらないとか野暮なツッコミはここまで読んだ辛抱強い読者からは来ないと信じている)


おすすめのウェブページ: デザインパターン | TECHSCORE(テックスコア)

GoF本。初心者にはおすすめしない。

オブジェクト指向における再利用のためのデザインパターン

オブジェクト指向における再利用のためのデザインパターン

やっぱこれでしょ。

増補改訂版Java言語で学ぶデザインパターン入門

増補改訂版Java言語で学ぶデザインパターン入門