I walk very slowly, but I never walk backwards
工廠模式 - 抽象工廠模式
? 寂然
大家好~,我是寂然,本節(jié)課呢,我們接著來聊工廠模式的第三種,抽象工廠模式,以及工廠模式在 JDK源碼中的應(yīng)用,最后,我們明確幾點(diǎn)工廠模式的注意事項(xiàng),對工廠模式進(jìn)行總結(jié),那我們啟程吧
基本介紹 - 抽象工廠模式
抽象工廠模式:定義了一個 interface 用于創(chuàng)建相關(guān)或有依賴關(guān)系的對象簇,而無需指明具體的類
對象簇即一系列對象
抽象工廠模式可以將簡單工廠模式和工廠方法模式進(jìn)行整合
案例回顧
怎么理解上面的幾句話呢,首先,我們來回顧一下上節(jié)課的需求,然后我們通過類圖來詳細(xì)說明
有這樣一個披薩店的需求,披薩的種類很多(比如 GreekPizz、CheesePizz 等)
披薩的制作有 prepare,bake,cut,box 等,而且客戶在點(diǎn)披薩時,可以在不同城市點(diǎn)披薩, 比如北京點(diǎn)了芝士 pizza、水果 pizza 或者在上海點(diǎn)了芝士 pizza、水果 pizza等
要求:完成披薩店訂購功能,便于披薩種類的擴(kuò)展,便于維護(hù)
類圖演示
OK,需求明確了,我們根據(jù)需求,繪制類圖,分析抽象工廠模式的實(shí)現(xiàn)思路,類圖如下圖所示
為什么說抽象工廠模式可以將簡單工廠模式和工廠方法模式進(jìn)行整合,首先仍然是簡單工廠的思想,由工廠類封裝實(shí)例化對象的行為,其次,createPizza()方法下沉到各個子類,又體現(xiàn)出工廠方法模式的特點(diǎn)
從設(shè)計(jì)層面看,抽象工廠模式就是對簡單工廠模式的改進(jìn),或者稱為進(jìn)一步的抽象,將工廠抽象成兩層,一個AbsFactory(抽象工廠)和 具體實(shí)現(xiàn)的工廠子類,程序員可以根據(jù)創(chuàng)建對象類型使用對應(yīng)的工廠子類,這樣將單個的簡單工廠類變成了工廠簇,更利于代碼的維護(hù)和擴(kuò)展
解決方案四:抽象工廠模式
根據(jù)上面類圖的思路,我們對披薩訂購項(xiàng)目再次進(jìn)行重構(gòu),使用抽象工廠模式來完成功能,示例代碼如下圖所示
//披薩抽象類
public abstract class Pizza {
//定義一個屬性,披薩的名稱,并給定set方法
protected String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
//認(rèn)為不同的披薩,準(zhǔn)備的原材料不同,所以定義成抽象方法
public abstract void prepare();
//烘焙方法
public void bake() {
System.out.println(name + "正在烘焙中");
}
//切割方法
public void cut() {
System.out.println(name + "正準(zhǔn)備把披薩大卸八塊");
}
//打包方法
public void box() {
System.out.println(name + "將披薩打包給顧客");
}
}
//北京的水果披薩
public class BJFruitPizza extends Pizza {
@Override
public void prepare() {
setName("北京的水果披薩");
System.out.println("北京的水果披薩正在準(zhǔn)備原材料");
}
}
//北京的希臘披薩
public class BJGreekPizza extends Pizza {
@Override
public void prepare() {
setName("北京的希臘披薩");
System.out.println("北京的希臘披薩正在準(zhǔn)備原材料");
}
}
//上海的水果披薩
public class SHFruitPizza extends Pizza {
@Override
public void prepare() {
setName("上海的水果披薩");
System.out.println("上海的水果披薩正在準(zhǔn)備原材料");
}
}
//上海的希臘披薩
public class SHGreekPizza extends Pizza {
@Override
public void prepare() {
setName("上海的希臘披薩");
System.out.println("上海的希臘披薩正在準(zhǔn)備原材料");
}
}
//抽象工廠模式- 抽象接口
public interface AbsFactory {
//讓下面的工廠子類來具體實(shí)現(xiàn)
public Pizza createPizza(String orderType);
}
//北京工廠子類
public class BJFactory implements AbsFactory{
@Override
public Pizza createPizza(String orderType) {
Pizza pizza = null;
if (orderType.equals("GreekPizza")){
pizza = new BJGreekPizza();
}else if (orderType.equals("FruitPizza")){
pizza = new BJFruitPizza();
}
return pizza;
}
}
//上海工廠子類
public class SHFactory implements AbsFactory{
@Override
public Pizza createPizza(String orderType) {
Pizza pizza = null;
if (orderType.equals("GreekPizza")){
pizza = new SHGreekPizza();
}else if (orderType.equals("FruitPizza")){
pizza = new SHFruitPizza();
}
return pizza;
}
}
//訂購Pizza類
public class OrderPizza {
//聚合抽象工廠接口
AbsFactory absFactory;
public OrderPizza(AbsFactory absFactory){
setAbsFactory(absFactory);
}
public void setAbsFactory(AbsFactory absFactory) {
Pizza pizza = null;
String orderType = ""; //用戶輸入
this.absFactory = absFactory;
while (true){
orderType = getType();
pizza = absFactory.createPizza(orderType);
if (pizza != null){ //訂購成功,輸出制作過程
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} else{
System.out.println("訂購失敗,咱這沒有,去別處把");
break;
}
}
}
//定義方法獲取客戶希望訂購的披薩種類
private String getType(){
System.out.println("你想訂購那個種類的Pizza呢?");
Scanner scanner = new Scanner(System.in);
String str = scanner.next();
return str;
}
}
//客戶端演示
public class PizzaStore {
public static void main(String[] args) {
System.out.println("請輸入要訂購披薩的地點(diǎn)");
String address = new Scanner(System.in).next();
if (address.equals("北京")){
new OrderPizza(new BJFactory());
}else if (address.equals("上海")){
new OrderPizza(new SHFactory());
}else{
System.out.println("該地點(diǎn)暫未開通訂購披薩功能");
}
}
}
案例解讀
上面我們使用抽象工廠模式進(jìn)行了代碼重構(gòu),其實(shí)抽象工廠模式代碼層面并不難,重點(diǎn)在于分析過程和類圖的關(guān)系要清晰,其實(shí)這兩種方式差別是有的,但是不大,核心思想大同小異,抽象工廠模式只不過把工廠抽象成了兩層,而簡單工廠模式就是一個工廠類,但是大多數(shù)情況下,一個工廠類很難滿足復(fù)雜業(yè)務(wù)邏輯下多種不同類型的實(shí)例的創(chuàng)建,這個時候,使用抽象工廠模式搭建,會更顯得得心應(yīng)手,所以,雖然工廠模式有三種,但是大家要根據(jù)實(shí)際情況靈活運(yùn)用,切忌不要為了用而用
JDK源碼解析
Calendar 日歷類中,就使用到了簡單工廠模式,下面我們寫一段建議代碼,進(jìn)入 getInstance() 進(jìn)行查看
//JDK源碼解析 - 工廠模式
public class Test {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
System.out.println((calendar.get(Calendar.MONTH) + 1) + "月");//月份下標(biāo)從0開始
System.out.println(calendar.get(Calendar.DAY_OF_MONTH) + "日");
}
}
可以看到,源碼中首先定義 Calendar cal = null; 然后通過switch case語句,根據(jù)不同的種類,來創(chuàng)建不同的實(shí)例賦給 cal ,然后返回,和我們簡單工廠模式中,根據(jù)客戶輸入類型的不同,訂購不同的披薩實(shí)例是非常類似的,如圖所示,所以JDK中 Calendar 類給我們返回對象實(shí)例就是經(jīng)典的簡單工廠模式
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;
}
}
}
三種工廠模式對比
那到這里,三種工廠模式已經(jīng)全部結(jié)束了,那下面,我把三種模式對比著進(jìn)行總結(jié)與回顧,方便大家融會貫通
簡單工廠模式
定義了一個創(chuàng)建對象的類,由這個類來封裝實(shí)例化對象的行為,可以根據(jù)參數(shù)的不同返回不同類的實(shí)例
簡單工廠模式最大的優(yōu)點(diǎn)在于工廠類中包含了必要的判斷邏輯,根據(jù)客戶端的條件動態(tài)的實(shí)例化相關(guān)的類,對于客戶端來說去除了對具體產(chǎn)品的依賴
工廠方法模式
定義一個創(chuàng)建對象的抽象方法,由子類決定要實(shí)例化的類,將對象的實(shí)例化推遲到子類
工廠方法使一個類的實(shí)例化,延遲到子類中去實(shí)現(xiàn),可以在具體工廠產(chǎn)生實(shí)例的時候,做各種校驗(yàn),和產(chǎn)生規(guī)則,避免了直接用new帶來的不方便控制流程
抽象工廠模式
定義了一個 interface 用于創(chuàng)建相關(guān)或有依賴關(guān)系的一系列對象,而無需指明具體的類
進(jìn)一步的抽象,將工廠抽象成兩層,一個AbsFactory(抽象工廠)和 具體實(shí)現(xiàn)的工廠子類,可以根據(jù)創(chuàng)建對象類型使用對應(yīng)的工廠子類,這樣將單個的簡單工廠類變成了工廠簇,更利于代碼的維護(hù)和擴(kuò)展
工廠模式意義
將實(shí)例化對象的代碼提取出來,放在一個類中統(tǒng)一維護(hù)和管理,達(dá)到和主項(xiàng)目的依賴關(guān)系的解耦,從而提高項(xiàng)目的擴(kuò)展和可維護(hù)性,還是那句話,雖然工廠模式有三種,但是大家要根據(jù)實(shí)際情況靈活運(yùn)用,切忌不要為了用而用
下節(jié)預(yù)告
OK,工廠模式的內(nèi)容到了這里就告一段落了,下一節(jié),我們從大家最感興趣的簡歷入手,開啟原型模式的學(xué)習(xí),最后,希望大家在學(xué)習(xí)的過程中,能夠感覺到設(shè)計(jì)模式的有趣之處,高效而愉快的學(xué)習(xí),那我們下期見~
定義了一個 interface 用于創(chuàng)建相關(guān)或有依賴關(guān)系的一系列對象,而無需指明具體的類
進(jìn)一步的抽象,將工廠抽象成兩層,一個AbsFactory(抽象工廠)和 具體實(shí)現(xiàn)的工廠子類,可以根據(jù)創(chuàng)建對象類型使用對應(yīng)的工廠子類,這樣將單個的簡單工廠類變成了工廠簇,更利于代碼的維護(hù)和擴(kuò)展
工廠模式意義
將實(shí)例化對象的代碼提取出來,放在一個類中統(tǒng)一維護(hù)和管理,達(dá)到和主項(xiàng)目的依賴關(guān)系的解耦,從而提高項(xiàng)目的擴(kuò)展和可維護(hù)性,還是那句話,雖然工廠模式有三種,但是大家要根據(jù)實(shí)際情況靈活運(yùn)用,切忌不要為了用而用
下節(jié)預(yù)告
OK,工廠模式的內(nèi)容到了這里就告一段落了,下一節(jié),我們從大家最感興趣的簡歷入手,開啟原型模式的學(xué)習(xí),最后,希望大家在學(xué)習(xí)的過程中,能夠感覺到設(shè)計(jì)模式的有趣之處,高效而愉快的學(xué)習(xí),那我們下期見~