Java 程式範例來探討 Design Pattern: Factory Method

作者:李元豪

使用的時機與目的:

當我們撰寫應用程式時,如果無法明確地知道要產生的類別,需使用那一種物件型態時,你應該考慮使用 Factory Method 模式,讓子類別去指定要生成物件的類型;比如說現在你要寫一個能使用多種文件型態的應用程式 (Application),有時候你想使用 text 型態的文件,有時候你想使用 Doc. 型態的文件,如果你把某種文件的類型寫死在應用程式時,程式將變得毫無彈性,不容易修改,不能再利用。

不良的解決方式:

class TextDocument{
        TextDocument(){
                System.out.println("I am class TextDocument");
        }
        void Open(){
               System.out.println("Open a TextDocument");
        }
        void Save(){
               System.out.println("Save the TextDocument");
        }
        void Close(){
               System.out.println("Close the TextDocument");
        }
}

class MyApplication{
        public static void main(String[] args){
                TextDocument doc = new TextDocument();
                doc.Open();
                doc.Save();
                doc.Close();
        }
}

像這樣把某種文件類型寫死在應用程式裡,如果你想使用 Doc. 的文件類型,那 MyApplication 類別勢必要重寫。如果你的應用程式需要用到數十種不同的文件類型,那 MyApplication 類別是不是要重寫數十遍?上面的程式例子還簡單,如果你的應用程式很複雜,單單修改程式碼就夠你瞧,更不用說測試、除錯了!

使用 Factory Method 模式:「Design Patterns」這本書提到有三、四種解決的方式。

使用參數的方式:

程式碼:

public abstract class Document{
    public abstract void Open() ;
    public abstract void Save() ;
    public abstract void Close() ;
}

public abstract class Application{
    int select ;
    public abstract Document CreateDocument(int select) ;
    public void NewDocument(){
        Document doc = CreateDocument(select);
        doc.Open() ;
        doc.Save() ;
        doc.Close() ;
    }
}

public class TextDocument extends Document{
    public TextDocument(){
        System.out.println("I am class TextDocument");
    }
    public void Open(){
        System.out.println("I open a TextDocument");
    }
    public void Save(){
        System.out.println("I save the TextDocument");
    }
    public void Close(){
        System.out.println("I close the TextDocument");
    }
}

public class DocDocument extends Document{
    public DocDocument(){
        System.out.println("I am class DocDocument");
    }
    public void Open(){
        System.out.println("I open a DocDocument");
    }
    public void Save(){
        System.out.println("I save the DocDocument");
    }
    public void Close(){
        System.out.println("I close the DocDocument");
    }
}

public class MyApplication extends Application{
  
int select ;
    public MyApplication(){
    }
    public Document CreateDocument(int select){
        if (select==1) return new TextDocument();
        if (select==2) return new DocDocument();
        return new TextDocument();
    }
}

public class ExampleFactory2 {
  
public ExampleFactory2() {
    }
  
public static void main(String[] args){
          MyApplication App = new MyApplication();
          App.select = 1 ; // 使用 TextDocument
          // App.select = 2 ; // 使用 DocDocument
          App.NewDocument();
    }
}

使用類別變數的方式:

程式碼:

public abstract class Document 程式碼同上。
public class DocDocument 程式碼同上。
public class TextDocument 程式碼同上。

public abstract class Application{
  
protected Document doc;
    public Application(){
    }
    public abstract Document CreateDocument() ;
    public void NewDocument(){
        doc = CreateDocument();
        doc.Open();
        doc.Save();
        doc.Close();
    }
}

public class MyApplication extends Application{
  
public MyApplication(){
    }
    public Document CreateDocument(){
        return doc;
    }
}

public class ExampleFactory3 {
  
public ExampleFactory3() {
    }
    public static void main(String[] args){
        MyApplication App = new MyApplication();
        App.doc = new TextDocument();
        // App.doc = new DocDocument();
        App.NewDocument();
    }
}

平行類別階層方式:

程式碼:

public abstract class Document 程式碼同上。
public class DocDocument 程式碼同上。
public class TextDocument 程式碼同上。

public abstract class Application{
  
public abstract Document CreateDocument() ;
}

public class DocApplication extends Application{
  
public Document CreateDocument(){
        return new DocDocument();
    }
}

public class TextApplication extends Application{
  
public Document CreateDocument(){
        return new TextDocument();
    }
}

public class ExampleFactory4 {
  
public ExampleFactory1() {
    }
    public static void main(String[] args){
        DocApplication DocApp = new DocApplication();
        Document doc = DocApp.CreateDocument();
        // TextApplication TextApp = new TextApplication();
        // Document doc = TextApp.CreateDocument();
        doc.Open();
        doc.Save();
        doc.Close();
    }
}