目錄
一、簡單工廠模式
1.1 使用傳統的方式來完成
1.2 傳統的方式的優缺點
二、簡單工廠
2.1 簡單工廠基本介紹
2.2 使用簡單工廠模式
三、工廠方法模式
3.1 工廠方法模式介紹
3.2 工廠方法模式應用案例
四、抽象工廠模式
4.1 抽象工廠基本介紹
4.2 抽象工廠模式應用執行個體
五、工廠模式在 JDK-Calendar 應用的源碼分析
六、工廠模式小結
一、簡單工廠模式
看一個披薩的項目:要便于披薩種類的擴充,要便于維護
- 披薩的種類很多(比如 GreekPizz、CheesePizz 等)
- 披薩的制作有 prepare,bake, cut, box
- 完成披薩店訂購功能。
1.1 使用傳統的方式來完成
思路分析(類圖)
編寫 OrderPizza.java 去訂購需要的各種 Pizza,傳統方式代碼如下:
public class OrderPizza {
// 構造器
public OrderPizza() {
Pizza pizza = null;
String orderType; // 訂購披薩的類型
do {
orderType = getType();
if (orderType.equals("greek")) {
pizza = new GreekPizza();
pizza.setName(" 希臘披薩 ");
} else if (orderType.equals("cheese")) {
pizza = new CheesePizza();
pizza.setName(" 奶酪披薩 ");
} else if (orderType.equals("pepper")) {
pizza = new PepperPizza();
pizza.setName("胡椒披薩");
} else {
break;
}
//輸出 pizza 制作過程
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} while (true);
}
public String getType() {
return "";
}
}
abstract class Pizza{
public void setName(String name){}
public void prepare(){}
public void bake() {}
public void cut() {}
public void box() {}
}
class GreekPizza extends Pizza{
}
class CheesePizza extends Pizza {
}
class PepperPizza extends Pizza {
}
1.2 傳統的方式的優缺點
- 優點是比較好了解,簡單易操作。
- 缺點是違反了設計模式的 ocp 原則,即對擴充開放,對修改關閉。即當我們給類增加新功能的時候,盡量不修改代碼,或者盡可能少修改代碼.
- 比如我們這時要新增加一個 Pizza 的種類(Pepper 披薩),我們需要做如下修改. 如果我們增加一個 Pizza 類,隻要是訂購 Pizza 的代碼都需要修改.
改進的思路分析
- 分析:修改代碼可以接受,但是如果我們在其它的地方也有建立 Pizza 的代碼,就意味着,也需要修改,而建立 Pizza 的代碼,往往有多處。
- 思路:把建立 Pizza 對象封裝到一個類中,這樣我們有新的 Pizza 種類時,隻需要修改該類就可,其它有建立到 Pizza 對象的代碼就不需要修改了 -> 簡單工廠模式
二、簡單工廠
2.1 簡單工廠基本介紹
- 簡單工廠模式是屬于建立型模式,是工廠模式的一種。簡單工廠模式是由一個工廠對象決定建立出哪一種産品類的執行個體。簡單工廠模式是工廠模式家族中最簡單實用的模式
- 簡單工廠模式:定義了一個建立對象的類,由這個類來封裝執行個體化對象的行為(代碼)
- 在軟體開發中,當我們會用到大量的建立某種、某類或者某批對象時,就會使用到工廠模式
2.2 使用簡單工廠模式
簡單工廠模式的設計方案: 定義一個可以執行個體化 Pizaa 對象的類,封裝建立對象的代碼。
示例代碼:
// 簡單工廠類
public class SimpleFactory {
// 簡單工廠模式 也叫 靜态工廠模式
public static Pizza createPizza(String orderType) {
Pizza pizza = null;
System.out.println("使用簡單工廠模式");
if (orderType.equals("greek")) {
pizza = new GreekPizza();
pizza.setName(" 希臘披薩 ");
} else if (orderType.equals("cheese")) {
pizza = new CheesePizza();
pizza.setName(" 奶酪披薩 ");
} else if (orderType.equals("pepper")) {
pizza = new PepperPizza();
pizza.setName("胡椒披薩");
}
return pizza;
}
}
public class OrderPizza {
//定義一個簡單工廠對象
SimpleFactory simpleFactory;
Pizza pizza = null;
//構造器
public OrderPizza(SimpleFactory simpleFactory) {
setFactory(simpleFactory);
}
public void setFactory(SimpleFactory simpleFactory) {
String orderType = ""; //使用者輸入的
this.simpleFactory = simpleFactory; //設定簡單工廠對象
do {
orderType = getType();
pizza = this.simpleFactory.createPizza(orderType);
//輸出 pizza
if (pizza != null) { //訂購成功
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} else {
System.out.println(" 訂購披薩失敗 ");
break;
}
} while (true);
}
// 寫一個方法,可以擷取客戶希望訂購的披薩種類
private String getType() {
try {
BufferedReader strIn = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza 種類:");
String str = strIn.readLine();
return str;
} catch (IOException e) {
e.printStackTrace(); return "";
}
}
}
三、工廠方法模式
- 看一個新的需求
披薩項目新的需求:客戶在點披薩時,可以點不同口味的披薩,比如 北京的奶酪 pizza、北京的胡椒pizza 或者是倫敦的奶酪 pizza、倫敦的胡椒 pizza。
- 思路 1
使用簡單工廠模式,建立不同的簡單工廠類,比如 BJPizzaSimpleFactory、LDPizzaSimpleFactory 等等.從目前這個案例來說,也是可以的,但是考慮到項目的規模,以及軟體的可維護性、可擴充性并不是特别好
- 思路 2
使用工廠方法模式
3.1 工廠方法模式介紹
- 工廠方法模式設計方案:将披薩項目的執行個體化功能抽象成抽象方法,在不同的口味點餐子類中具體實作。
- 工廠方法模式:定義了一個建立對象的抽象方法,由子類決定要執行個體化的類。工廠方法模式将對象的執行個體化推遲到子類。
3.2 工廠方法模式應用案例
披薩項目新的需求:客戶在點披薩時,可以點不同口味的披薩,比如 北京的奶酪 pizza、北京的胡椒pizza 或者是倫敦的奶酪 pizza、倫敦的胡椒 pizza
思路分析圖解
代碼實作
public class BJCheesePizza extends Pizza {
}
public class BJPepperPizza extends Pizza {
}
public class LDCheesePizza extends Pizza {
}
public class LDPepperPizza extends Pizza {
}
public abstract class AbstractOrderPizza {
//定義一個抽象方法,createPizza , 讓各個工廠子類自己實作
abstract Pizza createPizza(String orderType);
// 構造器
public AbstractOrderPizza() { Pizza pizza = null;
String orderType; // 訂購披薩的類型
do {
orderType = getType();
pizza = createPizza(orderType); //抽象方法,由工廠子類完成
//輸出 pizza 制作過程
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} while (true);
}
// 寫一個方法,可以擷取客戶希望訂購的披薩種類
private String getType() {
try {
BufferedReader strIn = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza 種類:");
String str = strIn.readLine();
return str;
} catch (IOException e) {
e.printStackTrace(); return "";
}
}
}
public class BJOrderPizza extends AbstractOrderPizza {
@Override
Pizza createPizza(String orderType) {
Pizza pizza = null;
if(orderType.equals("cheese")) {
pizza = new BJCheesePizza();
} else if (orderType.equals("pepper")) {
pizza = new BJPepperPizza();
}
return pizza;
}
}
public class LDOrderPizza extends AbstractOrderPizza {
@Override
Pizza createPizza(String orderType) {
Pizza pizza = null; if(orderType.equals("cheese")) {
pizza = new LDCheesePizza();
} else if (orderType.equals("pepper")) {
pizza = new LDPepperPizza();
}
return pizza;
}
}
四、抽象工廠模式
4.1 抽象工廠基本介紹
- 抽象工廠模式:定義了一個 interface 用于建立相關或有依賴關系的對象簇,而無需指明具體的類
- 抽象工廠模式可以将簡單工廠模式和工廠方法模式進行整合。
- 從設計層面看,抽象工廠模式就是對簡單工廠模式的改進(或者稱為進一步的抽象)。
- 将工廠抽象成兩層,AbsFactory(抽象工廠) 和 具體實作的工廠子類。程式員可以根據建立對象類型使用對應的工廠子類。這樣将單個的簡單工廠類變成了工廠簇,更利于代碼的維護和擴充。
類圖
4.2 抽象工廠模式應用執行個體
使用抽象工廠模式來完成披薩項目.
//一個抽象工廠模式的抽象層(接口)
public interface AbsFactory {
//讓下面的工廠子類來 具體實作
public Pizza createPizza(String orderType);
}
//這是工廠子類
public class BJFactory implements AbsFactory {
@Override
public Pizza createPizza(String orderType) {
System.out.println("~使用的是抽象工廠模式~");
Pizza pizza = null;
if(orderType.equals("cheese")) {
pizza = new BJCheesePizza();
} else if (orderType.equals("pepper")){
pizza = new BJPepperPizza();
}
return pizza;
}
}
public class LDFactory implements AbsFactory {
@Override
public Pizza createPizza(String orderType) {
System.out.println("~使用的是抽象工廠模式~");
Pizza pizza = null;
if (orderType.equals("cheese")) {
pizza = new LDCheesePizza();
} else if (orderType.equals("pepper")) {
pizza = new LDPepperPizza();
}
return pizza;
}
}
public class OrderPizza {
AbsFactory factory;
// 構造器
public OrderPizza(AbsFactory factory) {
setFactory(factory);
}
private void setFactory(AbsFactory factory) {
Pizza pizza = null;
String orderType = ""; // 使用者輸入
this.factory = factory; do {
orderType = getType();
// factory 可能是北京的工廠子類,也可能是倫敦的工廠子類
pizza = factory.createPizza(orderType);
if (pizza != null) { // 訂 購 ok
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} else {
System.out.println("訂購失敗");
break;
}
} while (true);
}
// 寫一個方法,可以擷取客戶希望訂購的披薩種類
private String getType() {
try {
BufferedReader strIn = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza 種類:");
String str = strIn.readLine();
return str;
} catch (IOException e) {
e.printStackTrace(); return "";
}
}
}
五、工廠模式在 JDK-Calendar 應用的源碼分析
- JDK 中的 Calendar 類中,就使用了簡單工廠模式
- 源碼分析+Debug 源碼+說明
import java.util.Calendar;
public class Factory {
public static void main(String[] args) {
// getInstance 是 Calendar 靜态方法
Calendar cal = Calendar.getInstance();
// 注意月份下标從 0 開始,是以取月份要+1
System.out.println("年:" + cal.get(Calendar.YEAR));
System.out.println(" 月 :" + (cal.get(Calendar.MONTH) + 1));
System.out.println("日:" + cal.get(Calendar.DAY_OF_MONTH));
System.out.println("時:" + cal.get(Calendar.HOUR_OF_DAY));
System.out.println("分:" + cal.get(Calendar.MINUTE));
System.out.println("秒:" + cal.get(Calendar.SECOND));
}
}
在Calendar.java 中:
public static Calendar getInstance()
{
return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
}
private static Calendar createCalendar(TimeZone zone,
Locale aLocale)
{
CalendarProvider provider =
LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
.getCalendarProvider();
if (provider != null) {
try {
return provider.getInstance(zone, aLocale);
} catch (IllegalArgumentException iae) {
// fall back to the default instantiation
}
}
Calendar cal = null;
if (aLocale.hasExtensions()) {
String caltype = aLocale.getUnicodeLocaleType("ca");
if (caltype != null) {
switch (caltype) {
case "buddhist":
cal = new BuddhistCalendar(zone, aLocale);
break;
case "japanese":
cal = new JapaneseImperialCalendar(zone, aLocale);
break;
case "gregory":
cal = new GregorianCalendar(zone, aLocale);
break;
}
}
}
if (cal == null) {
// If no known calendar type is explicitly specified,
// perform the traditional way to create a Calendar:
// create a BuddhistCalendar for th_TH locale,
// a JapaneseImperialCalendar for ja_JP_JP locale, or
// a GregorianCalendar for any other locales.
// NOTE: The language, country and variant strings are interned.
if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
cal = new BuddhistCalendar(zone, aLocale);
} else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
&& aLocale.getCountry() == "JP") {
cal = new JapaneseImperialCalendar(zone, aLocale);
} else {
cal = new GregorianCalendar(zone, aLocale);
}
}
return cal;
}
六、工廠模式小結
- 工廠模式的意義: 将執行個體化對象的代碼提取出來,放到一個類中統一管理和維護,達到和主項目的依賴關系的解耦。進而提高項目的擴充和維護性。
- 三種工廠模式 (簡單工廠模式、工廠方法模式、抽象工廠模式)
- 設計模式的依賴抽象原則
使用建議:
- 建立對象執行個體時,不要直接 new 類, 而是把這個 new 類的動作放在一個工廠的方法中,并傳回。有的書上說, 變量不要直接持有具體類的引用。
- 不要讓類繼承具體類,而是繼承抽象類或者是實作 interface(接口)
- 不要覆寫基類中已經實作的方法。
文章最後,給大家推薦一些受歡迎的技術部落格連結:
- JAVA相關的深度技術部落格連結
- Flink 相關技術部落格連結
- Spark 核心技術連結
- 設計模式 —— 深度技術部落格連結
- 機器學習 —— 深度技術部落格連結
- Hadoop相關技術部落格連結
- 超全幹貨--Flink思維導圖,花了3周左右編寫、校對
- 深入JAVA 的JVM核心原了解決線上各種故障【附案例】
- 請談談你對volatile的了解?--最近小李子與面試官的一場“硬核較量”
- 聊聊RPC通信,經常被問到的一道面試題。源碼+筆記,包懂
- 深入聊聊Java 垃圾回收機制【附原理圖及調優方法】
歡迎掃描下方的二維碼或 搜尋 公衆号“大資料進階架構師”,我們會有更多、且及時的資料推送給您,歡迎多多交流!