java設(shè)計(jì)模式-組合模式詳解
組合模式
組合模式(Composite Pattern)又叫部分整體模式,是用于把一組相似的對(duì)象當(dāng)作一個(gè)單一的對(duì)象。組合模式依據(jù)樹形結(jié)構(gòu)來組合對(duì)象,用來表示部分以及整體層次。這種類型的設(shè)計(jì)模式屬于結(jié)構(gòu)型模式,它創(chuàng)建了對(duì)象組的樹形結(jié)構(gòu)。
- 主要解決:它在我們樹型結(jié)構(gòu)的問題中,模糊了簡(jiǎn)單元素和復(fù)雜元素的概念,客戶程序可以像處理簡(jiǎn)單元素一樣來處理復(fù)雜元素,從而使得客戶程序與復(fù)雜元素的內(nèi)部結(jié)構(gòu)解耦。
- 如何解決:樹枝和葉子實(shí)現(xiàn)統(tǒng)一接口,樹枝內(nèi)部組合該接口。
- 何時(shí)使用:
1.您想表示對(duì)象的部分-整體層次結(jié)構(gòu)(樹形結(jié)構(gòu))。
2.您希望用戶忽略組合對(duì)象與單個(gè)對(duì)象的不同,用戶將統(tǒng)一地使用組合結(jié)構(gòu)中的所有對(duì)象。
- 使用場(chǎng)景:部分、整體場(chǎng)景,如樹形菜單,文件、文件夾的管理。
優(yōu)點(diǎn) | 缺點(diǎn) |
---|---|
高層模塊調(diào)用簡(jiǎn)單,節(jié)點(diǎn)自由增加。 | 葉子和樹枝的聲明都是實(shí)現(xiàn)類,而不是接口,違反了依賴倒置原則。 |
文件夾-文件的樹形結(jié)構(gòu)一定不陌生,文件看作葉子結(jié)點(diǎn)(單個(gè)對(duì)象),文件夾看作中間結(jié)點(diǎn)(組合對(duì)象)。
組合模式使得用戶對(duì)單個(gè)對(duì)象和組合對(duì)象的訪問具有一致性,即讓用戶以一致的方式處理個(gè)別對(duì)象以及組合對(duì)象。避免在使用過程中區(qū)分開來,造成麻煩。
- Component :組合中的對(duì)象聲明接口,用于訪問和管理Component子部件。
- Leaf:?jiǎn)蝹€(gè)/葉子對(duì)象,葉子結(jié)點(diǎn)沒有子結(jié)點(diǎn)。
- Composite:組合/容器對(duì)象,存儲(chǔ)子部件和枝節(jié)點(diǎn)行為,實(shí)現(xiàn)與子部件有關(guān)操作,如增加(add)和刪除(remove)等,list實(shí)現(xiàn)容器,容納Component對(duì)象。
Demo
先來看看一般的寫法:
當(dāng)用戶只滿足一種折扣方案時(shí),這種方法還能應(yīng)對(duì)。
但精打細(xì)算的我們往往是同時(shí)滿足多種折扣方案,這時(shí)就可以用組合模式,把這些單個(gè)折扣方案組合容納起來,然后定義解決折扣沖突策略。實(shí)現(xiàn)單個(gè)對(duì)象和組合對(duì)象的統(tǒng)一,讓調(diào)用者使用時(shí)不必在區(qū)分。
把組合對(duì)象CompositeDiscount定義成抽象類,SingleMinStrategy和MultipleStrategy繼承它,表示解決沖突的策略,分別是取最小折扣和取折上折。
一般解決折扣沖突都是折上折,但商家往往更精明,推出互斥券之類的,即取最小折扣。也可以自定義其他折扣沖突策略。
涉及了點(diǎn)工廠模式和策略模式,DiscountFactory就是實(shí)例化Order類的屬性DiscountStrategy的工廠,各種折扣策略實(shí)現(xiàn)同一接口。
代碼:
public interface DiscountStrategy { public double getTotal(double price); } public class VIPDiscount implements DiscountStrategy { //95折 @Override public double getTotal(double price) { return 0.95*price; } } public class ActivityDiscount implements DiscountStrategy{ //9折 @Override public double getTotal(double price) { return 0.9*price; } } public class StoreDiscount implements DiscountStrategy{ //滿500超出部分打6折 @Override public double getTotal(double price) { return 500+0.6*(price-500); } }
public abstract class CompositeDiscount implements DiscountStrategy { protected List<DiscountStrategy> strategies =new ArrayList(); //容器 public void add(DiscountStrategy discountStrategy){ //添加葉子結(jié)點(diǎn) strategies.add(discountStrategy); } @Override public double getTotal(double price) { return price; } } //多種折扣選最低折扣 public class SingleMinStrategy extends CompositeDiscount { @Override public double getTotal(double price) { double rtn=price; for (DiscountStrategy s: strategies) { rtn=Math.min(rtn,s.getTotal(price)); } return rtn; } } //多種折扣用折上折 public class MultipleStrategy extends CompositeDiscount { @Override public double getTotal(double price) { double rtn = price; for (DiscountStrategy s : strategies) { rtn = s.getTotal(rtn); } return rtn; } }
public class DiscountFactory { public DiscountStrategy create(String type){ //工廠來創(chuàng)建相應(yīng)策略 //單一折扣策略 if("ynn".equals(type))return new VIPDiscount(); else if("nyn".equals(type))return new StoreDiscount(); else if("nny".equals(type))return new ActivityDiscount(); else{ //多種折扣策略 CompositeDiscount compositeDiscount; System.out.println("請(qǐng)選擇沖突解決方案:1.折上折 2.最低折"); Scanner scanner=new Scanner(System.in); int type2=scanner.nextInt(); if(type2==1){ compositeDiscount=new MultipleStrategy(); } else{ compositeDiscount=new SingleMinStrategy(); } if(type.charAt(1)=='y')compositeDiscount.add(new StoreDiscount()); if(type.charAt(0)=='y')compositeDiscount.add(new VIPDiscount()); if(type.charAt(2)=='y')compositeDiscount.add(new ActivityDiscount()); return compositeDiscount; } } }
public class Order { public double price; private String type; public DiscountStrategy discountStrategy; public Order(double price) { this.price=price; } public void display(){ System.out.println("總價(jià):"+price); System.out.println("是否是VIP?y/n"); Scanner scanner=new Scanner(System.in); type=scanner.next(); System.out.println("是否超過500?y/n"); String tmp; tmp=scanner.next(); type+=tmp; System.out.println("是否滿足活動(dòng)價(jià)?y/n"); tmp=scanner.next(); type+=tmp; DiscountFactory discountFactory=new DiscountFactory(); double discountPrice=discountFactory.create(type).getTotal(price); System.out.println("優(yōu)惠:"+(price-discountPrice)); System.out.println("應(yīng)付:"+discountPrice); } } public class Client { public static void main(String[] args) { Order order=new Order(620); order.display(); } }
運(yùn)行結(jié)果:
這樣一來,無論是單一折扣還是多種折扣,客戶端使用時(shí)都是一個(gè)用法,不必區(qū)分和操心。
總結(jié)
本篇文章就到這里了,希望能給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
Spring中BeanFactory和ApplicationContext的作用和區(qū)別(推薦)
這篇文章主要介紹了Spring中BeanFactory和ApplicationContext的作用和區(qū)別,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09Java 1.8使用數(shù)組實(shí)現(xiàn)循環(huán)隊(duì)列
這篇文章主要為大家詳細(xì)介紹了Java 1.8使用數(shù)組實(shí)現(xiàn)循環(huán)隊(duì)列,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-10-10IDEA:Error running,Command line is too&n
這篇文章主要介紹了IDEA:Error running,Command line is too long.解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07Spring boot中PropertySource注解的使用方法詳解
這篇文章主要給大家介紹了關(guān)于Spring boot中PropertySource注解的使用方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Spring boot具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧。2017-12-12Java字符串技巧之刪除標(biāo)點(diǎn)或最后字符的方法
這篇文章主要介紹了Java字符串技巧之刪除標(biāo)點(diǎn)或最后字符的方法,是Java入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-11-11