java設計模式之工廠方法模式
目錄
1.0 ●工廠方法模式概述
✓不再提供一個按鈕工廠類來統一負責所有產品的建立,而是將具體按鈕的建立過程交給專門的工廠子類去完成。
✓如果出現新的按鈕型別,只需要為這種新型別的按鈕定義一個具體的工廠類就可以建立該新按鈕的例項。
1.1 ●工廠方法模式的定義
工廠方法模式:定義一個用於建立物件的介面,但是讓子類決定將哪一個類例項化。工廠方法模式讓一個類的例項化延遲到其子類。
✓簡稱為工廠模式(Factory Pattern)。
✓又可稱作虛擬構造器模式(Virtual Constructor Pattern)或多型工廠模式(Polymorphic Factory Pattern)。
✓工廠父類負責定義建立產品物件的公共介面,而工廠子類則負責生成具體的產品物件。
✓目的是將產品類的例項化操作延遲到工廠子類中完成,即通過工廠子類來確定究竟應該例項化哪一個具體產品類。
1.2 ●工廠方法模式的結構
工廠方法模式包含以下4個角色:
•Product(抽象產品)
•ConcreteProduct(具體產品)
•Factory(抽象工廠)
•ConcreteFactory(具體工廠)
1.3 ●例項說明
某系統執行日誌記錄器(Logger)可以通過多種途徑儲存系統的執行日誌,例如通過檔案記錄或資料庫記錄,使用者可以通過修改配置檔案靈活地更換日誌記錄方式。在設計各類日誌記錄器時,開發人員發現需要對日誌記錄器進行一些初始化工作,初始化引數的設定過程較為複雜,而且某些引數的設定有嚴格的先後次序,否則可能會發生記錄失敗。
為了更好地封裝記錄器的初始化過程並保證多種記錄器切換的靈活性,現使用工廠方法模式設計該系統。
1.3.1 ●例項程式碼分析
✓(1) Logger:日誌記錄器介面,充當抽象產品角色
✓(2) DatabaseLogger:資料庫日誌記錄器,充當具體產品角色
✓(3) FileLogger:檔案日誌記錄器,充當具體產品角色
✓(4) LoggerFactory:日誌記錄器工廠介面,充當抽象工廠角色
✓(5) DatabaseLoggerFactory:資料庫日誌記錄器工廠類,充當具體工廠角色
✓(6) FileLoggerFactory
package designpatterns.factoryMethod;
/**
* @Author: Frank
* @Description:日誌記錄器介面 充當抽象產品角色
* @Date: Create in 2018/10/9 9:39 PM
*/
public interface Logger {
public void writeLog();
}
package designpatterns.factoryMethod;
/**
* @Author: Frank
* @Description: 檔案日誌記錄器 充當具體的產品角色
* @Date: Create in 2018/10/9 9:44 PM
*/
public class FileLogger implements Logger {
@Override
public void writeLog() {
System.out.println("檔案日誌記錄器");
}
}
package designpatterns.factoryMethod;
/**
* @Author: Frank
* @Description: 資料庫日誌記錄器 充當具體的產品角色
* @Date: Create in 2018/10/9 9:44 PM
*/
public class DataBaseLogger implements Logger{
@Override
public void writeLog() {
System.out.println("資料庫日誌記錄器");
}
}
package designpatterns.factoryMethod;
/**
* @Author: Frank
* @Description: 日誌記錄器工廠介面 充當抽象工廠角色
* @Date: Create in 2018/10/9 9:46 PM
*/
public interface LoggerFactory {
public Logger createLogger();
}
package designpatterns.factoryMethod;
/**
* @Author: Frank
* @Description: 檔案日誌記錄器工廠類
* @Date: Create in 2018/10/9 9:48 PM
*/
public class FileLoggerFactory implements LoggerFactory{
@Override
public Logger createLogger() {
Logger logger=new FileLogger();
return logger;
}
}
package designpatterns.factoryMethod;
/**
* @Author: Frank
* @Description: 資料庫日誌記錄器工廠類 充當具體工廠角色
* @Date: Create in 2018/10/9 9:52 PM
*/
public class DataBaseLoggerFactory implements LoggerFactory {
@Override
public Logger createLogger() {
Logger logger = new DataBaseLogger();
return logger;
}
}
<?xml version="1.0"?>
<config>
<!--<className>designpatterns.factoryMethod.demo.FileLoggerFactory2</className>-->
<className>designpatterns.factoryMethod.FileLoggerFactory</className>
</config>
//designpatterns.factorymethod.XMLUtil.java
package designpatterns.factoryMethod;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
public class XMLUtil {
//該方法用於從XML配置檔案中提取具體類類名,並返回一個例項物件
public static Object getBean() {
try {
//建立DOM文件物件
DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dFactory.newDocumentBuilder();
Document doc;
doc = builder.parse(new File("src//designpatterns//factorymethod//config.xml"));
//獲取包含類名的文字結點
NodeList nl = doc.getElementsByTagName("className");
Node classNode=nl.item(0).getFirstChild();
String cName=classNode.getNodeValue();
//通過類名生成例項物件並將其返回
Class c= Class.forName(cName);
Object obj=c.newInstance();
return obj;
}
catch(Exception e) {
e.printStackTrace();
return null;
}
}
}
package designpatterns.factoryMethod;
/**
* @Author: Frank
* @Description:
* @Date: Create in 2018/10/9 9:56 PM
*/
public class Client {
public static void main(String[] args) {
LoggerFactory loggerFactory;
Logger logger;
loggerFactory= (LoggerFactory) XMLUtil.getBean();
logger=loggerFactory.createLogger();
logger.writeLog();
}
}
編譯執行結果如下;
如果需要更換日誌記錄器,只需要修改客戶端程式碼中的具體工廠類的類名即可,例如將FileLoggerFactory改為DataBaseLoggerFactory,此時輸出結果如下;
如果需要增加並使用新的日誌記錄器,只需要對應增加一個新的具體工廠類,然後修改客戶端,原有類庫的原始碼無需做任何修改。
1.3.2 ●結果及分析
在未使用配置檔案和反射機制之前,更換具體工廠類需修改客戶端原始碼,但無須修改類庫程式碼。
1.4 ●工廠方法的過載
在某些情況下,可以通過多種方式來初始化同一個產品類,例如上面提到的日誌記錄器,可以為各種日誌記錄器提供預設實現;還可以為資料庫日誌記錄器提供資料庫連結字串,為日誌記錄器提供檔案路徑。
抽象工廠類LoggerFactory示意程式碼:
public interface LoggerFactory {
public Logger createLogger();
public Logger createLogger(String args);
public Logger createLogger(Object obj);
}
public class DatabaseLoggerFactory implements LoggerFactory {
public Logger createLogger() {
//使用預設方式連線資料庫,程式碼省略
Logger logger = new DatabaseLogger();
//初始化資料庫日誌記錄器,程式碼省略
return logger;
}
public Logger createLogger(String args) {
//使用引數args作為連線字串來連線資料庫,程式碼省略
Logger logger = new DatabaseLogger();
//初始化資料庫日誌記錄器,程式碼省略
return logger;
}
public Logger createLogger(Object obj) {
//使用封裝在引數obj中的連線字串來連線資料庫,程式碼省略
Logger logger = new DatabaseLogger();
//使用封裝在引數obj中的資料來初始化資料庫日誌記錄器,程式碼省略
return logger;
}
}
//其他具體工廠類程式碼省略
1.5 ●工廠方法的隱藏
✓目的:為了進一步簡化客戶端的使用。
✓實現:在工廠類中直接呼叫產品類的業務方法,客戶端無須呼叫工廠方法建立產品物件,直接使用工廠物件即可呼叫所建立的產品物件中的業務方法。
抽象工廠類LoggerFactory示意程式碼:
//將介面改為抽象類
public abstract class LoggerFactory {
//在工廠類中直接呼叫日誌記錄器類的業務方法writeLog()
public void writeLog() {
Logger logger = this.createLogger();
logger.writeLog();
}
public abstract Logger createLogger();
}
客戶端程式碼:
public class Client {
public static void main(String args[]) {
LoggerFactory factory;
factory = (LoggerFactory)XMLUtil.getBean();
factory.writeLog(); //直接使用工廠物件來呼叫產品物件的業務方法
}
}
1.6 ●模式優點
✓工廠方法用來建立客戶所需要的產品,同時還向客戶隱藏了哪種具體產品類將被例項化這一細節。
✓能夠讓工廠自主確定建立何種產品物件,而如何建立這個物件的細節則完全封裝在具體工廠內部。
✓在系統中加入新產品時,完全符合開閉原則。
1.7 ●模式缺點
✓系統中類的個數將成對增加,在一定程度上增加了系統的複雜度,會給系統帶來一些額外的開銷。
✓增加了系統的抽象性和理解難度。
1.8 ●模式適用環境
✓客戶端不知道它所需要的物件的類(客戶端不需要知道具體產品類的類名,只需要知道所對應的工廠即可,具體產品物件由具體工廠類建立)。
✓抽象工廠類通過其子類來指定建立哪個物件。
相關程式碼;
原創公眾號
關注java 設計模式,JVM特性,
併發程式設計、分散式、微服務,
inux高可用叢集,等相關技術。
掃一掃關注我吧!