装饰者模式
装饰者模式是继承的一种替代方案。它以对客户端透明的状态动态的为对象赋予不同的职责,而不用进行大量子类的扩写。
模式结构
它的类图如下:
Component(抽象构件): 通常作为接口被实现,用于规范被装饰角色
ConcreteComponent(具体构件): 作为揽收职责的对象类,实现了抽象构件接口
Decorator(装饰职责超类): 用于规范具体装饰组件,持有一个具体构建实例
ConcreteDecoratorA,B(具体装饰类): 作为具体装饰职责,为对象扩展功能
装饰者模式实例
记录这篇笔记主要目的是把io类啃下来,单纯是理论就太枯燥了。在对io类三拜五扣之前先引入一段栗子
考虑一下咖啡厅的业务,咖啡作为产出,但不同的人口味不尽相同,有的喜欢加薄荷有的喜欢加牛奶;不同的配料需要收不同的钱。当我们使用继承来进行抽象,可能会多出许多coffee的子类,例如MintCoffee,MilkCoffee….这样的实现是不优雅的,考虑客人可能要加一点薄荷,再加一点牛奶。我们总不能上两杯吧
这个时候就需要装饰者模式派上用场了,定义出抽象构件 CoffeeComponent
1 2 3 4 5 6 7 8 9 10
| package com.meteor;
public interface CoffeeComponent { String getName(); String getAdd(); double getPrice(); }
|
以及具体构件,客人可能需要一杯蓝山咖啡
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package com.meteor;
public class BlueCoffee implements CoffeeComponent{ @Override public String getName() { return "蓝山咖啡"; }
@Override public String getAdd() { return ""; }
@Override public double getPrice() { return 5.0; } }
|
装饰职责超类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| package com.meteor;
public class CoffeeDecorator implements CoffeeComponent{
private CoffeeComponent coffeeEntity;
public CoffeeDecorator(CoffeeComponent coffeeEntity){ this.coffeeEntity = coffeeEntity; }
@Override public String getName() { return coffeeEntity.getName(); }
@Override public String getAdd() { return coffeeEntity.getAdd(); }
@Override public double getPrice() { return coffeeEntity.getPrice(); }
public CoffeeComponent getCoffeeEntity() { return coffeeEntity; } }
|
根据客人的喜好写出具体装饰类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| package com.meteor.decorator;
import com.meteor.CoffeeComponent; import com.meteor.CoffeeDecorator;
public class MilkCoffeeDecorator extends CoffeeDecorator { public MilkCoffeeDecorator(CoffeeComponent coffeeEntity) { super(coffeeEntity); }
@Override public String getName() { return getCoffeeEntity().getName(); }
@Override public double getPrice() { return getCoffeeEntity().getPrice()+2.0; }
@Override public String getAdd() { return getCoffeeEntity().getAdd()+"牛奶 "; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| package com.meteor.decorator;
import com.meteor.CoffeeComponent; import com.meteor.CoffeeDecorator;
public class MintCoffeeDecorator extends CoffeeDecorator {
public MintCoffeeDecorator(CoffeeComponent coffeeEntity) { super(coffeeEntity); }
@Override public String getName() { return getCoffeeEntity().getName(); }
@Override public String getAdd() { return getCoffeeEntity().getAdd()+"薄荷 "; }
@Override public double getPrice() { return getCoffeeEntity().getPrice()+2.0; } }
|
到这里就完成了,当需要推出新口味的时候你可以再次增加装饰类。在客人需要薄荷的时候赋予薄荷的职责,或者是直接new 一杯红豆牛奶咖啡….不论是什么口味,他总是一杯CoffeeComponent
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public static void print(CoffeeComponent coffeeComponent){ out.println(coffeeComponent.getName()+"/"+coffeeComponent.getAdd()+"/"+coffeeComponent.getPrice()+"$"); }
public static void main(String[] args) { CoffeeComponent coffeeComponent = new BlueCoffee(); print(coffeeComponent); out.println("======加入牛奶======="); coffeeComponent = new MilkCoffeeDecorator(coffeeComponent); print(coffeeComponent); out.println("======加入薄荷======="); coffeeComponent = new MintCoffeeDecorator(coffeeComponent); print(coffeeComponent); out.println("======一杯薄荷牛奶咖啡====="); CoffeeComponent mintMilkCoffee = new MintCoffeeDecorator(new MilkCoffeeDecorator(new BlueCoffee())); print(coffeeComponent); }
|
1 2 3 4 5 6 7
| 蓝山咖啡/ /5.0$ ======加入牛奶======= 蓝山咖啡/ 牛奶 /7.0$ ======加入薄荷======= 蓝山咖啡/ 牛奶 薄荷 /9.0$ ======一杯薄荷牛奶咖啡===== 蓝山咖啡/ 牛奶 薄荷 /9.0$
|
通过装饰者模式记忆IO类
在理解了装饰者模式后,再来理io的脉络就不是很难了。以下是java中io的类图关系
将目光放在字节流上
用前文提到的模式结构将他们的关系用装饰者模式理一遍
InputStream OutputStream 作为字节流的两个超类(抽象构件 Component),分别是输入与输出
在InputStream这个模式中,拥有 FileInputStream(文件处理),ByteArrayInputStream…. 等具体构件(ConcreteComponent)
同样与具体构件实现了Component的是装饰职责超类 FilterInputStream ,它的继承下定义了三个具体装饰类
BufferedInputStream,DataInputStream,PushbakInputStream;OutStream同样可以这样整理出来
在脑海中整理出模型后,当我们想读取某个文件时终于不用对着文档敲了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public static void main(String[] args) { File file = new File("zsh.txt"); try { FileInputStream inputStream = new FileInputStream(file); BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream); byte bytes[] = new byte[bufferedInputStream.available()]; while ((bufferedInputStream.read(bytes))!=-1){ out.print(new String(bytes)); } } catch (FileNotFoundException e) { throw new RuntimeException(e); } catch (IOException e) { throw new RuntimeException(e); } }
|
当使用字符流时也是同样的方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package com.meteor.io;
import java.io.*;
public class Test2 { public static void main(String[] args) { File file = new File("zsh.txt"); try { FileReader fileReader = new FileReader(file); BufferedReader bufferedReader = new BufferedReader(); String line = null; while ((line = bufferedReader.readLine())!=null){ System.out.println(line); } } catch (FileNotFoundException e) { throw new RuntimeException(e); } catch (IOException e) { throw new RuntimeException(e); } } }
|