您好,登錄后才能下訂單哦!
這篇文章主要講解了“如何使用結(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:
支持的新接口
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
。
其實(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:
代理對象類
通過上面的例子,相信對大家都對代理模式有點(diǎn)感覺了,但是好像又不那么恰當(dāng),上面的測試類種,還是需要新建一個(gè)player,這就相當(dāng)于什么,相當(dāng)于我們是在手機(jī)上登錄了游戲,然后再把手機(jī)給代練者代練。而事實(shí)上,經(jīng)常是把賬號密碼給代練即可。
這就引出了普通代理模式,客戶端不能訪問真實(shí)角色,只能訪問代理角色,我們能夠知道代理的存在。
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(); } }
普通代理呢是去找到一個(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(); } }
這個(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:
具體的裝飾類
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)出來叫整容,我們講的是裝飾模式,不是整容模式。
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)注!
免責(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)容。