java I/O系統(2)-裝飾器模式

引言

IO系統是使用了裝飾器模式的典型。所以對裝飾器模式的深入研究對IO系統的理解肯定大有裨益。在本文中會詳細介紹裝飾器模式,會用以demo展示,同時會舉出例子在IO系統中是如何呈現了這種模式,最後,我們探討一下裝飾器模式與代理模式之間的異同。筆者目前整理的一些blog針對面試都是超高頻出現的。大家可以點擊鏈接:http://blog.csdn.net/u012403290

裝飾器模式

裝飾器(Decorator )模式是指允許向一個現有的對象添加新的功能,同時又不改變其結構。一定意義上來說是繼承的替代品。
比如說一幅畫,我們給它上了一個邊框。那麼這個時候畫還是那副畫,但是它多了個邊框,這個邊框就是裝飾器給它賦予的。但是如果你要通過繼承來實現的話,就相當於是在畫上,在畫的周圍給畫上了邊框,這個時候的改變就變成了靜態的了,不夠靈活的增加和撤銷功能。
而且,還有一個極具重要的作用,比如上面說的這幅畫,畫和畫框是相互耦合的,他們兩者可以獨立發展,畫可以畫的更精妙,邊框也可以改的更好看,但是對他們組合的效果不會影響。

解決的問題

通過繼承來擴展類的功能,逐漸冗餘會產生很多的子類,或者保證原本的類的功能不變,需要額外的增加類的功能。

組織架構

下面這張圖就是裝飾器模式的設計方式:
這裏寫圖片描述
我們解釋一下每個類是幹什麼的:
1、component:是存在的一個抽象。可以是abstract類也可以是一個interface。也可以稱爲抽象被裝飾類

2、concreteComponent:真實被裝飾類,也是針對與component的具體實現。

3、Decorator:是裝飾的一個抽象,可以稱爲抽象裝飾類用abstract描述,因爲它要包含一個真實對象的引用,無法用interface表示,而且必須要有和component一樣的待實現方法。可以稱爲抽象裝飾類

4、concreteDecorator:真實裝飾類,具體的呈現對真實被裝飾類的功能進行強化。

有的小夥伴就很疑惑了。
爲什麼真實被裝飾類和抽象裝飾類都要實現component接口呢?
因爲裝飾器只是進行裝飾,東西還是原來那個東西。變形金剛可以變成人,但是改變不了他是汽車的根本,本質上來說它還是一輛車,只是多了類人的功能而已。
再者,我們需要對一個需要裝飾的對象進行多次裝飾。比如說一隻貓是一半紅色一半白色,那麼我們就需要用白色裝飾器裝飾完接着用紅色裝飾器裝飾。如果不實現這個接口,那麼就無法嵌套裝飾。

爲什麼裝飾器還需要進行抽象呢?跳過Decorator類,直接由concreteDecorator來實現Component不就好了?
的確,如果是簡單的裝飾器模式來說,跳過Decorator也未嘗不可,也不會影響既有功能。但是這樣會違背面對對象編程的概念,如果新增的功能非常多,會產生很多相同的代碼。

代碼實現

存在對動物的描述,我們要給它賦予它的顏色。

animal類:描述一種動物

package com.brickworkers;
/**
 * 
 * @author Brickworker
 * Date:2017年5月12日下午4:10:54 
 * 關於類Animal.java的描述:抽象被裝飾類
 * Copyright (c) 2017, brcikworker All Rights Reserved.
 */
public interface Animal {

    //描述一種動物
    public void name();
}

Cat類:描述具體的動物

package com.brickworkers;
/**
 * 
 * @author Brickworker
 * Date:2017年5月12日下午4:11:07 
 * 關於類Cat.java的描述:真實被裝飾類
 * Copyright (c) 2017, brcikworker All Rights Reserved.
 */
public class Cat implements Animal{

    @Override
    public void name() {
        System.out.println("貓");
    }

}

Decorator類,抽象裝飾器類:

package com.brickworkers;
/**
 * 
 * @author Brickworker
 * Date:2017年5月12日下午4:10:42 
 * 關於類Decorator.java的描述:抽象裝飾類
 * Copyright (c) 2017, brcikworker All Rights Reserved.
 */
public abstract class Decorator implements Animal{

    //一個真實對象的引用
    private Animal animal;

    //構造方法
    public Decorator(Animal animal) {
        this.animal = animal;
    }


    //把請求轉發給被代理對象
    public void name(){
        animal.name();
    }
}

RedDecorator類,紅色動物裝飾器:

package com.brickworkers;
/**
 * 
 * @author Brickworker
 * Date:2017年5月12日下午4:09:17 
 * 關於類RedDecorator.java的描述:具體裝飾類
 * Copyright (c) 2017, brcikworker All Rights Reserved.
 */
public class RedDecorator extends Decorator{

    //構造函數
    public RedDecorator(Animal animal) {
        super(animal);
    }


    //重寫name方法,注入新的功能
    @Override
    public void name() {
        super.name();
        red();
    }


    //新增對動物顏色的描述
    private void red(){
        System.out.println("紅色");
    }
}

WhiteDecorator類,白色動物裝飾器:

package com.brickworkers;
/**
 * 
 * @author Brickworker
 * Date:2017年5月12日下午4:10:24 
 * 關於類WhiteDecorator.java的描述:具體裝飾類
 * Copyright (c) 2017, brcikworker All Rights Reserved.
 */
public class WhiteDecorator extends Decorator{
    //構造函數
    public WhiteDecorator(Animal animal) {
        super(animal);
    }


    //重寫name方法,注入新的功能
    @Override
    public void name() {
        super.name();
        white();
    }


    //新增對動物顏色的描述
    private void white(){
        System.out.println("白色");
    }
}

以上就是一個完整的裝飾器模式,接下來我們對它進行測試:

package com.brickworkers;
/**
 * 
 * @author Brickworker
 * Date:2017年5月12日下午4:11:50 
 * 關於類DecoratorTest.java的描述:裝飾器類測試
 * Copyright (c) 2017, brcikworker All Rights Reserved.
 */
public class DecoratorTest {


    public static void main(String[] args) {
        //定義貓
        Animal animal = new Cat();
        animal.name();
        System.out.println("==============================");

        //紅色的貓
        RedDecorator redDecorator = new RedDecorator(animal);
        redDecorator.name();
        System.out.println("==============================");
        //白色的貓
        WhiteDecorator whiteDecorator = new WhiteDecorator(animal);
        whiteDecorator.name();
        System.out.println("==============================");

        //一直白色的帶紅色的貓
        RedDecorator redDecorator2 = new RedDecorator(new WhiteDecorator(animal));
        redDecorator2.name();
        System.out.println("==============================");
    }
}
//運行結果:
//貓
//==============================
//貓
//紅色
//==============================
//貓
//白色
//==============================
//貓
//白色
//紅色
//==============================
//
//

如果要說裝飾器模式複雜,那麼複雜的一定是它一層層的嵌套。比如例子中的“一隻白色帶紅色的貓”。但是你有沒有想過我爲什麼描述成這樣,而不描述成“一隻紅色帶白的的貓”呢?在裝飾器模式中,我們已最後一層來代表真實裝飾的對象。比如說:

    RedDecorator redDecorator = new RedDecorator(animal);

那麼我們裝飾的就是這隻貓。但是如果這樣:

RedDecorator redDecorator2 = new RedDecorator(new WhiteDecorator(animal));

那麼我們裝飾的其實是一隻白色的貓。所以我上面的描述就是“一隻白色帶紅色的貓”。

在IO系統中裝飾器模式體現

我們以InputStream爲例來說明裝飾器模式,我在網上找了一些比較好的圖:
InputStream
這裏寫圖片描述
從圖中,我們進行分析,或者自己去查看代碼可以發現InputStream是祖宗,它就相當於我們前面說的component類,它是抽象的被裝飾類。

在直接實現InputStream的子類中,我們發現FilterInputStream就是一個裝飾器類,同時它有4個具體的裝飾器子類。

那麼按照我們前面的邏輯,我們要定義一個從File文件讀入生產一個有行號標記的流要怎麼寫呢?從上面的圖可以知道,我們其實需要對FileInputStream用LineNumberInputStream進行裝飾,代碼如下:

package com.brickworkers;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.LineNumberInputStream;

/**
 * 
 * @author Brickworker
 * Date:2017年5月12日下午4:11:50 
 * 關於類DecoratorTest.java的描述:裝飾器類測試
 * Copyright (c) 2017, brcikworker All Rights Reserved.
 */
public class DecoratorTest {


    public static void main(String[] args) throws FileNotFoundException {

        //對FileInputStream用LineNumberInputStream進行裝飾
        LineNumberInputStream inputStream = new LineNumberInputStream(new FileInputStream(new File("C:/user/brickworker/test.txt")));
    }
}

與代理模式的區別

裝飾器模式與代理模式實現非常相似,都使用了組合的方式,而且裝飾類與被裝飾類都實現了同一個接口,代理類與被代理類也都實現了同一個接口。個人感覺而言總結了幾點區別:
①功能不同,裝飾器模式是爲了更靈活的豐富對象功能;而代理模式是限制了對象訪問。
②裝飾器模式可以裝飾多個對象,對象變動是靈活的。代理模式中代理類與被代理類是固定的,甚至是寫死在代理類內部。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章