2. 工廠方法模式
(1)概念
工廠方法模式的定義是:定義一個用於創建對象的接口,讓子類決定實現哪一個類。
即工廠父類負責定義創建產品對象的公共接口,工廠子類負責生成具體的產品對象。
將產品類的實例化操作延遲到工廠子類中完成,即通過工廠子類來確定究竟應該實例化哪一個具體產品類。
工廠方法模式是簡單工廠模式的延伸與改進,既繼承了其封裝性等優點,又彌補了其缺陷(提高擴展性),使其符合原則的要求。
在工廠方法模式中,每一個具體工廠只能生產一種具體產品。具體工廠與具體產品一一對應。
(2)類圖、典型代碼
Factory(抽象類或接口):抽象工廠類,聲明了工廠方法,用於返回一個產品。抽象工廠是工廠方法模式的核心,它與應用程序無關。
ConcreteFactory(實現類):具體工廠類,實現抽象工廠類中聲明的方法,可由客戶端調用,返回一個具體產品類的實例。
Product(抽象類或接口):抽象產品類,定義了產品的接口,是產品對象的共同父類或接口。
ConcreteProduct(實現類):具體產品類,實現了抽象產品類的方法,其具體產品由對應的具體工廠創建。
典型代碼如下:
//抽象工廠類 public interface Factory { public Product factoryMethod(); }
//具體工廠類之一 public class ConcreteFactory implements Factory { @Override public Product factoryMethod() { return new ConcreteFactory(); } }
(3)舉例
原有一個工廠生產一種電視機,現分為兩個子工廠:海爾工廠生產海爾電視機,海信工廠生產海信電視機。
根據工廠方法模式設計類圖如下:
實現代碼如下:
//抽象產品類,定義所有產品必須實現的方法 public interface TV { public void play(); }
//具體產品類1--海爾電視 public class HaierTV implements TV { @Override public void play() { // 海爾電視的功能 System.out.println("海爾電視播放中..."); } }
//具體產品類2--海信電視 public class HisenseTV implements TV { @Override public void play() { // 海信電視的功能 System.out.println("海信電視播放中..."); } }
//抽象工廠類,定義所有工廠必須實現的方法 public interface TVFactory { public TV produceTV(); }
//具體產品類1--海爾電視 public class HaierTV implements TV { @Override public void play() { // 海爾電視的功能 System.out.println("海爾電視播放中..."); } }
//具體產品類2--海信電視 public class HisenseTV implements TV { @Override public void play() { // 海信電視的功能 System.out.println("海信電視播放中..."); } }
//客戶端調用類--調用具體電視工廠生產對應電視 public class Client { public static void main(String[] args) { TVFactory factory; TV tv; // 產生海爾電視並調用其功能 factory = new HaierTVFactory(); tv = factory.produceTV(); tv.play(); // 產生海信電視並調用其功能 factory = new HisenseTVFactory(); tv = factory.produceTV(); tv.play(); } }
輸出結果如下:
海爾電視播放中...
海信電視播放中...
如果此時還需要增加一種電視機品牌TCL,則只需要新建一個實現TV接口的具體產品類和一個實現TVFactory接口的具體工廠類即可,不需要修改其他代碼:
//新增具體產品類3--TCL電視 public class TCLTV implements TV { @Override public void play() { // TCL電視的功能 System.out.println("TCL電視播放中..."); } }
//新增具體工廠類3--專門生產TCL電視的TCL工廠 public class TCLTVFactory implements TVFactory { @Override public TV produceTV() { // 生產一個TCL電視的對象 return new TCLTV(); } }
在客戶端調用類中增加調用TCL工廠生產TCL電視的語句:
// 產生TCL電視並調用其功能 factory = new TCLTVFactory(); tv = factory.produceTV(); tv.play();
輸出結果如下:
海爾電視播放中...
海信電視播放中...
TCL電視播放中...
(4)優缺點、適用場景
工廠方法模式的優點:
用戶只需關心所需產品對應的工廠,無須關心創建細節(屏蔽產品類),甚至不需要知道具體產品的類名;
典型的解耦框架,高層模塊需要知道產品的抽象類,其他的實現類都不用關心;
良好的封裝性,代碼結構清晰,優秀的擴展性,同時符合開閉原則。
工廠方法模式的缺點:
在添加新產品時成對增加了類的個數,增加了系統的復雜度,編譯和運行更多的類也會增加系統的開銷;
考慮到可擴展性引入抽象層,在客戶端代碼中均使用抽象層進行定義,增加了系統的抽象性和理解難度。
工廠方法模式的適用場景:
需要靈活、可擴展的框架時;
當一個類(比如客戶端類)不知道所需要的對象的類時(需要知道其對應的工廠);
一個類通過其子類來確定創建那個對象。
3. 抽象工廠模式
(1)概念
抽象工廠模式的定義是:為創建一組相關或相互依賴的對象提供一個接口,而且無需指定他們的具體類。
抽象工廠模式是工廠方法模式的泛化版,即工廠方法模式只是抽象工廠模式的一種特殊情況。
在抽象工廠模式中,每一個具體工廠可以生產多個具體產品。
(2)類圖、典型代碼
AbstractFactory(抽象類或接口):抽象工廠類,用於聲明生產產品的方法。
ConcreteFactory(實現類):具體工廠類,具體實現生產產品的方法,返回一個具體產品。
AbstractProduct(抽象類或接口):抽象產品類,定義了每種產品的功能方法。
ConcreteProduct(實現類):具體產品類,具體實現了抽象產品類定義的功能方法。
典型代碼如下:
// 抽象工廠類 public interface AbstractFactory { public AbstractProductA createProductA(); public AbstractProductB createProductB(); }
// 具體工廠類之一 public class ConcreteFactory implements AbstractFactory{ public AbstractProductA createProductA(){ return new ConcreteProductA(); } public AbstractProductB createProductB(){ return new ConcreteProductB(); } }
(3)舉例
一個電器工廠可以生產多種電器,比如海爾工廠可以生產海爾電視和海爾空調,TCL工廠可以生產TCL電視和TCL空調。
根據抽象工廠模式設計類圖如下:
實現代碼如下:
//抽象產品類1--電視類 public interface TV { public void play(); }
//電視具體產品類1--海爾電視 public class HaierTV implements TV { @Override public void play() { System.out.println("海爾電視播放中..."); } }
//電視具體產品類2--TCL電視 public class TCLTV implements TV { @Override public void play() { System.out.println("TCL電視播放中..."); } }
//抽象產品類2--空調類 public interface AirConditioner { public void changeTemperature(); }
//空調具體產品類1--海爾空調 public class HaierAirConditioner implements AirConditioner { @Override public void changeTemperature() { System.out.println("海爾空調吹風中..."); } }
//空調具體產品類2--TCL空調 public class TCLAirConditioner implements AirConditioner { @Override public void changeTemperature() { System.out.println("TCL空調吹風中..."); } }
//抽象工廠類,定義所有工廠必須實現的方法 public interface Factory { public TV produceTV(); public AirConditioner produceAirConditioner(); }
//具體工廠類1--海爾工廠 public class HaierFactory implements Factory{ @Override public TV produceTV() { return new HaierTV(); } @Override public AirConditioner produceAirConditioner() { return new HaierAirConditioner(); } }
//具體工廠類2--TCL工廠 public class TCLFactory implements Factory{ @Override public TV produceTV() { return new TCLTV(); } @Override public AirConditioner produceAirConditioner() { return new TCLAirConditioner(); } }
運行結果如下:
海爾電視播放中...
海爾空調吹風中...
———————————————
TCL電視播放中...
TCL空調吹風中...
(4)優缺點、適用場景
抽象工廠模式的優點:
隔離了具體類的生成,使客戶並不知道什么被創建;
產品內的約束為非公開狀態(比如不同產品的生產比例,這對調用工廠類的高層模塊是透明的);
抽象工廠模式的缺點:
在添加新的產品對象時,難以擴展抽象工廠來生產新種類的產品(對接口進行擴展會導致所有子類的修改);
抽象工廠模式的適用場景:
一個系統不依賴產品類實例如何被創建、組合和表達的細節時;
系統中有多個產品族,而每次只使用其中某一產品族時;
屬於同一產品族的產品將一起使用;
多個對象有相同的約束時。
在實際的應用開發中,一般將具體類的類名寫入配置文件中,再通過Java的反射機制讀取XML格式的配置文件,根據存儲在XML文件中的類名字符串生成對象。
新建XML文件config.xml如下:
<?xml version="1.0" encoding="UTF-8"?> <config> <className>xxxFactory</className> </config>
新建工具類文件XMLUtil.java如下:
package com.test.util; import java.io.File; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; public class XMLUtil { // 該方法用於從XML配置文件中提取類名字符串,並返回一個實例對象 public static Object getBean() { try { // 創建DOM文檔對象 DocumentBuilderFactory dFactory = DocumentBuilderFactory .newInstance(); DocumentBuilder builder = dFactory.newDocumentBuilder(); Document doc = builder.parse(new File( "./src/com/test/util/config.xml"));// 若不在同一路徑下,必須指定文件具體路徑,用正斜杠/或雙反斜杠\\ // 獲取包含類名的文本節點 NodeList nlist = doc.getElementsByTagName("className"); Node classNode = nlist.item(0).getFirstChild(); String cName = classNode.getNodeValue(); // 通過類名生成實例對象並將其返回 Class c = Class.forName("com.test.factory_method." + cName);// 若不在同一路徑下,必須寫出類的全名 Object obj = c.newInstance(); return obj; } catch (Exception e) { e.printStackTrace(); } return null; } }
改進工廠方法模式中的客戶端代碼如下:
//客戶端調用類--調用具體電視工廠生產對應電視 public class Client { public static void main(String[] args) { TVFactory factory; TV tv; // // 產生海爾電視並調用其功能 // factory = new HaierTVFactory(); // tv = factory.produceTV(); // tv.play(); // // // 產生海信電視並調用其功能 // factory = new HisenseTVFactory(); // tv = factory.produceTV(); // tv.play(); // // // 產生TCL電視並調用其功能 // factory = new TCLTVFactory(); // tv = factory.produceTV(); // tv.play(); // 將具體類名寫入配置文件中,再通過Java反射機制讀取XML格式文件,根據類名生成對象並返回 factory = (TVFactory) XMLUtil.getBean(); tv = factory.produceTV(); tv.play(); } }
將config.xml文件中的“xxxFactory”更改為需要生成對象的類名“HaierTVFactory”,運行客戶端結果如下:
海爾電視播放中...
抽象工廠模式中例子的客戶端改進與此相同。
6大設計原則,與常見設計模式(概述):http://www.cnblogs.com/LangZXG/p/6204142.html
類圖基礎知識:http://www.cnblogs.com/LangZXG/p/6208716.html
注:轉載請注明出處 http://www.cnblogs.com/LangZXG/p/6249425.html