天天看點

研磨設計模式筆記之簡單工廠模式1.不用模式的解決方案2.帶模式的解決方案3.思考簡單工廠

1.不用模式的解決方案

1.1 示例

(1)首先定義接口Api,示例代碼如下:

public interface Api {
    public void test1(String s);
}
           

(2)有了Api,自然要有實作,此處功能為輸出字元串,示例如下:

public class ImplA implements  Api{
    @Override
    public void test1(String s) {
        System.out.println("Now In Impl. The input s == " + s);
    }
}
           

(3)用戶端調用實作

public class Client {
    public static void main(String[] args){
        Api api = new ImplA();
        api.test1("哈哈,不要緊張,隻是一個測試而已!");
    }
}
           

1.2 分析問題

可以看到上例中,用戶端已經知道了相應的實作,是以根本沒有做到“封裝隔離”。

要做到“封裝隔離”,用戶端就不應該知道具體的實作是什麼,那麼“new Impl()”就應該封裝起來,讓用戶端看不到。

2.帶模式的解決方案

2.1 簡單工廠重寫示例

(1)Api與相應實作Impl都與上面相同

此處主要說一下簡單工廠Factory,示例如下:

public class Factory {
	//傳回的執行個體包裹在Factory中,這樣用戶端就看不到了
    public static Api create(){
        return new ImplA();
    }
}
           

(2)重寫用戶端代碼,代碼如下:

public class Client {
    public static void main(String[] args){
    	//這個時候就不用顯式寫出new Impl(),而是通過工廠來傳回
        Api api = Factory.create();
        api.test1("正在測試重寫簡單工廠。。。");
    }
}
           

從用戶端來看,不需要知道具體的實作是什麼,也不需要知道如何實作的,隻知道從工廠獲得了一個接口對象,然後通過接口來擷取想要的功能。

2.2 帶選擇的簡單工廠

上面重寫示例中,隻有一種實作,要是有多種實作又該如何呢?

(1)Api和相應實作不變

在上面的基礎上再添加一種實作ImplB,代碼如下:

public class ImplB implements Api {
    @Override
    public void operation(String s) {
        System.out.println("ImplB s == "+ s);
    }
}
           

(2)修改簡單工廠Factory,做到選擇實作,代碼如下:

public class Factory {
    public static Api create(int condition){
        Api api = null;
        if(condition == 1){
            api = new ImplA();
        }else if(condition == 2){
            api = new ImplB();
        }
        return api;
    }
}
           

(3)修改用戶端代碼:

public class Client {
    public static void main(String[] args){
    //用戶端通過1或者2,來選擇相應的實作
        Api api = Factory.create(1);
        api.operation("正在使用簡單工廠");
    }
}
           

2.3 帶選擇的簡單工廠的缺陷

帶選擇的簡單工廠有很大的局限性,如果有多重選擇,那麼就要在工廠中多次判斷,複雜性大大提升。另外從用戶端調用工廠的時候傳入選擇的參數,增加了寫死。

這種複雜性不能放在代碼中來判斷,而應該放在外部,從代碼中獨立出來,放在配置中。

2.4 可配置的簡單工廠

(1)配置檔案用properties檔案,定義一個“FactoryTest.properties”檔案放在工廠類的同一個包下,内容如下:

ImplClass=com.zte.rewriteonproperties.ImplA
           

(2)修改工廠類Factory,代碼如下:

public class Factory {
    
    public static Api createApi(){
    	Properties p = new Properties();
    	InputStream in = null;
    	//讀取配置檔案
        in = Factory.class.getResourceAsStream("FactoryTest.properties");
        try {
            p.load(in);
        } catch (IOException e) {
            System.out.println("裝載工廠配置檔案出錯了,具體的堆棧資訊如下:");
            e.printStackTrace();
        }finally {
            try {
            //讀取完畢,要記住關流
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        Api api = null;
        try {
            try {
            //反射建立執行個體
                api =(Api)Class.forName(p.getProperty("ImplClass")).newInstance();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return api;
    }
}
           

(3)用戶端代碼示例如下:

public class Client {
    public static void main(String[] args){
    //可以看到,此時就不需要傳入參數,複雜性直接轉移到配置檔案中控制
        Api api = Factory.createApi();
        api.test1("哈哈哈,不要緊張,測試而已!");
    }
}
           

3.思考簡單工廠

本人還處于學習階段,在使用簡單工廠方面還缺少經驗。

根據我的了解,在同一個功能有多重實作的時候,就可以選用簡單工廠,一方面能夠做到接口隔離,另一方面能夠做到選擇實作,集中管理和控制。

舉個例子,同樣的列印功能,可以用愛普生的,或者惠普的。這些列印廠家都實作了列印标準,同一接口。

(1)首先有個同一的接口Print

public interface Print{
	public void print(String s);
}
           

(2)然後再有多個實作EpsonImpl,HpImpl

public class EpsonImpl implements Print{
	public void print(String s){
		System.out.println("this is EpsonPrinter");
	}
}
           
public class HpImpl implements Print{
	public void print(String s){
		System.out.println("this is EpsonPrinter");
	}
}
           

再之後就可以從多個實作中做出選擇,完成列印功能的隔離。

繼續閱讀