設(shè)計(jì)模式系列教程—Factory Pattern(工廠(chǎng)模式)

4 Factory Pattern(工廠(chǎng)模式)

前言:工廠(chǎng)模式是為了解決new的問(wèn)題
案例分析:
REQ1:Vander作為pizza店的老板,具有一整套制作pizza的流程,準(zhǔn)備食材、烘焙、切片、包裝,隨著pizza種類(lèi)的漸漸增加,設(shè)計(jì)如下:

public class PizzaStore {

    public Pizza pizza;
    
    public Pizza orderPizza(String type) {
        Pizza pizza = new Pizza();
        
        if(type.equals("cheese")) {
            pizza = new CheesePizza();
        } else if(type.equals("greek")) {
            pizza = new GreekPizza();
        } else if(type.equals("pepperoni")) {
            pizza = new Pepperoni();
        }
        
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        
        return pizza;
        
    }
    
}

分析:隨著pizza店的發(fā)展,后期發(fā)現(xiàn)要制作越來(lái)越多的pizza,而且種類(lèi)也是超級(jí)多,多達(dá)幾十種pizza,而且有些舊類(lèi)型的pizza,幾乎沒(méi)人點(diǎn),greek和pepperoni pizza就從菜單上撤離了,而增加了FruitsPizza、HawaiiPizza、DurianPizza,所以上面orderPizza()方法就會(huì)頻繁地改動(dòng),經(jīng)常改動(dòng)導(dǎo)致pizza店的訂單系統(tǒng)經(jīng)常處在維護(hù)狀態(tài),這非常不利于接單,很明顯,實(shí)例化某些具體類(lèi),將使得orderPizza()方法出問(wèn)題,而且這違反了“開(kāi)放-關(guān)閉原則”,這個(gè)方法就無(wú)法對(duì)修改關(guān)閉了;但是現(xiàn)在已經(jīng)知道哪些是會(huì)改變的,哪些是不會(huì)改變的部分,接下來(lái)用封裝來(lái)解決上面的問(wèn)題。
解決方法1:Vander 又開(kāi)始改進(jìn)設(shè)計(jì)了
將創(chuàng)建pizza的代碼封裝起來(lái)轉(zhuǎn)移到另一個(gè)對(duì)象中,然后orderPizza()方法專(zhuān)門(mén)負(fù)責(zé)pizza的制作工作。new pizza這件事專(zhuān)門(mén)交給其他類(lèi)去負(fù)責(zé)。
重新修改PizzaStore的代碼:

public class PizzaStore {

    public SimplePizzaFactory simplePizzaFactory;
    
    public PizzaStore(SimplePizzaFactory simplePizzaFactory) {
        this.simplePizzaFactory = simplePizzaFactory;
    }
    
    public Pizza orderPizza(String type) {
        Pizza pizza = simplePizzaFactory.producePizza(type);
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        
        return pizza;
    }
    
}
簡(jiǎn)單工廠(chǎng)的代碼:
public class SimplePizzaFactory {

    public Pizza producePizza(String type) {
        Pizza pizza = new Pizza();
        
        if(type.equals("cheese")) {
            pizza = new CheesePizza();
        } else if(type.equals("greek")) {
            pizza = new GreekPizza();
        } else if(type.equals("pepperoni")) {
            pizza = new Pepperoni();
        }
        return pizza;
    }
    
}

現(xiàn)在相當(dāng)于,每次修改SimplePizzaFactory的代碼的時(shí)候,可以暫時(shí)不對(duì)線(xiàn)上的版本進(jìn)行影響,修改完SimplePizzaFactory之后再替換掉線(xiàn)上的SimplePizzaFactory就OK了。而orderPizza()方法相當(dāng)于變成了SimplePizzaFactory的使用者,它使用pizza工廠(chǎng)來(lái)完成pizza的new操作。
下面定義簡(jiǎn)單工廠(chǎng),實(shí)際上簡(jiǎn)單工廠(chǎng)不算做一個(gè)設(shè)計(jì)模式,更多地是一種編程習(xí)慣,我們會(huì)經(jīng)常用到它,所以也是非常重要的。


image.png

REQ2:Vander的Pizza店名氣非常大,吸引了很多大腕來(lái)加盟,但是由于區(qū)域的差異,每個(gè)加盟店都可能需要作出不同風(fēng)格的pizza,例如四川的希望水果Pizza里面有辣椒,廣東的則希望水果Pizza里面多一點(diǎn)菠蘿,這受到了開(kāi)店地區(qū)當(dāng)?shù)厝丝谖兜挠绊憽?br> 首先要完成加盟店,所以PizzaStore的SimpleFactory就成了一些地區(qū)PizzaFactory,如GuangDongPizzaFactory、SiChuanPizzaFactory。然后他們各自使用各自的Pizza類(lèi),例如廣東的創(chuàng)造廣東類(lèi)型的Pizza-GDPizza;四川的創(chuàng)作自己類(lèi)型的Pizza-SCPizza,然后他們各自再覆蓋原來(lái)的pizza里面的prepare、bake、cut、box等方法,他們自創(chuàng)了自己的流程,而且box的時(shí)候還用了別的商家的盒子,這完全就是打著Vander Pizza店的牌子在亂搞嘛。
問(wèn)題來(lái)了,怎樣才能加強(qiáng)質(zhì)量把控,又不失彈性,讓這些地區(qū)Pizza店可以依照自己的口味去。

解決方法2:工廠(chǎng)方法模式
Vander思前想后就是想不到合適的辦法,這時(shí)候他只能去請(qǐng)教Panda大師,Panda大師說(shuō)這個(gè)可以用工廠(chǎng)方法解決,首先要給加盟Pizza點(diǎn)使用框架,先從PizzaStore開(kāi)始改造:
由于想控制加盟店制作Pizza類(lèi)中的bake、cut、box,不能讓這些加盟店隨便亂弄,也不能讓它們使用別的公司的包裝,需要把控整個(gè)制作過(guò)程,把PizzaStore的producePizza方法設(shè)為抽象類(lèi),讓GDPizzaStore和SCPizzaStore去繼承,它們?cè)谏a(chǎn)完P(guān)izza之后只能調(diào)用父類(lèi)的orderPizza方法,繼續(xù)完成準(zhǔn)備、烘焙、切片、包裝。讓我們來(lái)看看現(xiàn)在的改造吧:

image.png

public abstract class PizzaStore {
    
    public final Pizza orderPizza(String type) {
        Pizza pizza = producePizza(type);
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        
        return pizza;
    }
    
    protected abstract Pizza producePizza(String type);
    
}
public abstract class Pizza {

    protected String name;
    //面團(tuán)類(lèi)型
    protected String dough;
    //醬料
    protected String sauce;
    //佐料    
    protected ArrayList<String> toppings = new ArrayList<String>(10);
    
    public void prepare() {
        System.out.println("Preparing " + name);
        System.out.println(" Tossing dough ...");
        System.out.println(" Adding sauce ...");
        System.out.println(" Adding toppings: ");
        for(int i=0; i<toppings.size(); i++) {
            System.out.println("  " + toppings.get(i));
        }
    }
    
    public void bake() {
        System.out.println("Bake for 25 mins at 350℃");
    }
    
    public void cut() {
        System.out.println("Cutting the pizza into diagonal slices");
    }
    
    public void box() {
        System.out.println("Boxing the pizza");
    }
    
    public String getName() {
        return name;
    }
    
}
public class GDStyleFruitsPizza extends Pizza {

    public GDStyleFruitsPizza() {
        name = "GuangDong Style Fruits Pizza";
        dough = "Thin Crust Dough";
        sauce = "Marinara Sauce";
        
        toppings.add("GuangDong Delicious Cheese");
        toppings.add("GuangDong Pipeapple");
    }
    
}
public class SCStyleFruitsPizza extends Pizza {

    public SCStyleFruitsPizza() {
        name = "SiChuan Style Fruits Pizza";
        dough = "Thick Crust Dough";
        sauce = "Marinara Sauce";
        
        toppings.add("SiChuan Delicious Cheese");
        toppings.add("SiChuan Pepper");
    }
    
}
public class GDPizzaStore extends PizzaStore {

    @Override
    public Pizza producePizza(String type) {
        Pizza pizza = null;
        
        if(type.equals("cheese")) {
            pizza = new GDStyleCheesePizza();
        } else if(type.equals("fruits")) {
            pizza = new GDStyleFruitsPizza();
        } else if(type.equals("durian")) {
            pizza = new GDStyleDurianPizza();
        } else if(type.equals("Hawaii")) {
            pizza = new GDStyleHawaiiPizza();
        }
        return pizza;
    }
    
}
public class SCPizzaStore extends PizzaStore {

    @Override
    public Pizza producePizza(String type) {
        Pizza pizza = null;

        if(type.equals("cheese")) {
            pizza = new SCStyleCheesePizza();
        } else if(type.equals("fruits")) {
            pizza = new SCStyleFruitsPizza();
        } else if(type.equals("durian")) {
            pizza = new SCStyleDurianPizza();
        } else if(type.equals("Hawaii")) {
            pizza = new SCStyleHawaiiPizza();
        }
        return pizza;
    }
    
}

說(shuō)明:工廠(chǎng)方法用來(lái)處理對(duì)象的創(chuàng)建,并將這樣的行為封裝在子類(lèi)中,這樣客戶(hù)程序中關(guān)于超類(lèi)的代碼就和子類(lèi)對(duì)象創(chuàng)建代碼解耦了。也就是說(shuō)不管以后有多少加盟店,父類(lèi)PizzaStore都不會(huì)去改變了,任由子類(lèi)PizzaStore天翻地覆,PizzaStore都以不變應(yīng)萬(wàn)變。工廠(chǎng)方法模式通過(guò)讓子類(lèi)決定該創(chuàng)建的對(duì)象是什么,來(lái)達(dá)到將對(duì)象創(chuàng)建的過(guò)程封裝的目的。


image.png

Vander就納悶了,我之前用的簡(jiǎn)單工廠(chǎng)難道不能實(shí)現(xiàn)嗎,然后Vander就想Panda大師這么干到底有什么優(yōu)勢(shì),接著Vander發(fā)現(xiàn)他也能給像Panda那樣做到把控整個(gè)控制過(guò)程。實(shí)際上關(guān)鍵的代碼就是將PizzaStore的orderPizza()方法寫(xiě)成final,讓子類(lèi)加盟店都無(wú)法修改整個(gè)制作過(guò)程,接著讓Pizza類(lèi)的box()方法也寫(xiě)成final,這樣子子類(lèi)Pizza就無(wú)法使用別的商家的包裝了。接著Vander給出了他的實(shí)現(xiàn)。


image.png

以下是主要的代碼:

public class GDPizzaStore extends PizzaStore {

    public SimplePizzaFactory simplePizzaFactory;
    
    public GDPizzaStore(SimplePizzaFactory simplePizzaFactory) {
        super(simplePizzaFactory);
    }
    
}
public class GDPizzaFactory extends SimplePizzaFactory{

    public Pizza producePizza(String type) {
        Pizza pizza = null;
        
        if(type.equals("cheese")) {
            pizza = new GDStyleCheesePizza();
        } else if(type.equals("fruits")) {
            pizza = new GDStyleFruitsPizza();
        } else if(type.equals("durian")) {
            pizza = new GDStyleDurianPizza();
        } else if(type.equals("Hawaii")) {
            pizza = new GDStyleHawaiiPizza();
        }
        return pizza;
    }
    
}
public class PizzaStore {

    public SimplePizzaFactory simplePizzaFactory;
    
    public PizzaStore(SimplePizzaFactory simplePizzaFactory) {
        this.simplePizzaFactory = simplePizzaFactory;
    }
    
    public final Pizza orderPizza(String type) {
        Pizza pizza = simplePizzaFactory.producePizza(type);
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        
        return pizza;
    }
    
}
public abstract class SimplePizzaFactory {

    public abstract Pizza producePizza(String type); 
    
}

說(shuō)明:從代碼上看來(lái),其實(shí)確實(shí)跟工廠(chǎng)方法模式差不多,只是把producePizza方法放到了簡(jiǎn)單工廠(chǎng)而已,但是這么操作最后在運(yùn)行的時(shí)候,必須要實(shí)例化相應(yīng)的工廠(chǎng),例如GDPizzaStore需要傳入GDPizzaFactory,則需要實(shí)例化GDPizzaFactory,并且還不能傳錯(cuò),傳錯(cuò)了就會(huì)導(dǎo)致最后廣東的Pizza店賣(mài)的確是其它工廠(chǎng)造出來(lái)的Pizza。
下面比對(duì)一下工廠(chǎng)方法模式和簡(jiǎn)單工廠(chǎng)是怎么造出Pizza的。
簡(jiǎn)單工廠(chǎng)的制作Pizza方法:

public class Main {
    public static void main(String[] args) {
        GDPizzaFactory gdFactory = new GDPizzaFactory();
        GDPizzaStore gdPizzaStore = new GDPizzaStore(gdFactory);
        gdPizzaStore.orderPizza("fruits");
        
        SCPizzaFactory scFactory = new SCPizzaFactory();
        SCPizzaStore scPizzaStore = new SCPizzaStore(scFactory);
        scPizzaStore.orderPizza("fruits");
    }
}

工廠(chǎng)方法的制作Pizza方法:

public class Main {
    public static void main(String[] args) {
        GDPizzaStore gdPizzaStore = new GDPizzaStore();
        Pizza gdFruitsPizza1 = gdPizzaStore.orderPizza("fruits");
        SCPizzaStore scPizzaStore = new SCPizzaStore();
        Pizza scfruitsPizza2 = scPizzaStore.orderPizza("fruits");
    }
}

最后Vander發(fā)現(xiàn)原來(lái)他的方法需要先實(shí)例化GDPizzaFactory,然后將Factory作為參數(shù)放進(jìn)GDPizzaStore里面,Vander就想,既然都是GDPizzaFactory對(duì)應(yīng)GDPizzaStore,SCPizzaFactory對(duì)應(yīng)SCPizzaStore,那我直接在GDPizzaStore里面實(shí)例化GDPizzaFactory,SCPizzaStore里面實(shí)例化SCPizzaFactory不就好了。接著又進(jìn)行了一輪改造:

public class GDPizzaStore extends PizzaStore {
    
    public GDPizzaStore() {
        simplePizzaFactory = new GDPizzaFactory();
    }
    
}
public class PizzaStore {

    protected SimplePizzaFactory simplePizzaFactory;
    
    public final Pizza orderPizza(String type) {
        Pizza pizza = simplePizzaFactory.producePizza(type);
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        
        return pizza;
    }
    
}

說(shuō)明:最后雖然在運(yùn)行時(shí)的調(diào)用是一樣的,但是這里相當(dāng)于直接讓GDPizzaStore和GDPizzaFactory直接綁定在一起了,也增加了代碼的耦合度。而且每次多開(kāi)一個(gè)加盟店,都需要實(shí)現(xiàn)PizzaStore和PizzaFactory多一次,確實(shí)也沒(méi)多大必要。

REQ3:由于Pizza大家已經(jīng)吃膩了,所以Vander為了招來(lái)更多的客戶(hù),想擴(kuò)展菜單了,現(xiàn)在不只是賣(mài)Pizza了,現(xiàn)在Pizza店學(xué)習(xí)必勝客的方式,還賣(mài)雞翅、漢堡并且還出售各種獨(dú)家飲料。那么怎么擴(kuò)展接口呢?在PizzaStore基類(lèi)中繼續(xù)添加produceFiredChicken()、produceBurger()、produceBeverage(),這么做的后果就是所有的加盟店都得跟著做改變,但是有些加盟店P(guān)izza生意做得火熱,覺(jué)得根本沒(méi)必要添加這么多種類(lèi)進(jìn)去。這時(shí)候Vander想到了他之前實(shí)現(xiàn)的SimpleFactory2,就是將簡(jiǎn)單工廠(chǎng)編程“抽象工廠(chǎng)”,讓子類(lèi)工廠(chǎng)去完成產(chǎn)品的創(chuàng)建,這樣做的好處就是加盟店的PizzaStore都可以不用動(dòng),而且把這些食物的創(chuàng)建都放到了一個(gè)大工廠(chǎng)去做這么一件事情了。
下面是Vander的設(shè)計(jì):

image.png

這個(gè)設(shè)計(jì)圖看起來(lái)有一定的復(fù)雜度,但是其實(shí)就是在SimpleFactory2上的PizzaFactory改為了FoodFactory添加了幾個(gè)生產(chǎn)其他產(chǎn)品的方法,本來(lái)只有一個(gè)Pizza的抽象類(lèi),現(xiàn)在多了Beverage、Burger、FiredChicken這幾個(gè)抽象類(lèi),相當(dāng)于現(xiàn)在FoodFactory是一個(gè)大工廠(chǎng)來(lái)生產(chǎn)各種各樣的產(chǎn)品。可能你會(huì)發(fā)現(xiàn)這FoodFactory里面實(shí)際上不就是工廠(chǎng)方法模式嘛,沒(méi)錯(cuò)!就是這樣相當(dāng)于FoodFactory里面生產(chǎn)各種各樣的食物,但是生產(chǎn)的食物是什么樣的Pizza、什么樣的炸雞,都是由子類(lèi)做決定的。
實(shí)際上SimpleFactory2已經(jīng)不算是簡(jiǎn)單工廠(chǎng)了,簡(jiǎn)單工廠(chǎng)應(yīng)該是更單純一點(diǎn)的,通過(guò)一個(gè)含參的方法來(lái)實(shí)例化產(chǎn)品類(lèi)。SimpleFactory2經(jīng)過(guò)改進(jìn)之后已經(jīng)成為了抽象工廠(chǎng)模式了。
抽象工廠(chǎng)允許客戶(hù)使用抽象的接口來(lái)創(chuàng)建一組相關(guān)的產(chǎn)品,而不需要知道實(shí)際產(chǎn)出的具體產(chǎn)品是什么,這樣一來(lái),客戶(hù)就從具體的產(chǎn)品中被解耦了。上面這段話(huà)通俗地說(shuō),就是像子類(lèi)工廠(chǎng)完成Pizza的創(chuàng)建,然后PizzaStore在orderPizza()方法中直接是拿基類(lèi)Pizza類(lèi)來(lái)進(jìn)行prepare、bake、cut、box等操作,而不用理會(huì)是哪種口味哪個(gè)區(qū)域的Pizza。從而達(dá)到了PizzaStore與具體的Pizza類(lèi)的解耦。
下面我們梳理一下整個(gè)過(guò)程,首先剛開(kāi)始PizzaStore依賴(lài)于具體的Pizza類(lèi),如下圖所示:


image.png

而且隨著Pizza類(lèi)型的增加,依賴(lài)將會(huì)越來(lái)越多,代碼里面減少具體的類(lèi)的依賴(lài)是必須要做的,接下來(lái)我們看看我們后面的實(shí)現(xiàn):


image.png

這樣就依賴(lài)抽象的類(lèi)Pizza。(遵循了依賴(lài)抽象而不是具體的類(lèi)的原則)
我們發(fā)現(xiàn)高層組件(PiizaStore)和底層組件(具體的Piizza子類(lèi))都依賴(lài)于Pizza的抽象類(lèi)。我們一般進(jìn)行設(shè)計(jì)的時(shí)候,首先設(shè)計(jì)PizzaStore,PizzaStore要能生產(chǎn)出很多具體的不同類(lèi)型的Pizza,其實(shí)這時(shí)候你可以倒過(guò)來(lái)想,從具體的Pizza想起,發(fā)現(xiàn)應(yīng)該先抽象出一個(gè)Pizza基類(lèi),然后這個(gè)Pizza基類(lèi)直接給PizzaStore用,然后就發(fā)現(xiàn)這個(gè)抽象的Pizza基類(lèi)就像橋梁一樣連接著PizzaStore和具體的Pizza類(lèi)。這里面就包含了“依賴(lài)倒置原則”,即在從上往下的設(shè)計(jì)的同時(shí),不妨先想想能不能從下往上。
下面有三個(gè)指導(dǎo)方針來(lái)避免違反“依賴(lài)倒置原則”:
1、變量不可以持有具體類(lèi)的引用。(如果需要用new,用工廠(chǎng)代替)
2、不要讓類(lèi)派生自具體類(lèi)。(如果派生具體類(lèi)了,你就會(huì)依賴(lài)具體類(lèi),請(qǐng)派生一個(gè)抽象)
3、不要覆蓋基類(lèi)中已實(shí)現(xiàn)的方法(基類(lèi)已經(jīng)實(shí)現(xiàn)了,說(shuō)明具有通用性,否則請(qǐng)?jiān)O(shè)為抽象)

最后的最后,我們又來(lái)總結(jié)我們現(xiàn)在現(xiàn)有的設(shè)計(jì)模式武器。

面向?qū)ο蠡A(chǔ)

抽象、封裝、多態(tài)、繼承

六大原則

設(shè)計(jì)原則一:封裝變化
設(shè)計(jì)原則二:針對(duì)接口編程,不針對(duì)實(shí)現(xiàn)編程。
設(shè)計(jì)原則三:多用組合,少用繼承。
設(shè)計(jì)原則四:為交互對(duì)象之間的松耦合設(shè)計(jì)而努力。
設(shè)計(jì)原則五:對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉。
設(shè)計(jì)原則六:依賴(lài)抽象,不要依賴(lài)于具體的類(lèi) 。

模式

工廠(chǎng)方法模式:定義了一個(gè)創(chuàng)建對(duì)象的接口,但由子類(lèi)決定要實(shí)例化的類(lèi)是哪一個(gè)。工廠(chǎng)方法讓類(lèi)把實(shí)例化推遲到子類(lèi)。

image.png

抽象工廠(chǎng)模式:提供一個(gè)接口,用于創(chuàng)建相關(guān)對(duì)象或者是依賴(lài)對(duì)象的家族,而不需要明確指定具體的類(lèi)(說(shuō)白了就是一系列同類(lèi)型的工廠(chǎng)方法集合)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 本章目錄如下: 一、階段一 二、階段二 三、階段三 四、java中的裝飾者 五、模式問(wèn)答 六、設(shè)計(jì)原則總結(jié) 我們...
    黑夜0411閱讀 542評(píng)論 0 0
  • 設(shè)計(jì)模式之——工廠(chǎng)模式 一、學(xué)習(xí)內(nèi)容: 所有的 工廠(chǎng)模式 都是通過(guò)減少應(yīng)用程序和具體類(lèi)之間的依賴(lài),促進(jìn)松耦合。 所...
    FeidianJava閱讀 363評(píng)論 0 0
  • 工廠(chǎng)方法模式通過(guò)引入工廠(chǎng)等級(jí)結(jié)構(gòu),解決了簡(jiǎn)單工廠(chǎng)模式中工廠(chǎng)類(lèi)職責(zé)太重的問(wèn)題,但由于工廠(chǎng)方法模式中的每個(gè)工廠(chǎng)只生產(chǎn)一...
    justCode_閱讀 1,293評(píng)論 1 6
  • 工廠(chǎng)模式包含三種模式:簡(jiǎn)單工廠(chǎng)模式、工廠(chǎng)方法模式和抽象工廠(chǎng)模式。 簡(jiǎn)單工廠(chǎng)模式 定義簡(jiǎn)單工廠(chǎng)模式:由一個(gè)工廠(chǎng)類(lèi)根據(jù)...
    RickGe閱讀 396評(píng)論 0 0
  • 最近,南方的臺(tái)風(fēng)與暴雨鬧得兇,北方的天氣熱得人發(fā)悶又發(fā)慌。有個(gè)妹子問(wèn)我,哥,你說(shuō),冬天再冷我可以一件件穿,穿多厚都...
    林濤亂翻書(shū)閱讀 542評(píng)論 0 5

友情鏈接更多精彩內(nèi)容