1. 程式人生 > >設計模式之結構性模式(二)

設計模式之結構性模式(二)

設計模式之結構性模式(二)

三、橋接模式

四、組合模式

五、裝飾模式

三、橋接模式

如果用多層繼承結構實現下圖關係,有如下問題:
1)擴充套件性問題
2)違反單一職責原則
在這裡插入圖片描述
將其分為兩個維度:
型別維度和品牌維度
在這裡插入圖片描述

Computer2

/**
 * 電腦型別
 */
package com.bridge;

public class Computer2 {

    //品牌
    protected Brand brand;
    
    public Computer2(Brand brand) {
    	this.brand = brand;
    }
    
    public void sale() {
    	brand.sale();
    }
}

//桌上型電腦
class Desktop2 extends Computer2 {

    public Desktop2(Brand brand) {
    super(brand);
    }
    
    public void sale() {
	    super.sale();
	    System.out.println("銷售臺式機");
	    }
    }
    
    //筆記本
    class Laptop2 extends Computer2 {
    
    public Laptop2(Brand brand) {
   		super(brand);
    }
    
    public void sale() {
	    super.sale();
	    System.out.println("銷售筆記本");
    }
}

//平板
class Pad2 extends Computer2 {

public Pad2(Brand brand) {
	super(brand);
}

public void sale() {
    super.sale();
    System.out.println("銷售平板");
    }
}

Brand

/**
 * 品牌型別
 */
package com.bridge;

public interface Brand {
	public void sale();
}

class Lenovo implements Brand {

@Override
public void sale() {
    System.out.println("銷售聯想電腦");
    }
}

class Dell implements Brand {

@Override
public void sale() {
    System.out.println("銷售戴爾電腦");
    }
}

Client

package com.bridge;

public class Client {
public static void main(String[] args) {
    Computer2 c = new Desktop2(new Lenovo());
    c.sale();
    }
}

結果:

銷售聯想電腦
銷售臺式機

橋接模式總結:

1)可以取代多層繼承方案,多層繼承違背了單一職責原則,複用性差,類個數多;橋接模式極大減少了子類的個數,降低管理和維護成本
2)橋接模式提高了系統的可擴充套件性,在兩個變化的維度中任意變化一個維度,都不需要修改原有的系統,符合開閉原則

橋接模式應用場景:

1)JDBC驅動程式
2)AWT中的Peer架構
3)銀行日誌管理
格式分類:操作日誌、交易日誌、異常日誌
距離分類:本地記錄日誌、異地記錄日誌
4)人力資源系統中的獎金獎金計算模組
5)OA系統中的訊息處理

四、組合模式

使用場景:把部分和整體的關係用樹形結構來表示,從而使客戶端可以使用統一的方式處理部分物件和整體物件

核心:
抽象掛件(Component)角色:定義了葉子和容器構建的共同點
葉子(Leaf)構件角色:無子節點
容器(Composite)構件角色:有容器特徵,可以包含葉子節點

組合模式工作流程分析:
1)組合模式為處理樹形結構提供了完美的解決方案,描述如何將容器和葉子進行遞迴組合,使得使用者在使用時可以一致性的對待容器和葉子。
2)當容器物件的指定方法被呼叫時,將遍歷整個樹形結構,尋找也包含這個方法的成員,並執行呼叫,其中,使用了遞迴呼叫的機制對整個結構進行處理

例項:模擬防毒軟體架構設計

Conponent

/**
 * 抽象元件
 */
package com.composite;

public interface Component {
	public void operation();
}

//葉子元件
interface Leaf extends Component {

}

//容器元件
interface Composite extends Component {
    void add(Component c);
    void remove(Component c);
    Composite getChild(int index);
}

AbstractFile

package com.composite;

import java.util.ArrayList;
import java.util.List;

//抽象構建
public interface AbstractFile {
	void killVirus();//查毒
}

//文字檔案
class TextFile implements AbstractFile {

	    private String name;
	    
	    public TextFile(String name) {
	    this.name = name;
    }
    
    @Override
    public void killVirus() {
  		  System.out.println("---文字檔案:"+this.name+",進行查殺");
    }
}

//影象檔案
class ImageFile implements AbstractFile {
    
    private String name;
    
    public ImageFile(String name) {
    	this.name = name;
    }
    
    @Override
    public void killVirus() {
   		 System.out.println("---影象檔案:"+this.name+",進行查殺");
    }
}

//視訊檔案
class VedioFile implements AbstractFile {

    private String name;
    
    public VedioFile(String name) {
   		this.name = name;
    }
    
    @Override
    public void killVirus() {
    	System.out.println("---視訊檔案:"+this.name+",進行查殺");
    }
}

//資料夾
class Folder implements AbstractFile {

    private String name;
	//容器
    private List<AbstractFile> list = new ArrayList<AbstractFile>();
    
    public Folder(String name) {
    	this.name = name;
    }
    
    //新增
    public void add(AbstractFile file) {
    	list.add(file);
    }
    
    //移除
    public void remove(AbstractFile file) {
    	list.remove(file);
    }
    
    //獲取
    public AbstractFile getChild(int index) {
   		return list.get(index);
    }
    
    @Override
    public void killVirus() {
   		System.out.println("---資料夾:"+this.name+",進行查殺");
    
    //遍歷容器
    for(AbstractFile file:list) {
	    file.killVirus();
	    }
    }
}

Client

package com.composite;

public class Client {
    public static void main(String[] args) {

    AbstractFile f1,f2,f3;//檔案
    Folder f = new Folder("我的收藏");//資料夾
    Folder f11 = new Folder("我的電影");//資料夾
    
	//檔案
    f1 = new TextFile("hello.txt");
    f2 = new ImageFile("111.jpg");
    f3 = new VedioFile("視訊");

    //將各個檔案加入到資料夾中
    f.add(f1);
    f.add(f2);
    
    f11.add(f3);
    f.add(f11);
    
    //查殺資料夾
    f.killVirus();
    }
}

結果

---資料夾:我的收藏,進行查殺
---文字檔案:hello.txt,進行查殺
---影象檔案:111.jpg,進行查殺
---資料夾:我的電影,進行查殺
---視訊檔案:視訊,進行查殺

組合模

式應用場景:
1)作業系統的資源管理器
2)GUI中的容器層次圖
2)XML檔案解析
4)OA系統中組織結構的處理
5)Junit單元測試框架
底層設計就是典型的組合模式,TestCase(葉子)、TestUnite(容器)、Test介面(抽象)

五、裝飾模式

職責:
1)動態的為一個物件增加新的功能
2)裝飾模式是一種代替繼承的技術,無需通過繼承增加子類就能擴充套件性功能,使用物件的關聯關係代替繼承關係,更加靈活,同時避免型別體系的快速膨脹

實現細節:

1)Component抽象構件角色
真實物件和裝飾物件有相同的介面,因此客戶端物件能夠以與真實物件相同的方式同裝飾物件互動
2)ConcreteComponent具體構件角色(真實物件)
io流中的FileInputStream、FileOutputStream
3)Decorator裝飾角色
持有一個抽象構件的引用,裝飾物件接受所有客戶端的請求,並把這些請求轉發給真實的物件,因此就能在真是物件呼叫後增加新的功能
4)ConcreteDecorato具體裝飾角色
負責給構件物件增加新的責任

例項:車

實現細節:

package com.decorate;

//抽象構件角色Component
public interface Icar {
	public void move();
}

//具體構件角色ConcreteComponent
class Car implements Icar {
    @Override
    public void move() {
    	System.out.println("陸地上跑");
    }
}

//Decorate裝飾角色
class SuperCar implements Icar {
    //protected供子類使用
    protected Icar car;
    
    public SuperCar(Icar car) {
	    super();
	    this.car = car;
    }
    
    @Override
    public void move() {
   		car.move();
    }
}

//具體裝飾角色ConcreteDecorate
class FlyCar extends SuperCar {

    public FlyCar(Icar car) {
    	super(car);
    }
    
    public void fly() {
    	System.out.println("天上飛");
    }
    
    @Override
    public void move() {
	    super.move();
	    fly();
    }
}

class WaterCar extends SuperCar {

    public WaterCar(Icar car) {
    	super(car);
    }
    
    public void swim() {
    System.out.println("水上游");
    }
    
    @Override
    public void move() {
	    super.move();
	    swim();
    }
}

class AICar extends SuperCar {

    public AICar(Icar car) {
    	super(car);
    }
    
    public void autoRun() {
    	System.out.println("自動跑");
    }
    
    @Override
    public void move() {
	    super.move();
	    autoRun();
    }
}

Client

package com.decorate;

public class Client {
    public static void main(String[] args) {
	    Car car = new Car();
	    car.move();
	    
	    System.out.println("------增加新的功能:天上飛------");
	    FlyCar flyCar = new FlyCar(car);
	    flyCar.move();
	    
	    System.out.println("------增加新的功能:水上游------");
	    WaterCar waterCar = new WaterCar(car);
	    waterCar.move();
	    
	    System.out.println("------增加兩個新的功能:自動天上飛------");
	    AICar aiCar = new AICar(new FlyCar(car));
	    aiCar.move();
    
    }
}

結果

陸地上跑
------增加新的功能:天上飛------
陸地上跑
天上飛
------增加新的功能:水上游------
陸地上跑
水上游
------增加兩個新的功能:自動天上飛------
陸地上跑
天上飛
自動跑

IO流實現細節:
1)Conponent抽象構件角色
io流中InputStream、OutputStream、Reader、Writer
2)ConcreteComponent具體構件角色
io流中FileInputStream、FileOutputStream
3)DEcorator裝飾角色
持有一個抽象構件的引用:io流中的FilterInoutStream、FilterOutputStream
4)ConcreteDecorator具體裝飾角色
負責給構件物件增加新的責任,io流中的BufferdInputStream、BufferedOutputStream

開發中使用場景:
1)io輸入流和輸出流的設計
2)Swing包中圖形介面構建功能
3)Servlet API中提供一個request物件的Decorator設計模式的預設實現類HttpServletrequestWrapper,增強request物件功能

總結:裝飾模式也叫包裝器模式;裝飾模式降低系統的耦合度,可以動態的增加或刪除物件職責,並使得需要裝飾的具體構件類和具體裝飾類可以獨立變化,一邊則更加新的具體構件類和具體裝飾類

優點:
1)擴充套件對物件功能,比繼承靈活,不會導致類個數急劇增加
2)可以對一個物件進行多次裝飾,創造出不同行為的組合,得到功能更加強大的物件
3)具體構件類和具體裝飾類可以獨立變化,,使用者可以根據自己需要增加新的具體構件子類和具體裝飾子類

缺點:
1)產生很多小物件,大量小物件佔據記憶體,一定程度上影響效能
2)裝飾模式易於出錯,除錯排查比較麻煩

裝飾模式和橋接模式的區別:
都是為了解決過多子類物件問題,橋接模式是物件自身現有機制沿著多個維度變化,有部分不穩定;裝飾模式是為了增加新的功能