java設計模式-組合模式詳解
組合模式
組合模式(Composite Pattern)又叫部分整體模式,是用于把一組相似的對象當作一個單一的對象。組合模式依據(jù)樹形結構來組合對象,用來表示部分以及整體層次。這種類型的設計模式屬于結構型模式,它創(chuàng)建了對象組的樹形結構。
- 主要解決:它在我們樹型結構的問題中,模糊了簡單元素和復雜元素的概念,客戶程序可以像處理簡單元素一樣來處理復雜元素,從而使得客戶程序與復雜元素的內(nèi)部結構解耦。
- 如何解決:樹枝和葉子實現(xiàn)統(tǒng)一接口,樹枝內(nèi)部組合該接口。
- 何時使用:
1.您想表示對象的部分-整體層次結構(樹形結構)。
2.您希望用戶忽略組合對象與單個對象的不同,用戶將統(tǒng)一地使用組合結構中的所有對象。
- 使用場景:部分、整體場景,如樹形菜單,文件、文件夾的管理。
| 優(yōu)點 | 缺點 |
|---|---|
| 高層模塊調(diào)用簡單,節(jié)點自由增加。 | 葉子和樹枝的聲明都是實現(xiàn)類,而不是接口,違反了依賴倒置原則。 |

文件夾-文件的樹形結構一定不陌生,文件看作葉子結點(單個對象),文件夾看作中間結點(組合對象)。
組合模式使得用戶對單個對象和組合對象的訪問具有一致性,即讓用戶以一致的方式處理個別對象以及組合對象。避免在使用過程中區(qū)分開來,造成麻煩。

- Component :組合中的對象聲明接口,用于訪問和管理Component子部件。
- Leaf:單個/葉子對象,葉子結點沒有子結點。
- Composite:組合/容器對象,存儲子部件和枝節(jié)點行為,實現(xiàn)與子部件有關操作,如增加(add)和刪除(remove)等,list實現(xiàn)容器,容納Component對象。
Demo
先來看看一般的寫法:

當用戶只滿足一種折扣方案時,這種方法還能應對。
但精打細算的我們往往是同時滿足多種折扣方案,這時就可以用組合模式,把這些單個折扣方案組合容納起來,然后定義解決折扣沖突策略。實現(xiàn)單個對象和組合對象的統(tǒng)一,讓調(diào)用者使用時不必在區(qū)分。

把組合對象CompositeDiscount定義成抽象類,SingleMinStrategy和MultipleStrategy繼承它,表示解決沖突的策略,分別是取最小折扣和取折上折。
一般解決折扣沖突都是折上折,但商家往往更精明,推出互斥券之類的,即取最小折扣。也可以自定義其他折扣沖突策略。
涉及了點工廠模式和策略模式,DiscountFactory就是實例化Order類的屬性DiscountStrategy的工廠,各種折扣策略實現(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){ //添加葉子結點
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)建相應策略
//單一折扣策略
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("請選擇沖突解決方案: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("總價:"+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("是否滿足活動價?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("應付:"+discountPrice);
}
}
public class Client {
public static void main(String[] args) {
Order order=new Order(620);
order.display();
}
}
運行結果:


這樣一來,無論是單一折扣還是多種折扣,客戶端使用時都是一個用法,不必區(qū)分和操心。
總結
本篇文章就到這里了,希望能給你帶來幫助,也希望您能夠多多關注腳本之家的更多內(nèi)容!
相關文章
Spring中BeanFactory和ApplicationContext的作用和區(qū)別(推薦)
這篇文章主要介紹了Spring中BeanFactory和ApplicationContext的作用和區(qū)別,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-09-09
Java 1.8使用數(shù)組實現(xiàn)循環(huán)隊列
這篇文章主要為大家詳細介紹了Java 1.8使用數(shù)組實現(xiàn)循環(huán)隊列,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-10-10
IDEA:Error running,Command line is too&n
這篇文章主要介紹了IDEA:Error running,Command line is too long.解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07
Spring boot中PropertySource注解的使用方法詳解
這篇文章主要給大家介紹了關于Spring boot中PropertySource注解的使用方法,文中通過示例代碼介紹的非常詳細,對大家學習或者使用Spring boot具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧。2017-12-12

