亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

《設(shè)計(jì)模式》3.結(jié)構(gòu)型模式

Yuqi / 1731人閱讀

摘要:類的適配器結(jié)構(gòu)目標(biāo)角色這就是所期待得到的接口,由于是類適配器模式,因此目標(biāo)不可以是類。這種類型的設(shè)計(jì)模式屬于結(jié)構(gòu)型模式,它是作為現(xiàn)有的類的一個包裝。和適配器模式的關(guān)系適配器模式的用意是改變所考慮對象的接口,而代理模式不能改變。

點(diǎn)擊進(jìn)入我的博客 3.1 適配器模式

適配器模式把一個類的接口變換成客戶端所期待的另一種接口,使得原本因接口不匹配而無法在一起工作的兩個類能夠在一起工作。

3.1.1 類的適配器結(jié)構(gòu)

目標(biāo)(Target)角色:這就是所期待得到的接口,由于是類適配器模式,因此目標(biāo)不可以是類。

源(Adaptee)角色:現(xiàn)有需要適配的接口。

適配器(Adapter)角色:適配器類是本模式的核心,必須是具體類。

interface Target {
    void operation1();
    void operation2();
}

class Adaptee {
    public void operation1() {}
}

class Adapter extends Adaptee implements Target {
    @Override
    public void operation2() {}
}
類的適配器

類的適配器模式把被的類的API轉(zhuǎn)換成目標(biāo)類的API。

是通過繼承實(shí)現(xiàn)的。

類的適配器效果

使用一個具體類把源適配到目標(biāo)中。這樣如果源以及源的子類都使用此類適配,就行不通了。

由于適配器是源的子類,因此可以在適配器中重寫源的一些方法。

由于引進(jìn)了一個適配器類,因此只有一個線路到達(dá)目標(biāo)類,是問題得到簡化。

3.1.2 對象的適配器結(jié)構(gòu)

目標(biāo)(Target)角色:這就是所期待得到的接口,因此目標(biāo)可以是具體或抽象的類。

源(Adaptee)角色:現(xiàn)有需要適配的接口。

適配器(Adapter)角色:適配器類是本模式的核心,必須是具體類。

interface Target {
    void operation1();
    void operation2();
}

class Adaptee {
    public void operation1() {}
}

class Adapter implements Target {
    private Adaptee adaptee;

    public Adapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }

    @Override
    public void operation1() {
        adaptee.operation1();
    }

    @Override
    public void operation2() {
        // do something
    }
}
對象的適配器

對象的適配器是通過依賴實(shí)現(xiàn)的

推薦使用該方法

對象的適配器效果

一個適配器可以把多種不同的源適配到同一個目標(biāo),即同一個適配器可以把源類和它的子類都適配到目標(biāo)接口

與類的適配器模式相比,要想置換源類的方法就不容易。

增加新的方法方便的多

3.1.3 細(xì)節(jié)
使用場景

系統(tǒng)需要使用現(xiàn)有的類,而此類的接口不符合系統(tǒng)的需要

想要建立一個可以重復(fù)使用的類,用于與一些彼此間沒有太大關(guān)聯(lián)的一些類,包括一些可能在將來引進(jìn)的類一起工作。

對對象的適配器兒模式而言,在設(shè)計(jì)里,需要改變多個已有的子類的接口,如果使用類的適配器模式,就需要針對每個子類做一個適配器類,這不太實(shí)際。

優(yōu)點(diǎn)

可以讓任何兩個沒有關(guān)聯(lián)的類一起運(yùn)行。

提高了類的復(fù)用。

增加了類的透明度。

靈活性好。

缺點(diǎn)

過多地使用適配器,會讓系統(tǒng)非常零亂,不易整體進(jìn)行把握。比如,明明看到調(diào)用的是 A 接口,其實(shí)內(nèi)部被適配成了 B 接口的實(shí)現(xiàn),一個系統(tǒng)如果太多出現(xiàn)這種情況,無異于一場災(zāi)難。因此如果不是很有必要,可以不使用適配器,而是直接對系統(tǒng)進(jìn)行重構(gòu)。

由于 JAVA 至多繼承一個類,所以至多只能適配一個適配者類,而且目標(biāo)類必須是抽象類。

注意點(diǎn)

目標(biāo)接口可以忽略,此時目標(biāo)接口和源接口實(shí)際上是相同的

適配器類可以是抽象類

可以有帶參數(shù)的適配器模式

3.1.4 一個充電器案例
// 充電器只能接受USB接口
public class Charger {
    public static void main(String[] args) throws Exception{
        USB usb = new SuperAdapter(new TypeC());
        connect(usb);
        usb = new SuperAdapter(new Lightning());
        connect(usb);
    }

    public static void connect(USB usb) {
        usb.power();
        usb.data();
    }
}

// 充電器的接口都是USB的,假設(shè)有兩個方法分別是電源和數(shù)據(jù)
interface USB {
    void power();
    void data();
}

// IOS的Lightning接口
class Lightning {
    void iosPower() {
        System.out.println("IOS Power");
    }
    void iosData() {
        System.out.println("IOS Data");
    }
}

// TYPE-C接口
class TypeC {
    void typeCPower() {
        System.out.println("TypeC Power");
    }
    void typeCData() {
        System.out.println("TypeC Data");
    }
}

// 超級適配器,可以適配多種手機(jī)機(jī)型
class SuperAdapter implements USB {
    private Object obj;

    public SuperAdapter(Object obj) {
        this.obj = obj;
    }

    @Override
    public void power() {
        if(obj.getClass() == Lightning.class) {
            ((Lightning)obj).iosPower();
        } else if(obj.getClass() == TypeC.class) {
            ((TypeC)obj).typeCPower();
        }
    }

    @Override
    public void data() {
        if(obj.getClass() == Lightning.class) {
            ((Lightning)obj).iosData();
        } else if(obj.getClass() == TypeC.class) {
            ((TypeC)obj).typeCData();
        }
    }
}
3.2 缺省適配模式

缺省適配模式為一個接口提供缺省實(shí)現(xiàn),這樣子類型可以從這個缺省實(shí)現(xiàn)進(jìn)行擴(kuò)展,而不必從原有接口進(jìn)行擴(kuò)展。

3.2.1 缺省適配模式結(jié)構(gòu)

簡單的例子

下面程序中,Monk接口定義了兩個方法,于是它的子類必須實(shí)現(xiàn)這兩個方法。

但出現(xiàn)了一個LuZhiShen,他只能實(shí)現(xiàn)一部分方法,另一部分方法無法實(shí)現(xiàn)

所以需要一個抽象的適配類MonkAdapter實(shí)現(xiàn)此Monk接口,此抽象類給接口所有方法都提供一個空的方法,LuZhiShen只需要繼承該適配類即可。

// 和尚
interface Monk {
    void practiceKungfu();
    void chantPrayer();
}

abstract class MonkAdapter implements Monk {
    @Override
    public void practiceKungfu() {}

    @Override
    public void chantPrayer() {}
}

class LuZhiShen extends MonkAdapter {
    @Override
    public void practiceKungfu() {
        System.out.println("拳打鎮(zhèn)關(guān)西");
    }
}
3.2.2 細(xì)節(jié)
使用場景

任何時候不準(zhǔn)備實(shí)現(xiàn)一個接口中所有方法的時候

作用

缺省適配器模式可以使所需要的類不必實(shí)現(xiàn)不需要的接口。

核心點(diǎn)

缺省適配的類必須是抽象類,因?yàn)檫@個類不應(yīng)當(dāng)被實(shí)例化

缺省適配的類提供的方法必須是具體的方法,而不是抽象的方法。

3.3 組合模式

組合模式,就是在一個對象中包含其他對象,這些被包含的對象可能是終點(diǎn)對象(不再包含別的對象),也有可能是非終點(diǎn)對象(其內(nèi)部還包含其他對象)。
我們將對象稱為節(jié)點(diǎn),即一個根節(jié)點(diǎn)包含許多子節(jié)點(diǎn),這些子節(jié)點(diǎn)有的不再包含子節(jié)點(diǎn),而有的仍然包含子節(jié)點(diǎn),以此類推。很明顯,這是樹形結(jié)構(gòu),終結(jié)點(diǎn)叫葉子節(jié)點(diǎn),非終節(jié)點(diǎn)叫樹枝節(jié)點(diǎn),第一個節(jié)點(diǎn)叫根節(jié)點(diǎn)。

3.3.1 安全式的合成模式結(jié)構(gòu)

安全式的合成模式要求管理集合的方法只出現(xiàn)在樹枝結(jié)點(diǎn)(Composite)中,而不出現(xiàn)在樹葉結(jié)點(diǎn)中。

抽象構(gòu)建(Component)角色:這是一個抽象角色,他給參加組合的對象定義出公共的接口及其默認(rèn)行為,可以用來管理所有的子對象。

樹葉(Leaf)角色:樹葉是沒有子對象的對象,定義出參加組合的原始對象的行為。

樹枝(Composite)角色:代表參加組合的有下級子對象的對象。樹枝構(gòu)件類給出所有管理子對象的方法。

3.3.2 透明的合成模式結(jié)構(gòu)


透明的合成模式要求所有的具體構(gòu)建類,都符合一個固定的接口。

3.4 裝飾器模式

裝飾器模式(Decorator)允許向一個現(xiàn)有的對象添加新的功能,同時又不改變其結(jié)構(gòu)。這種類型的設(shè)計(jì)模式屬于結(jié)構(gòu)型模式,它是作為現(xiàn)有的類的一個包裝。

3.4.1 裝飾器結(jié)構(gòu)

抽象構(gòu)件(Component)角色:給出一個抽象結(jié)構(gòu),以規(guī)范準(zhǔn)備接受附加責(zé)任的對象。

具體構(gòu)件(Concrete Component)角色:定義一個要接受附加責(zé)任的類

裝飾(Decorator)角色:持有一個構(gòu)件對象的實(shí)例,并定義一個與抽象構(gòu)件接口一致的接口。

具體裝飾(Concrete Decorator)角色:負(fù)責(zé)給構(gòu)件對象“貼上”附加的責(zé)任。

3.4.2 裝飾器細(xì)節(jié)
使用場景

需要擴(kuò)展一個類的功能

需要動態(tài)地給一個對象增加功能,這些功能可以再動態(tài)的插銷

需要增加由一些基本功能的排列組合而產(chǎn)生非常大量的功能

優(yōu)點(diǎn)

更加靈活:裝飾模式和繼承關(guān)系的目的都是要擴(kuò)展對象的功能,但是裝飾模式比繼承更加靈活

多樣性:通過使用不同具體裝飾類及其排列組合,可以創(chuàng)造出不同的行為

動態(tài)擴(kuò)展:裝飾器可以動態(tài)擴(kuò)展構(gòu)件類

缺點(diǎn)

會產(chǎn)生比繼承關(guān)系更多的對象

比繼承更加容易出錯

注意點(diǎn)

裝飾類的接口必須與被裝飾類的接口相容。

盡量保持抽象構(gòu)件(Component)簡單。

可以沒有抽象的(Component),此時裝飾器(Decorator)一般是具體構(gòu)件(Concrete Component)的一個子類。

裝飾(Decorator)和具體裝飾(Concrete Decorator)可以合并。

InputStream及其子類

抽象構(gòu)件(Component)角色:InputStream

具體構(gòu)件(Concrete Component)角色:ByteArrayInputStream、PipedInputStream、StringBufferInputStream等原始流處理器。

裝飾(Decorator)角色:FilterInputStream

具體裝飾(Concrete Decorator)角色:DateInputStream、BufferedInputStreamLineNumberInputStream

OutputStream及其子類

也用到類裝飾器模式

3.4.3 例子
// Component:一個藝人
interface Artist {
    void show();
}

// Concrete Component:一個歌手
class Singer implements Artist {
    @Override
    public void show() {
        System.out.println("Let It Go");
    }
}

// 裝飾后的歌手:不僅會唱歌,還會講笑話和跳舞
class SuperSinger implements Artist {
    private Artist role;
    
    public SuperSinger(Artist role) {
        this.role = role;
    }
    @Override
    public void show() {
        System.out.println("Tell Jokes!");
        role.show();
        System.out.println("Dance!");
    }
}
3.5 代理模式

代理模式給某一個對象提供一個代理對象,并由代理對象控制對原對象對引用。

3.5.1 代理模式結(jié)構(gòu)

抽象主題(Subject)角色:聲明了真實(shí)主題和代理主題的共同接口

代理主題(Proxy)角色:代理主題角色內(nèi)部含有對真實(shí)主題的引用,從而可以在任何時候操作真實(shí)主題對象;代理主題角色提供一個與真實(shí)主題角色相同的接口,以便可以在任何時候都可以替代真實(shí)主題控制對真實(shí)主題的引用,負(fù)責(zé)在需要的時候創(chuàng)建真實(shí)主題對象(和刪除真實(shí)主題對象);代理角色通常在將客戶端調(diào)用傳遞給真實(shí)的主題之前或之后,都要執(zhí)行某個操作,而不是單純地將調(diào)用傳遞給真實(shí)主題對象。

真實(shí)主題(RealSubject)角色:定義了代理角色所代表的真實(shí)對象。

public class Test {
    public static void main(String[] args) {
        Subject subject = new RealSubject();
        Subject proxy = new ProxySubject(subject);
        proxy.request(); // 此處通過代理類來執(zhí)行
    }
}

interface Subject {
    void request();
}

class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("RealSubject");
    }
}

class ProxySubject implements Subject {
    private Subject subject;

    public ProxySubject(Subject subject) {
        this.subject = subject;
    }

    @Override
    public void request() {
        System.out.println("ProxySubject");
    }
}
3.5.2 動態(tài)代理

自從JDK 1.3以后,Java在java.lang.reflect庫中提供了一下三個類直接支持代理模式:Proxy、InvocationHanderMethod。

動態(tài)代理步驟

創(chuàng)建一個真實(shí)對象

創(chuàng)建一個與真實(shí)對象有關(guān)的調(diào)用處理器對象InvocationHandler

創(chuàng)建代理,把調(diào)用處理器和要代理的類聯(lián)系起來Proxy.newInstance()

在調(diào)用處理對象的invoke()方法中執(zhí)行相應(yīng)操作

public class Test {
    public static void main(String[] args) {
        // 創(chuàng)建要被代理的實(shí)例對象
        Subject subject = new RealSubject();
        // 創(chuàng)建一個與被代理實(shí)例對象有關(guān)的InvocationHandler
        InvocationHandler handler = new ProxySubject(subject);
        // 創(chuàng)建一個代理對象來代理subject,被代理的對象subject的每個方法執(zhí)行都會調(diào)用代理對象proxySubject的invoke方法
        Subject proxySubject = (Subject) Proxy.newProxyInstance(Subject.class.getClassLoader(), new Class[]{Subject.class}, handler);
        // 代理對象執(zhí)行
        proxySubject.request();
    }
}

interface Subject {
    void request();
}

class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("RealSubject");
    }
}

class ProxySubject implements InvocationHandler {
    private Subject subject;

    public ProxySubject(Subject subject) {
        this.subject = subject;
    }

    /**
     * @param proxy 要代理的
     * @param method
     * @param args
     * @return
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before Proxy");
        Object obj = method.invoke(subject, args);
        System.out.println("After Proxy");
        return obj;
    }
}

可以使用范型來創(chuàng)建ProxySubject

可以使用匿名內(nèi)部類減少代碼數(shù)量請查看14.7節(jié)

3.5.3 細(xì)節(jié)
優(yōu)點(diǎn)

代理類和真實(shí)類分離,職責(zé)清晰。

在不改變真是累代碼的基礎(chǔ)上擴(kuò)展了功能。

缺點(diǎn)

由于在客戶端和真實(shí)主題之間增加了代理對象,因此有些類型的代理模式可能會造成請求的處理速度變慢。

實(shí)現(xiàn)代理模式需要額外的工作,有些代理模式的實(shí)現(xiàn)非常復(fù)雜。

和適配器模式的關(guān)系

適配器模式的用意是改變所考慮對象的接口,而代理模式不能改變。

和裝飾模式

裝飾模式應(yīng)當(dāng)為所裝飾的對象提供增強(qiáng)功能

代理模式對對象的使用施加控制,并不提供對象本身的增強(qiáng)功能

虛擬代理

虛擬代理模式(Virtual PRoxy)會推遲真正所需對象實(shí)例化時間。在需要真正的對象工作之前,如果代理對象能夠處理,那么暫時不需要真正對象來出手。

當(dāng)一個真實(shí)主題對象的加載需要耗費(fèi)資源時,一個虛擬代理對象可以代替真實(shí)對象接受請求,并展示“正在加載”的信息,并在適當(dāng)?shù)臅r候加載真實(shí)主題對象。

3.6 享元模式

享元模式以共享的方式高效地支持大量的細(xì)粒度對象。

3.6.1 單純享元模式

單純享元模式中,所有的享元對象都是可以共享的。

抽象享元(Flyweight)角色:是所有具體享元角色的超類,并為這些類規(guī)定公共接口。

具體享元(Concrete Flyweight)角色:實(shí)現(xiàn)抽象享元的接口。如果由內(nèi)蘊(yùn)狀態(tài)的話,必須負(fù)責(zé)為內(nèi)蘊(yùn)狀態(tài)提供空間。

享元工廠(Flyweight Factory)角色:負(fù)責(zé)創(chuàng)建和管理享元角色。如果系統(tǒng)中有了則返回該角色,沒有則創(chuàng)建。

客戶端(Client)角色:維護(hù)一個所有享元對象的引用。存儲所有享元對象的外蘊(yùn)狀態(tài)。

3.6.2 復(fù)合享元模式

抽象享元(Flyweight)角色 :給出一個抽象接口,以規(guī)定出所有具體享元角色需要實(shí)現(xiàn)的方法。

具體享元(ConcreteFlyweight)角色:實(shí)現(xiàn)抽象享元角色所規(guī)定出的接口。如果有內(nèi)蘊(yùn)狀態(tài)的話,必須負(fù)責(zé)為內(nèi)蘊(yùn)狀態(tài)提供存儲空間。

復(fù)合享元(ConcreteCompositeFlyweight)角色 :復(fù)合享元角色所代表的對象是不可以共享的,但是一個復(fù)合享元對象可以分解成為多個本身是單純享元對象的組合。復(fù)合享元角色又稱作不可共享的享元對象(UnsharedConcreteFlyweight)。

享元工廠(FlyweightFactory)角色 :負(fù)責(zé)創(chuàng)建和管理享元角色。當(dāng)一個客戶端對象調(diào)用一個享元對象的時候,如果已經(jīng)有了,享元工廠角色就應(yīng)當(dāng)提供這個已有的享元對象;如果系統(tǒng)中沒有一個適當(dāng)?shù)南碓獙ο蟮脑?,享元工廠角色就應(yīng)當(dāng)創(chuàng)建一個合適的享元對象。

3.6.3 細(xì)節(jié)
內(nèi)蘊(yùn)狀態(tài)和外蘊(yùn)狀態(tài)

內(nèi)蘊(yùn)狀態(tài):是存儲在享元對象內(nèi)部的,不會隨環(huán)境改變而改變的。一個享元可以具有內(nèi)蘊(yùn)狀態(tài)并可以共享。
外蘊(yùn)狀態(tài):隨環(huán)境改變而改變、不可以共享的狀態(tài)。享元對象的外蘊(yùn)狀態(tài)必須由客戶端保存,并在享元對象被創(chuàng)建之后,在需要使用的時候再傳入到享元對象內(nèi)部。

不變模式

享元模式中的對象不一定非要是不變對象,但大多數(shù)享元對象的確是這么設(shè)計(jì)的。

享元工廠

使用單例模式:一般只需要一個享元工廠,可以設(shè)計(jì)成單例的。

備忘錄模式:享元工廠負(fù)責(zé)維護(hù)一個表,通過這個表把很多相同的實(shí)例與它們的一個對象聯(lián)系起來。

優(yōu)點(diǎn)

減少對象的創(chuàng)建,降低內(nèi)存消耗

缺點(diǎn)

提高了系統(tǒng)的復(fù)雜度,為了使對象可以共享,需要將一些狀態(tài)外部化

需要將一些狀態(tài)外部化,而讀取外部狀態(tài)是的運(yùn)行時間稍微變長

使用場景

一個系統(tǒng)中有大量對象。

這些對象消耗大量內(nèi)存。

這些對象的狀態(tài)大部分可以外部化。

這些對象可以按照內(nèi)蘊(yùn)狀態(tài)分為很多組,當(dāng)把外蘊(yùn)對象從對象中剔除出來時,每一組對象都可以用一個對象來代替。

系統(tǒng)不依賴于這些對象身份,換言之,這些對象是不可分辨的。

Java應(yīng)用

String對象,有則返回,沒有則創(chuàng)建一個字符串并保存

數(shù)據(jù)庫的連接池

3.6.4 案例

依舊是熟悉的KFC點(diǎn)餐為例:

外蘊(yùn)狀態(tài):點(diǎn)餐的顧客

內(nèi)蘊(yùn)狀態(tài):顧客點(diǎn)的食物

具體享元角色:維護(hù)內(nèi)蘊(yùn)狀態(tài)(客人要點(diǎn)的食物)。

public class KFC {
    public static void main(String[] args) {
        OrderFactory orderFactory = OrderFactory.getInstance();
        Order order = orderFactory.getOrder(Food.MiniBurger);
        order.operation("李雷");
        order = orderFactory.getOrder(Food.MiniBurger);
        order.operation("韓梅梅");
    }
}

enum Food {
    MiniBurger,
    MexicanTwister,
    CornSalad,
    HotWing,
    PepsiCola
}

// Flyweight角色
interface Order {
    // 傳入的是外蘊(yùn)對象:顧客
    void operation(String customer);
}

// ConcreteFlyweight角色
class FoodOrder implements Order {
    // 內(nèi)蘊(yùn)狀態(tài)
    private Food food;
    // 構(gòu)造方法,傳入享元對象的內(nèi)部狀態(tài)的數(shù)據(jù)
    public FoodOrder(Food food) {
        this.food = food;
    }

    @Override
    public void operation(String customer) {
        System.out.println("顧客[" + customer + "]點(diǎn)的是" + food.toString());
    }
}

// FlyweightFactory角色
class OrderFactory {
    private Map orderPool = new HashMap<>();
    private static OrderFactory instance = new OrderFactory();

    private OrderFactory() {}

    public static OrderFactory getInstance() {
        return instance;
    }

    // 獲取Food對應(yīng)的享元對象
    public Order getOrder(Food food) {
        Order order = orderPool.get(food);
        if (null == order) {
            order = new FoodOrder(food);
            orderPool.put(food, order);
        }
        return order;
    }
}
3.7 門面模式

門面模式(Facade Pattern)要求一個子系統(tǒng)的外部與其內(nèi)部通信,必須通過一個統(tǒng)一的門面對象進(jìn)行。

3.7.1 門面模式結(jié)構(gòu)

門面模式?jīng)]有一個一般化的類圖描述,可以用下面的例子來說明。

門面(Facade)角色:外部可以調(diào)用這個角色的方法。此角色知道子系統(tǒng)的功能和責(zé)任。

子系統(tǒng)(Subsystem)角色:可以有多個子系統(tǒng),子系統(tǒng)不需要知道門面的存在。

3.7.2 細(xì)節(jié)
門面數(shù)量

通常只需要一個門面類,而且只有一個實(shí)例,因此可以設(shè)計(jì)稱單例模式。當(dāng)然也可有多個類。

使用場景

為一個復(fù)雜的子系統(tǒng)提供一個簡單的接口

使子系統(tǒng)和外部分離開來

構(gòu)建一個層次化系統(tǒng)時,可以使使用Facade模式定義系統(tǒng)中每一層,實(shí)現(xiàn)分層。

優(yōu)點(diǎn)

減少系統(tǒng)之間的相互依賴。

提高了安全性。

缺點(diǎn)

不符合開閉原則

如果要改東西很麻煩,繼承重寫都不合適。

Java例子

MVC三層結(jié)構(gòu)

3.7.3 KFC例子

假如沒有服務(wù)員(門面),顧客(外部系統(tǒng))要點(diǎn)一個套餐需要知道每個套餐包含的食物(子系統(tǒng))種類,這樣就會非常麻煩,所以最好的方式是直接告訴服務(wù)員套餐名稱就好了。

public class Customer {
    public static void main(String[] args) {
        Waiter waiter = new Waiter();
        List foodList = waiter.orderCombo("Combo1");
    }
}

abstract class Food {}
class MiniBurger extends Food {}
class MexicanTwister extends Food {}
class CornSalad extends Food {}
class HotWing extends Food {}
class PepsiCola extends Food {}

class Waiter {
    public List orderCombo(String comboName) {
        List foodList;
        switch (comboName) {
            case "Combo1" : 
                foodList = Arrays.asList(new MiniBurger(), new CornSalad(), new PepsiCola()); 
                break;
            case "Combo2":
                foodList = Arrays.asList(new MexicanTwister(), new HotWing(), new PepsiCola());
                break;
            default:
                foodList = new ArrayList<>();
        }
        return foodList;
    }
}
3.8 過濾器模式

過濾器模式使用不同的條件過濾一組對象,并通過邏輯操作以解耦方式將其鏈接。這種類型的設(shè)計(jì)模式屬于結(jié)構(gòu)模式,因?yàn)樵撃J浇M合多個標(biāo)準(zhǔn)以獲得單個標(biāo)準(zhǔn)。

3.8.1 細(xì)節(jié)
步驟

創(chuàng)建一個要過濾的普通類,要有獲得其私有屬性的get方法

創(chuàng)建一個接口,規(guī)定過濾方法

實(shí)現(xiàn)接口,可以依需要重寫過濾方法,參數(shù)傳遞的一般是存儲過濾類的容器類

復(fù)雜過濾類可以通過設(shè)置傳遞接口參數(shù)(復(fù)用其他基礎(chǔ)過濾類)來實(shí)現(xiàn)多重過濾

Java8

Java8中的lambda表達(dá)式可以更簡單的實(shí)現(xiàn)過濾器

List movies = Stream.of(
                new Movie("大話西游","comedy"),
                new Movie("泰囧", "comedy"),
                new Movie("禁閉島", "suspense"))
                .filter(var -> "comedy".equals(var.getType()))
                .collect(Collectors.toList());
3.8.2 電影的例子

創(chuàng)建被過濾的類Movie,根據(jù)它的type屬性實(shí)現(xiàn)過濾

創(chuàng)建接口Criteria,規(guī)定過濾方法

創(chuàng)建喜劇電影過濾器ComedyMovieCriteria,根據(jù)comedy==movie.type來過濾出需要的喜劇電影

public class Test {
    public static void main(String[] args) {
        List movies = new ArrayList(){{
            add(new Movie("大話西游","comedy"));
            add(new Movie("泰囧", "comedy"));
            add(new Movie("禁閉島", "suspense"));
        }};
        System.out.println(new ComedyMovieCriteria().meetCriteria(movies));
    }
}

// 被篩選的對象
class Movie {
    private String name;
    // 電影類型
    private String type;

    public Movie(String name, String type) {
        this.name = name;
        this.type = type;
    }
    // getters & setters & toString
}

// 過濾器接口
interface Criteria {
    /**
     * @param movies 要被篩選的電影
     * @return 篩選后的結(jié)果
     */
    List meetCriteria(List movies);
}

// 過濾喜劇電影的過濾器,要求是movie.type==comedy
class ComedyMovieCriteria implements Criteria {
    @Override
    public List meetCriteria(List movies) {
        List result = new ArrayList<>();
        for (Movie movie : movies) {
            if ("comedy".equals(movie.getType())) {
                result.add(movie);
            }
        }
        return result;
    }
}
3.9 橋接模式

橋接模式是將抽象化實(shí)現(xiàn)化解耦,使兩者可以獨(dú)立地變化。橋接模式有助于理解面向?qū)ο蟮脑O(shè)計(jì)原則,包括開閉原則以及組合聚合復(fù)用原則

3.9.1 橋接模式結(jié)構(gòu)

這個系統(tǒng)含有兩個等級結(jié)構(gòu)

由抽象化角色和修正抽象化角色組成的抽象化等級結(jié)構(gòu)。

由實(shí)現(xiàn)化角色和兩個具體實(shí)現(xiàn)化角色所組成的實(shí)現(xiàn)化等級結(jié)構(gòu)。

橋接模式所涉及的角色

抽象化(Abstraction)角色:抽象化給出的定義,并保存一個對實(shí)現(xiàn)化對象的引用。

修正抽象化(Refined Abstraction)角色:擴(kuò)展抽象化角色,改變和修正父類對抽象化的定義。

實(shí)現(xiàn)化(Implementor)角色:這個角色給出實(shí)現(xiàn)化角色的接口,但不給出具體的實(shí)現(xiàn)。必須指出的是,這個接口不一定和抽象化角色的接口定義相同,實(shí)際上,這兩個接口可以非常不一樣。實(shí)現(xiàn)化角色應(yīng)該只給出底層操作,而抽象化角色應(yīng)該只給出基于底層操作的更高一層的操作。

具體實(shí)現(xiàn)化(Concrete Implementor)角色:這個角色給出實(shí)現(xiàn)化角色接口的具體實(shí)現(xiàn)。

3.9.2 細(xì)節(jié)
抽象化、實(shí)現(xiàn)化、解耦

抽象化:存在于多個實(shí)體中的共同的概念性聯(lián)系;通過忽略一些信息,把不同的實(shí)體當(dāng)作相同的實(shí)體來對待。
實(shí)現(xiàn)化:抽象化給出的具體實(shí)現(xiàn)就是實(shí)現(xiàn)化。一個類的實(shí)例就是這個類的實(shí)現(xiàn)化,一個子類就是它超類的實(shí)現(xiàn)化。
解耦:耦合就是兩個實(shí)體的某種強(qiáng)關(guān)聯(lián),把它們的強(qiáng)關(guān)聯(lián)去掉就是解耦。
強(qiáng)關(guān)聯(lián)與弱關(guān)聯(lián):所謂強(qiáng)關(guān)聯(lián),就是在編譯期已經(jīng)確定的,無法在運(yùn)行期動態(tài)改變的關(guān)聯(lián);所謂弱關(guān)聯(lián),就是可以動態(tài)地確定并且可以在運(yùn)行期動態(tài)地改變的關(guān)聯(lián)。繼承是強(qiáng)關(guān)聯(lián),而聚合關(guān)系是弱關(guān)聯(lián)。

核心理解

橋接模式中的脫耦,就是在抽象化和實(shí)現(xiàn)化之間使用組合關(guān)系而不是繼承關(guān)系,從而使兩者可以相對獨(dú)立的變化。

優(yōu)點(diǎn)

實(shí)現(xiàn)抽象化和實(shí)現(xiàn)化的分離。

提高了代碼的擴(kuò)展能力。

實(shí)現(xiàn)細(xì)節(jié)對客戶透明。

缺點(diǎn)

橋接模式的引入會增加系統(tǒng)的理解與設(shè)計(jì)難度

由于聚合關(guān)聯(lián)關(guān)系建立在抽象層,要求開發(fā)者針對抽象進(jìn)行設(shè)計(jì)與編程。

使用場景

如果一個系統(tǒng)需要在構(gòu)件的抽象化角色和具體化角色之間增加更多的靈活性,避免在兩個層次之間建立靜態(tài)的繼承聯(lián)系,通過橋接模式可以使它們在抽象層建立一個關(guān)聯(lián)關(guān)系。

抽象化角色和實(shí)現(xiàn)化角色可以以繼承的方式獨(dú)立擴(kuò)展而互不影響,在程序運(yùn)行時可以動態(tài)將一個抽象化子類的對象和一個實(shí)現(xiàn)化子類的對象進(jìn)行組合,即系統(tǒng)需要對抽象化角色和實(shí)現(xiàn)化角色進(jìn)行動態(tài)耦合。

一個類存在兩個獨(dú)立變化的維度,且這兩個維度都需要進(jìn)行擴(kuò)展。

雖然在系統(tǒng)中使用繼承是沒有問題的,但是由于抽象化角色和具體化角色需要獨(dú)立變化,設(shè)計(jì)要求需要獨(dú)立管理這兩者。

對于那些不希望使用繼承或因?yàn)槎鄬哟卫^承導(dǎo)致系統(tǒng)類的個數(shù)急劇增加的系統(tǒng),橋接模式尤為適用

Java例子

大多數(shù)的驅(qū)動器(Driver)都是橋接模式的應(yīng)用,使用驅(qū)動程序的應(yīng)用系統(tǒng)就是抽象化角色,而驅(qū)動器本身扮演實(shí)現(xiàn)化角色。

3.9.3 發(fā)送消息的案例

下面案例中,SendMsg及其子類是按照發(fā)送消息的方式進(jìn)行擴(kuò)展的;而Send是按照發(fā)送消息的時間進(jìn)行擴(kuò)展的,兩者互不影響。

Send持有類一個SendMsg對象,并可以使用此對象的方法。

// Implementor角色
interface SendMsg {
    void sendMsg();
}

// Concrete Implementor角色
class EmailSendMsg implements SendMsg {
    @Override
    public void sendMsg() {
        System.out.println("Send Msg By Email");
    }
}

// Concrete Implementor角色
class WeChatSendMsg implements SendMsg {
    @Override
    public void sendMsg() {
        System.out.println("Send Msg By WeChat");
    }
}

// Abstraction 角色
abstract class Send {
    protected SendMsg sendMsg;

    public Send(SendMsg sendMsg) {
        this.sendMsg = sendMsg;
    }

    public abstract void send();
}

// Concrete Implementor角色
class ImmediatelySend extends Send {
    public ImmediatelySend(SendMsg sendMsg) {
        super(sendMsg);
    }

    @Override
    public void send() {
        sendMsg.sendMsg();
        System.out.println("Send Msg Immediately");
    }
}

// Concrete Implementor角色
class DelayedlySend extends Send {
    public DelayedlySend(SendMsg sendMsg) {
        super(sendMsg);
    }

    @Override
    public void send() {
        sendMsg.sendMsg();
        System.out.println("Send Msg DelayedlySend");
    }
}

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/72495.html

相關(guān)文章

  • Java設(shè)計(jì)模式概述

    摘要:設(shè)計(jì)模式的類別設(shè)計(jì)模式一共分為種類型,共種。屬于結(jié)構(gòu)型的設(shè)計(jì)模式適配器模式橋接模式裝飾模式組合模式外觀模式享元模式代理模式。問題描述了應(yīng)該在何時使用設(shè)計(jì)模式。解決方案描述了設(shè)計(jì)的組成成分,它們之間的相互關(guān)系及各自的職責(zé)和協(xié)作方式。 設(shè)計(jì)模式概述 1. 設(shè)計(jì)模式是什么 我們在平時編寫代碼的過程中,會遇到各種各樣的問題,細(xì)想一下很多問題的解決思路大致一樣的,這時候你就可以把解決問題的思路整...

    leon 評論0 收藏0
  • 【編程課堂】php設(shè)計(jì)模式(二):結(jié)構(gòu)型模式(續(xù))

    摘要:能夠協(xié)調(diào)調(diào)用者和被調(diào)用者,能夠在一定程度上降低系統(tǒng)的耦合性。特點(diǎn)低耦合性,獨(dú)立性好,安全性應(yīng)用客戶訪問不到或者被訪問者希望隱藏自己,所以通過代理來訪問自己。 我們接著上面的幾種模式繼續(xù)講: 4、組合模式 將對象組合成樹形結(jié)構(gòu)表示部分-整體的層次結(jié)構(gòu)。 特點(diǎn):靈活性強(qiáng) 應(yīng)用:對象的部分-整體的層次結(jié)構(gòu),模糊組合對象和簡單對象處理問題 代碼實(shí)現(xiàn) /** 組合模式* *///繼承模式clas...

    Nekron 評論0 收藏0
  • Learning PHP —— 設(shè)計(jì)模式 | Chap2:設(shè)計(jì)模式與UML

    摘要:設(shè)計(jì)模式設(shè)計(jì)模式基本原則設(shè)計(jì)原則按接口而不是按實(shí)現(xiàn)來編程按接口而不是按實(shí)現(xiàn)編程是指,要將變量設(shè)置為一個抽象類或接口數(shù)據(jù)類型的實(shí)例,而不是一個具體實(shí)現(xiàn)的實(shí)例。例如父類的一個改變會逐級向下傳遞給子類實(shí)現(xiàn),這可能會影響子類使用的某個算法。 設(shè)計(jì)模式 設(shè)計(jì)模式基本原則 設(shè)計(jì)原則 ① : 按接口而不是按實(shí)現(xiàn)來編程 按接口而不是按實(shí)現(xiàn)編程是指,要將變量設(shè)置為一個抽象類或接口數(shù)據(jù)類型的實(shí)例,而不是一...

    senntyou 評論0 收藏0
  • Java學(xué)習(xí)路線總結(jié),搬磚工逆襲Java架構(gòu)師(全網(wǎng)最強(qiáng))

    摘要:哪吒社區(qū)技能樹打卡打卡貼函數(shù)式接口簡介領(lǐng)域優(yōu)質(zhì)創(chuàng)作者哪吒公眾號作者架構(gòu)師奮斗者掃描主頁左側(cè)二維碼,加入群聊,一起學(xué)習(xí)一起進(jìn)步歡迎點(diǎn)贊收藏留言前情提要無意間聽到領(lǐng)導(dǎo)們的談話,現(xiàn)在公司的現(xiàn)狀是碼農(nóng)太多,但能獨(dú)立帶隊(duì)的人太少,簡而言之,不缺干 ? 哪吒社區(qū)Java技能樹打卡?【打卡貼 day2...

    Scorpion 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<