裝飾者模式是結構型設計模式。java
裝飾模式以對客戶端透明的方式擴展對象的功能,是繼承關係的一個替代方案。git
容許向一個現有的對象添加新的功能。同時又不改變其結構,它是做爲現有的類的一個包裝。github
主要解決的問題: 通常咱們爲了擴展一個類常常使用繼承方式實現,因爲繼承爲類引入靜態特徵,而且隨着擴展功能的增多,子類會很膨脹。設計模式
結構:數組
模擬在餐館點飲料,咱們能夠點咖啡,而咖啡有
Decaf
咖啡和Espresso
咖啡,而這兩種咖啡均可以加牛奶和巧克力進去。bash
具體的代碼組織結構圖:ide
具體代碼:函數
先看最高的component
包下的Drink
類:性能
/**
* Component的超類
* 單品和裝飾者都要繼承自這個類
*/
public abstract class Drink {
private String description = ""; //一開始沒有描述
private double price = 0; //一開始價格爲0
/**
* 抽象方法
* 一、若是是單品的話就直接是本身的價格
* 二、若是是裝飾者的話就還要加上裝飾品本身的價格
*/
public abstract double cost();
// setter getter
public String getDescription() {
return description;
}
public double getPrice() {
return price;
}
public void setDescription(String description) { //描述的時候順便把價格描述一下
this.description = description;
}
public void setPrice(double price) {
this.price = price;
}
}複製代碼
下面看兩個具體的Component
:測試
/** ConcreteComponent 1*/
public class Decaf extends Drink {
public Decaf() {
super.setDescription("Decaf");
super.setPrice(3); //3塊錢
}
@Override
public double cost() {
return getPrice();//super.getPrice()//這個就是父類的價格(本身什麼也沒加 (沒有被裝飾))
}
// 重寫getter 後面加上本身的花費
@Override
public String getDescription() {
return super.getDescription() + "-" + cost();
}
}
複製代碼
/** ConcreteComponent 2
* 也能夠在ConcreteComponent和Drink類有一個過渡的類) (好比Coffee類)
*/
public class Espresso extends Drink {
public Espresso(){
super.setDescription("Espresso");
super.setPrice(4);
}
@Override
public double cost() {
return getPrice();//super.getPrice()//這個就是父類的價格(本身什麼也沒加)
}
@Override
public String getDescription() {
return super.getDescription() + "-" + cost();
}
}複製代碼
下面看decorator
下的三個類:
第一個是裝飾者的超類,繼承自Drink
類:
public class Decorator extends Drink{
/**
* 這個引用很重要,能夠是單品,也能夠是被包裝過的類型,因此使用的是超類的對象
* 這個就是要被包裝的單品(被裝飾的對象)
*/
private Drink drink; //這裏要拿到父類的引用,由於要控制另外一個分支(具體的組件)
public Decorator(Drink drink) {
this.drink = drink;
}
/**
* 若是drink是已經被裝包過的,那麼就會產生遞歸調用 最終到單品
*/
@Override
public double cost() {
return super.getPrice() + drink.cost(); // 本身的價格和被包裝單品的價格
}
@Override
public String getDescription() {
return super.getDescription() + "-" + super.getPrice()
+ " && " + drink.getDescription();
}
}複製代碼
而後是兩個裝飾者:
/**
* 這個是具體的裝飾者() --> 繼承自中間的裝飾着Decorator
*/
public class Chocolate extends Decorator{
public Chocolate(Drink drink) { //若是父類搞了一個 帶參數的構造函數,子類必須顯示的使用super調用
super(drink);
super.setDescription("Chocolate");
super.setPrice(1);
}
}複製代碼
public class Milk extends Decorator{
public Milk(Drink drink) {
super(drink); //調用父類的構造函數
super.setDescription("Milk");
super.setPrice(3);
}
}複製代碼
測試類:
public class MyTest {
public static void main(String[] args) {
//只點一個單品 (Decaf 咖啡)
Drink order = new Decaf();
System.out.println("order description : " + order.getDescription());
System.out.println("order price : " + order.cost());
System.out.println("---------------加了調料的----------------");
order = new Milk(order);// 加了牛奶
order = new Chocolate(order);
order = new Chocolate(order); // 加了兩個巧克力
System.out.println("order description : " + order.getDescription());
System.out.println("order price : " + order.cost());
}
}複製代碼
程序輸出:
order description : Decaf-3.0
order price : 3.0
---------------加了調料的----------------
order description : Chocolate-1.0 && Chocolate-1.0 && Milk-3.0 && Decaf-3.0
order price : 8.0複製代碼
因爲Java I/O庫須要不少性能的各類組合,若是這些性能都是用繼承的方法實現的,那麼每一種組合都須要一個類,這樣就會形成大量性能重複的類出現,因此Java IO使用的是裝飾者設計模式。
因此咱們能夠定義本身的裝飾者。
這裏咱們定義一個流,這個流將讀入的小寫字母轉換成大寫字母。
UpperCaseInputStream
代碼以下:
/**
* 本身定義的輸入流
* 擴展FilterInputStream(這個類就是咱們的Decorator) 中間裝飾者
* 因此咱們只要繼承這個就能夠擴展本身的輸入流裝飾者
*/
public class UpperCaseInputStream extends FilterInputStream{
protected UpperCaseInputStream(InputStream in) { //這個InputStream就是咱們的Drink 類(超類)
super(in);
}
// 實現兩個read()方法,將大寫轉化成小寫的讀入
//重寫 至關於cost和description
@Override
public int read() throws IOException {
int index = super.read(); //讀取一個字節
return index == -1 ? index : Character.toUpperCase((char)(index)); //小寫轉換成大寫
}
//字節數組
@Override
public int read(byte[] b, int off, int len) throws IOException {
int index = super.read(b, off, len);
for(int i = 0; i < index; i++)
b[i] = (byte)Character.toUpperCase((char)(b[i]));
return index;
}
}複製代碼
測試一下使用這個類:
public class MyTest {
public static void main(String[] args) throws IOException {
InputStream in = new UpperCaseInputStream(new BufferedInputStream(new FileInputStream("/home/zxzxin/Java_Maven/DesignPatterns/src/main/java/decorator/java/in.txt")));// 將這個in.txt文件讀入的內容轉換成大寫
int len;
while((len = in.read()) >= 0)
System.out.print((char)(len));
in.close();
}
}複製代碼
輸出結果演示:
優缺點:
實際應用: 大多數狀況下,裝飾模式的實現都要比上面給出的示意性例子要簡單。