溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何使用結(jié)構(gòu)型模式

發(fā)布時(shí)間:2021-10-12 09:40:36 來源:億速云 閱讀:92 作者:iii 欄目:編程語言

這篇文章主要講解了“如何使用結(jié)構(gòu)型模式”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“如何使用結(jié)構(gòu)型模式”吧!

適配器模式

所謂適配器模式,就是把一個(gè)類的結(jié)構(gòu)轉(zhuǎn)換成另外一個(gè)類的接口。使得原本由于接口不兼容而不能工作的類都能夠一起工作。

在生活中比較常見的就是當(dāng)我們想連接顯示器的時(shí)候,vga需要轉(zhuǎn)成hdmi,還有電源適配,比如可能需要220v的充電頭,但是只有110v的充電頭,那么就需要將220v的充電頭適配成110v的充電頭。

Adaptee: 需要適配的就代碼,舊接口

Adapter: 將調(diào)用轉(zhuǎn)發(fā)給Adaptee 的適配器類

Target: 支持的新接口

適配器模式UML

如何使用結(jié)構(gòu)型模式

類適配器模式

Target

public interface Target {
    void output110v();
}

Adaptee

public class Adaptee {
    public void output220v() {
        System.out.println("輸出電壓");
    }
}

Adapter

public class Adapter extends Adaptee implements Target {
    @Override
    public void output110v() {
        this.output220v();
    }
}

這里需要注意的是Adapter繼承了源類而實(shí)現(xiàn)了目標(biāo)類

Client

public class Client {
    public static void main(String[] args) {
        Target target = new Adapter();
        target.output110v();
    }
}

雖然我們是使用了output110v的充電頭,但是經(jīng)過Adapter后,最終通過this.output220v() 會調(diào)用output220v的充電頭,這就是把output220v適配成了output110v。

如何使用結(jié)構(gòu)型模式

對象適配器模式

其實(shí)說得這么高大上,其實(shí)就是Adaptor 的實(shí)現(xiàn)方式不同,類適配器模式采用繼承了源類(也就是需要適配的類)實(shí)現(xiàn)了目標(biāo)類。

這樣就存在一個(gè)問題,當(dāng)我們需要適配多個(gè)類的時(shí)候就會出現(xiàn)問題,因?yàn)閖ava中是允許實(shí)現(xiàn)多個(gè)接口,但是只能繼承一個(gè)類。

為了解決這個(gè)問題,我們可以把需要適配的類作為Adapter 的成員變量,然后通過構(gòu)造函數(shù)進(jìn)行適配

public class NewAdapter implements Target{
    Adaptee adaptee;

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

    @Override
    public void output110v() {
        adaptee.output220v();
    }
}

代理模式


代理模式也算是比較常用的設(shè)計(jì)模式之一,大家接觸最多的spring aop,就是采用動態(tài)代理模式來完成的。代理模式可以分為普通代理模式,強(qiáng)制代理模式,動態(tài)代理模式,也是本文著重講解的,當(dāng)然還有其代理模式。

先來用一段代碼來體會代理模式,場景是玩家打游戲。

Subject: 共同接口,客戶端使用的現(xiàn)有接口

RealSubject: 真實(shí)對象的類

ProxySubject: 代理對象類

代理模式UML

如何使用結(jié)構(gòu)型模式

普通代理模式


通過上面的例子,相信對大家都對代理模式有點(diǎn)感覺了,但是好像又不那么恰當(dāng),上面的測試類種,還是需要新建一個(gè)player,這就相當(dāng)于什么,相當(dāng)于我們是在手機(jī)上登錄了游戲,然后再把手機(jī)給代練者代練。而事實(shí)上,經(jīng)常是把賬號密碼給代練即可。

這就引出了普通代理模式,客戶端不能訪問真實(shí)角色,只能訪問代理角色,我們能夠知道代理的存在。

如何使用結(jié)構(gòu)型模式

Subject

public interface SubjectNormalGamePlayer {
    public void login();
    public void upgrade();
    public void matches();
}

RealSubject

public class RealSubjectNormalPlayerImpl implements SubjectNormalGamePlayer {
    private String name;
    private String password;

    public RealSubjectNormalPlayerImpl(String name, String password) {
        this.name = name;
        this.password = password;
    }

    @Override
    public void login() {
        if(name.equals("cutey") && password.equals("123456")) {
            System.out.println(name + "登錄成功");
        }
    }

    @Override
    public void upgrade() {  
        System.out.println(name + "升級");
    }

    @Override
    public void matches() {
        System.out.println(name + "打排位賽");
    }
}

ProxySubject

public class ProxySubjectNormalPlayerImpl implements SubjectNormalGamePlayer {
    SubjectNormalGamePlayer gamePlayer;

    //注意這里的區(qū)別,我們是拿賬號和密碼去登錄真實(shí)角色
    //并不是直接拿真實(shí)角色的手機(jī)來打
    public ProxySubjectNormalPlayerImpl(String name, String password) {
        gamePlayer = new RealSubjectNormalPlayerImpl(name, password);
    }

    @Override
    public void login() {
        System.out.print("代練:");
        gamePlayer.login();
    }

    @Override
    public void upgrade() {
        System.out.print("代練:");
        gamePlayer.upgrade();
    }

    @Override
    public void matches() {
        System.out.print("代練:");
        gamePlayer.matches();
    }
}

Client

public class Client {
    public static void main(String[] args) {
        //通過測試類也很明顯區(qū)別,不必要顯示構(gòu)造真實(shí)角色類
        SubjectNormalGamePlayer proxy = new ProxySubjectNormalPlayerImpl("cutey", "123456");
        proxy.login();
        proxy.upgrade();
        proxy.matches();
    }
}

強(qiáng)制代理


普通代理呢是去找到一個(gè)代理對象幫打,但是強(qiáng)制代理呢,主要是體現(xiàn)在“強(qiáng)制”,角色類會指定一個(gè)代理,其它方式找來的代理不能幫我打,一定要用我指定的代理才行。

Subject

和普通代理模式大部分代碼一樣,不同的是加了一個(gè)強(qiáng)制指定代理對象

public interface SubjectForceGamePlayer {
   	//省略登錄、升級和打排位等方法
    
    //強(qiáng)制指定代理對象
    public SubjectForceGamePlayer getForceProxy();
}

RealSubject

public class ForceGamePlayerImpl implements IForceGamePlayer {
    private String name;
    private String password;

    private IForceGamePlayer proxy = null;      //指定需要誰來代理


    public ForceGamePlayerImpl(String name, String password) {
        this.name = name;
        this.password = password;
    }
    
    @Override
    public IForceGamePlayer getForceProxy() {
        //強(qiáng)制指定代理類,并且只有這樣才能給proxy賦值
        proxy =  new ForceProxyGamePlayerImpl(this);
        return proxy;
    }

    @Override
    public void login() {
        //只要不是自己指定的proxy,其它方式proxy肯定是null
        if(proxy != null) {
            if(name.equals("imperfect") && password.equals("123456")) {
                System.out.println(name + "登錄成功");
            }
        } else {
            System.out.println("需要代理");
        }

    }

    @Override
    public void upgrade() {
        if(proxy != null) {
            System.out.println(name + "升級");
        } else {
            System.out.println("需要代理");
        }

    }

    @Override
    public void matches() {
        if(proxy != null) {
            System.out.println(name + "打排位賽");
        } else {
            System.out.println("需要代理");
        }

    }
}

ProxySubject

public class ProxySubjectForcePlayerImpl implements SubjectForceGamePlayer {

    private SubjectForceGamePlayer gamePlayer;

	//接收被代理對象的指定
    public ProxySubjectForcePlayerImpl(SubjectForceGamePlayer gamePlayer) {
        this.gamePlayer = gamePlayer;
    }


    //省略登錄、升級和打比賽的方法

    //沒有代理對象,暫時(shí)返回自己
    @Override
    public SubjectForceGamePlayer getForceProxy() {
        return this;
    }

}

Client

public class Client {
    public static void main(String[] args) {
        wrongProxy1();
        wrongProxy2();
        correctProxy();
    }

    public static void correctProxy() {
        SubjectForceGamePlayer player = new RealSubjectForcePlayerImpl("cutey", "123456");

        //你這個(gè)代理必須是我指定的,并且強(qiáng)制要有
        SubjectForceGamePlayer proxy = player.getForceProxy();
        proxy.login();
        proxy.upgrade();
        proxy.matches();
    }

    public static void wrongProxy1() {
        SubjectForceGamePlayer player = new RealSubjectForcePlayerImpl("cutey", "123456");
        SubjectForceGamePlayer proxy = new ProxySubjectForcePlayerImpl(player);
        proxy.login();
        proxy.upgrade();
        proxy.matches();
    }

    public static void wrongProxy2() {
        SubjectForceGamePlayer player = new RealSubjectForcePlayerImpl("cutey", "123456");
        player.login();
        player.upgrade();
        player.matches();
    }
}

動態(tài)代理


這個(gè)應(yīng)該是代理模式中用的比較多的,也是我覺得最需要各位小伙伴理解并且掌握的。所謂動態(tài)代理是指,不用在編譯器指定為誰代理,而是在運(yùn)行期再獲得被代理的對象并且執(zhí)行代理的方法。

下面將要講的例子是利用jdk中提供的InvocationHandler和Proxy類

Subject 、RealSubject 都和普通代理模式一樣

ProxySubject

我們不知道要給誰代理,所以要用到的是繼承InvocationHandler類

public class ProxySubjectDynamicPlayerImpl implements InvocationHandler {
    Class cls = null;	//要代理的類
    Object obj = null;	//需要代理的對象

    //指定需要代理的對象
    public ProxySubjectDynamicPlayerImpl(Object obj) {
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //調(diào)用代理對象的方法
        Object result = method.invoke(this.obj, args);
        if(method.getName().equalsIgnoreCase("login")) {
            System.out.println("異地登陸提醒:有人登錄我的賬戶");
        }
        return result;
    }
}

Client

public class Client {
    public static void main(String[] args) {
        SubjectDynamicPlayer player = new RealSubjectDynamicPlayerImpl("imperfect", "123456");

        //把需要代理的信息交給handler,還記得invoke方法嗎
        //在invoke方法中已經(jīng)實(shí)現(xiàn)了被代理對象的方法
        InvocationHandler handler = new ProxySubjectDynamicPlayerImpl(player);

        //獲取被代理類的類加載屬性
        ClassLoader cl = player.getClass().getClassLoader();

        //獲取被代理類的接口
        Class[] interfaces = player.getClass().getInterfaces();

        //把上述的三個(gè)信息交給Proxy創(chuàng)建出一個(gè)代理類
        SubjectDynamicPlayer proxy = (SubjectDynamicPlayer) Proxy.newProxyInstance(cl, interfaces, handler);

        proxy.login();
        proxy.upgrade();
        proxy.matches();
    }
}

在編譯時(shí)我們是完全不知道給誰代理,一切都是在運(yùn)行時(shí)才知道,這就是“動態(tài)”

裝飾模式

裝飾模式就是動態(tài)地給一個(gè)對象添加一些恩愛的職責(zé),就增加功能來說,裝飾模式比生成子類更為靈活。

無論干什么,最重要的都是揚(yáng)長避短,對于賣手機(jī)也是如此,肯定是把賣點(diǎn)詳細(xì)地介紹,而對于缺點(diǎn)能不提就不提。

Component: 定義一個(gè)對象的接口,可以給這些對象動態(tài)地添加職責(zé)

ConcreteComponent: 具體的對象

Decorator: 裝飾抽象類,繼承了Component,從外類來擴(kuò)展Component

ConcentrateDecorator: 具體的裝飾類

裝飾模式UML

如何使用結(jié)構(gòu)型模式

裝飾模式

Component

public abstract class ComponentMobile {
    //產(chǎn)品名字
    private String name;

    //省略get,set,tostring方法

    public abstract void showDetails();

    public abstract void onSale(String userName);

}

Concentrate Component

public class ConcreteComponentOnePlus extends ComponentMobile {

    public ConcreteComponentOnePlus(String name) {
        super(name);
    }

    @Override
    public void showDetails() {
        System.out.println("處理器:驍龍888 \r\n拍照:哈蘇專業(yè)模式 \r\n屏幕:2k+120hz 柔性屏 \r\n充電:65w快充");
    }

    @Override
    public void onSale(String userName) {
        System.out.println(userName + "購買了" + getName());
    }
}

Decorator

public abstract class Decorator extends ComponentMobile {

    //把要裝飾的手機(jī)拿給我
    private ComponentMobile mobile;

    public Decorator(String name, ComponentMobile mobile) {
        super(name);
        this.mobile = mobile;
    }

	//細(xì)節(jié)還是要展示的
    //只不過怎么展示呢,子類可以加以修飾
    public void showDetails() {
        mobile.showDetails();
    }

    //手機(jī)也是要出售的
    public void onSale(String name) {
        mobile.onSale(name);
    }

}

注意,我們手機(jī)的細(xì)節(jié)還是要展示的,不能說做的不好就不說出來,欺騙消費(fèi)者。能把你認(rèn)出來叫化妝,不能把你認(rèn)出來叫整容,我們講的是裝飾模式,不是整容模式。

如何使用結(jié)構(gòu)型模式

Concrete Decorator

public class ConcreteDecoratorSystem extends Decorator {

    public ConcreteDecoratorSystem(String name, ComponentMobile mobile) {
        super(name, mobile);
    }

    //裝飾系統(tǒng)
    public void decorateScreen() {
        System.out.println("出廠配備了ColorOS,其它型號的手機(jī)也會逐步適配");

    }

    @Override
    public void showDetails() {
        //想先介紹了系統(tǒng),再說其他參數(shù)
        decorateScreen();
        super.showDetails();
    }
}
public class ConcreteDecoratorPrice extends Decorator {


    public ConcreteDecoratorPrice(String name, ComponentMobile mobile) {
        super(name, mobile);
    }

    //公布價(jià)格
    public void decoratePrice() {
        System.out.println("8 + 128:4999");
        System.out.println("8 + 256: 5499");
    }

    @Override
    public void showDetails() {
        super.showDetails();
        //介紹完其它的后,公布性價(jià)比較高的價(jià)格
        decoratePrice();
    }
}

Client

public class Client {
    public static void main(String[] args) {
        //手機(jī)發(fā)布會,原產(chǎn)品
        ComponentMobile mobile = new ConcreteComponentOnePlus("OnePlus 9 Pro");
        //裝飾下系統(tǒng)
        mobile = new ConcreteDecoratorSystem(mobile.getName(), mobile);
        //裝飾下價(jià)格
        mobile = new ConcreteDecoratorPrice(mobile.getName(), mobile);
        mobile.showDetails();

        //用戶一看,誒,不錯(cuò)不錯(cuò),買了
        mobile.onSale("cutey");
    }
}

感謝各位的閱讀,以上就是“如何使用結(jié)構(gòu)型模式”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對如何使用結(jié)構(gòu)型模式這一問題有了更深刻的體會,具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識點(diǎn)的文章,歡迎關(guān)注!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI