Java設計模式-工廠方法模式
一、工廠方法模式概述
工廠方法模式是為了彌補簡單工廠模式的不足並且繼承它的優點而延生出的一種設計模式,屬於GoF中的一種。它能更好的符合開閉原則的要求
屬於建立型設計模式,此模式的核心精神是封裝類中變化的部分,提取其中個性化善變的部分為獨立類,通過依賴注入以達到解耦、複用和方便後期維護拓展的目的。
它的核心結構有四個角色,分別是抽象工廠;具體工廠;抽象產品;具體產品
定義:定義了一個用於建立物件的介面,但是讓子類決定將哪一個類例項化。即讓類的例項化延遲到子類
二、工廠方法模式的結構與實現
1. 結構:
1)Product(抽象產品):定義產品的介面,是工廠方法模式所建立物件的公共父類(獲取計算結果集)
2)ConcreteProduct(具體產品):實現了抽象產品的介面,某型別的具體產品由專門的工廠建立(對應具體的加減乘除操作)
3)Factory(抽象工廠):它聲明瞭工廠方法,用於返回一個產品。工廠方法模式的核心,所有建立物件的工廠必須實現該介面(建立計算器運算方式的工廠)
4)ConcreteFactory(具體工廠):抽象工廠類的子類,實現了抽象工廠中宣告的工廠方法,返回一個具體產品類的例項(對應具體的加減乘除)
2. 實現:
日誌記錄器
程式碼如下:
/** * @author *** * @title: LoggerFactory * @projectName design-pattern * @description 日誌記錄器工廠介面,充當抽象工廠 * @date 2021/7/7 11:15*/ public interface LoggerFactory { /** * 抽象工廠方法 * @return */ Logger CreateLogger(); }
/** * @author *** * @title: FileLoggerFactory * @projectName design-pattern * @description 檔案日誌記錄器工廠類,具體工廠 * @date 2021/7/7 11:18 */ public class FileLoggerFactory implements LoggerFactory { @Overridepublic Logger CreateLogger() { //建立檔案日誌記錄器 return new FileLogger(); } }
/** * @author *** * @title: DatabaseLoggerFactory * @projectName design-pattern * @description TODO * @date 2021/7/7 11:16 */ public class DatabaseLoggerFactory implements LoggerFactory { @Override public Logger CreateLogger() { //連線資料庫 //建立記錄器物件 return new DatabaseLogger(); } }
/** * @author *** * @title: Logger * @projectName design-pattern * @description 日誌記錄器介面 充當抽象產品介面 * @date 2021/7/7 11:10 */ public interface Logger { /** * 抽象產品方法 */ void WriteLog(); }
/** * @author *** * @title: FileLogger * @projectName design-pattern * @description 檔案日誌記錄器 具體產品 * @date 2021/7/7 11:15 */ public class FileLogger implements Logger{ @Override public void WriteLog() { System.out.println("寫入檔案日誌"); } }
/** * @author *** * @title: DatabaseLogger * @projectName design-pattern * @description 資料庫日誌記錄器 具體產品 * @date 2021/7/7 11:11 */ public class DatabaseLogger implements Logger{ @Override public void WriteLog() { System.out.println("寫入資料庫日誌!!!"); } }
/** * @author *** * @title: ClientTest * @projectName design-pattern * @description 客戶端測試 * @date 2021/7/7 11:19 */ public class ClientTest { public static void main(String[] args) { //抽象工廠 LoggerFactory factory; //抽象產品 Logger logger; //new DatabaseLoggerFactory 可以更換為資料庫日誌記錄器 factory = new FileLoggerFactory(); //抽象工廠方法 logger = factory.CreateLogger(); //抽象產品方法 logger.WriteLog(); } //如果要新增新的日誌記錄器,只要增加新的具體工廠類,並在客戶端中修改具體工廠的類名便可以,從而避免了對原類的修改 }
三、工廠方法模式的過載
在某種情況下,可以用不同的方式來初始化一個產品類,在Logger記錄器中,連線資料庫的操作可以不用在每次CreateLog後再傳入。
而是可以將相關引數封裝在一個object中,通過object物件將引數傳入工廠類中,或者在引數列表中給出連線方式來連線資料庫。
為此,可以提供一組過載的工廠方法,以不同的方式建立產品。
修改程式碼如下:
/** * @author *** * @title: LoggerFactory * @projectName design-pattern * @description TODO * @date 2021/7/7 11:39 */ public interface LoggerFactory { Logger CreateLogger();//抽象工廠方法 Logger CreateLogger(String args); Logger CreateLogger(Object obj); }
/** * @author LuQiMing * @title: DatabaseLoggerFactory * @projectName design-pattern * @description TODO * @date 2021/7/7 11:41 */ public class DatabaseLoggerFactory implements LoggerFactory { @Override public Logger CreateLogger() { //連線資料庫 //建立記錄器物件 return new DatabaseLogger(); } @Override public Logger CreateLogger(String args) { //用args連線資料庫 Logger logger = new DatabaseLogger(); ... return logger; } @Override public Logger CreateLogger(Object obj) { //將引數封裝在obj中來連線資料庫 Logger logger = new DatabaseLogger(); ... return logger; } }
四、工廠方法模式的隱藏
在客戶端中,為簡化使用,可以隱藏工廠方法。在工廠類調直接呼叫產品類的方法,在客戶端中無需用工廠方法建立產品物件,直接使用工廠物件即可呼叫所建立的產品物件中的業務方法
修改程式碼如下:
/** * @author *** * @title: LoggerFactory * @projectName design-pattern * @description 改介面為抽象類 * @date 2021/7/7 12:00 */ public abstract class LoggerFactory { /** * 工廠類直接呼叫日誌記錄器的WriteLog() */ public void WriteLog() { Logger logger = this.CreateLogger(); logger.WriteLog(); } public abstract Logger CreateLogger(); }
/** * @author *** * @title: ClientTest * @projectName design-pattern * @description 客戶端修改 * @date 2021/7/7 11:19 */ public class ClientTest { public static void main(String[] args) throws Exception { LoggerFactory factory = null; //直接使用工廠物件呼叫產品物件的業務方法 LoggerFactory loggerFactory = factory.getClass().newInstance(); loggerFactory.WriteLog(); } }
五、將簡單工廠改造為工廠方法
在原有的基礎上新增加了運算類的抽象工廠,其他演算法工廠實現該工廠
/** * @author *** * @title: IFactory * @projectName design-pattern * @description 工廠介面 * @date 2021/7/6 17:00 */ public interface IFactory { Operation createOperation(); }
/** * @author *** * @title: AddFactory * @projectName design-pattern * @description 加法工廠 * @date 2021/7/6 17:01 */ public class AddFactory implements IFactory{ @Override public Operation createOperation() { return new OperationAdd(); } }
/** * @author *** * @title: AddFactory * @projectName design-pattern * @description 減法工廠 * @date 2021/7/6 17:01 */ public class SubFactory implements IFactory{ @Override public Operation createOperation() { return new OperationSub(); } }
/** * @author *** * @title: AddFactory * @projectName design-pattern * @description 乘法工廠 * @date 2021/7/6 17:01 */ public class MulFactory implements IFactory{ @Override public Operation createOperation() { return new OperationMul(); } }
/** * @author *** * @title: AddFactory * @projectName design-pattern * @description 除法工廠 * @date 2021/7/6 17:01 */ public class DivFactory implements IFactory{ @Override public Operation createOperation() { return new OperationDiv(); } }
/** * @author *** * @title: Operation * @projectName design-pattern * @description 抽象運算類 * @date 2021/7/6 16:54 */ public abstract class Operation { public double number1; public double number2; public double getNumber1() { return number1; } public void setNumber1(double number1) { this.number1 = number1; } public double getNumber2() { return number2; } public void setNumber2(double number2) { this.number2 = number2; } public abstract double getResult() throws Exception; }
/** * @author *** * @title: OperationAdd * @projectName design-pattern * @description 加法運算類 * @date 2021/7/6 16:55 */ public class OperationAdd extends Operation { @Override public double getResult() throws Exception { return number1+number2; } }
/** * @author *** * @title: OperationAdd * @projectName design-pattern * @description 減法運算類 * @date 2021/7/6 16:55 */ public class OperationSub extends Operation { @Override public double getResult() throws Exception { return number1-number2; } }
/** * @author *** * @title: OperationAdd * @projectName design-pattern * @description 乘法運算類 * @date 2021/7/6 16:55 */ public class OperationMul extends Operation { @Override public double getResult() throws Exception { return number1 * number2; } }
/** * @author *** * @title: OperationAdd * @projectName design-pattern * @description 除法運算類 * @date 2021/7/6 16:55 */ public class OperationDiv extends Operation { @Override public double getResult() throws Exception { if(0==number2){ throw new Exception("被除數不能為零"); } return number1/number2; } }
/** * @author *** * @title: ClassT * @projectName design-pattern * @description 客戶端測試類 * @date 2021/7/6 17:04 */ public class ClassT { public static void main(String[] args) { IFactory factory = new AddFactory(); Operation oper = factory.createOperation(); oper.number1 = 1; oper.number2 = 2; try { System.out.println(oper.getResult() + ""); } catch (Exception e) { e.printStackTrace(); } IFactory factory1 = new SubFactory(); Operation oper1 = factory1.createOperation(); oper1.number1 = 1; oper1.number2 = 2; try { System.out.println(oper1.getResult() + ""); } catch (Exception e) { e.printStackTrace(); } IFactory factory2 = new MulFactory(); Operation oper2 = factory2.createOperation(); oper2.number1 = 1; oper2.number2 = 2; try { System.out.println(oper2.getResult() + ""); } catch (Exception e) { e.printStackTrace(); } IFactory factory3 = new DivFactory(); Operation oper3 = factory3.createOperation(); oper3.number1 = 1; oper3.number2 = 2; try { System.out.println(oper3.getResult() + ""); } catch (Exception e) { e.printStackTrace(); } } }
六、工廠方法模式的優缺點和適用環境
1. 工廠方法模式的優點
1)使用者只需要關心產品對應的工廠,甚至無需關心建立細節或具體產品類的類名
2)基於工廠角色和產品的多型性設計是工廠模式的關鍵。它能自主決定如何建立哪種產品物件,而建立細節都封裝在具體工廠內部
3)在系統要新增新的產品時,無需修改抽象工廠和抽象產品提供的介面,無需修改客戶端,也無需修改其他的具體工廠和具體產品,只要新增一個具體工廠和具體產品即可,從而提高系統的可擴充套件性(符合開閉原則)
2. 工廠方法模式的缺點
1)在新增新產品時,要編寫新的具體產品類,並要提供與之對應的具體工廠類。系統軟體個數也成對增加,從而增加了系統的複雜度,帶來更多開銷
2)由於系統的可擴充套件性,在客戶端中要引入抽象層進行定義,從而增加了系統的抽象性和理解難度
3. 工廠方法模式的適用環境
1)客戶端不需要知道它需要的物件的類。只需知道對應的工廠即可
2)抽象工廠類通過子類來指定建立那哪個物件。即抽象工廠類只需要提供一個建立產品的介面,而由其子類確定具體要建立的物件,利用多型性和里氏代換原則,子類物件將覆蓋父類物件,從而使系統更容易擴充套件