1. 装饰者模式
举个栗子, 假如在一家饮料店中有两种饮料, 分别是奶茶和咖啡, 相对的有三种调料, 蜂蜜, 块糖和摩卡, 这样消费者就有不同的消费组合, 比如加糖摩卡咖啡, 蜂蜜摩卡咖啡, 加糖奶茶...... 如果饮料的种类或者调料的种类增多, 那么消费组合就会相应的增多, 反映到编程代码上就会出现 "类爆炸", 而且再添加新的饮料或者调料时会不可避免的改变原有的类的代码, 这就违反了设计原则中的开放 - 关闭原则, 即类应该对扩展开放, 对修改关闭.
1.1 类图
使用装饰者模式就能很好地解决这个问题, 废话不多说, 该例子的类图如下:
其中 Beverage 是抽象类, CondimentDecorator 是继承自 Beverage 类的抽象类.
1.2 饮料的抽象类
Beverage.java 代码:
- /**
- * @author yylin
- */
- public abstract class Beverage {
- // 饮料的描述
- protected String description;
- public Beverage() {
- description = "饮料的抽象类";
- }
- public String getDescription() {
- return description;
- }
- public abstract double cost();
- }
image.gif
1.3 调味品的抽象类
CondimentDecorator.java 代码:
- /**
- * @author yylin
- */
- // 调味品装饰者, 继承自饮料的抽象类
- public abstract class CondimentDecorator extends Beverage {
- public CondimentDecorator() {
- description = "调味品的抽象类";
- }
- @Override
- public abstract String getDescription();// 所有的调料品装饰者必须重写 getDescription() 方法
- @Override
- public abstract double cost();// 所有的调料品装饰者必须重写 cost() 方法
- }
1.4 饮料 奶茶的实现类
TeaMilk.java 的代码:
- /**
- * @author yylin
- */
- public class TeaMilk extends Beverage {
- public TeaMilk(){
- // 饮料的描述
- description="奶茶";
- }
- /* (non-Javadoc)
- * @see Beverage#cost()
- */
- @Override
- public double cost() {
- // 奶茶一杯三块钱
- return 3.0;
- }
- }
1.5 饮料 咖啡的实现类
Coffee.java 的代码:
- /**
- * @author yylin
- */
- public class Coffee extends Beverage {
- public Coffee() {
- // 饮料的描述
- description="咖啡";
- }
- /* (non-Javadoc)
- * @see Beverage#cost()
- */
- @Override
- public double cost() {
- // 咖啡一杯四块钱
- return 4.0;
- }
- }
1.6 调味品 蜂蜜的实现类
Honey.java 的代码:
- /**
- * @author yylin
- */
- // 蜂蜜, 继承自调味品抽象类
- public class Honey extends CondimentDecorator {
- // 记录饮料的变量, 是被装饰者
- Beverage beverage;
- // 让被装饰者记录到实例变量中
- public Honey(Beverage beverage) {
- this.beverage = beverage;
- }
- @Override
- public String getDescription() {
- // 描述调味品和饮料
- return "蜂蜜" + beverage.getDescription();
- }
- /*
- * (non-Javadoc)
- *
- * @see CondimentDecorator#cost()
- */
- @Override
- public double cost() {
- // 加蜂蜜一块钱, 计算加了蜂蜜的饮料的价钱
- return 1.0 + beverage.cost();
- }
- }
1.7 调味品 摩卡的实现类
Mocha.java 的代码:
- /**
- * @author yylin
- */
- // 摩卡, 继承自调味品装饰者
- public class Mocha extends CondimentDecorator {
- // 用一个变量记录饮料, 也就是被装饰者
- Beverage beverage;
- // 把被装饰者记录到实例变量中
- public Mocha(Beverage beverage) {
- this.beverage = beverage;
- }
- @Override
- public String getDescription() {
- // 描述调味品和饮料
- return "摩卡" + beverage.getDescription();
- }
- @Override
- public double cost() {
- // 加摩卡一块钱, 计算加了摩卡的饮料的价钱
- return 1.0 + beverage.cost();
- }
- }
1.8 调味品 糖的实现类
Sugar.java 的代码:
- /**
- *
- * @author yylin
- *
- */
- // 糖, 继承自调味品抽象类
- public class Sugar extends CondimentDecorator {
- // 用一个变量记录饮料, 也就是被装饰者
- Beverage beverage;
- // 把被装饰者记录到实例变量中
- public Sugar(Beverage beverage) {
- this.beverage = beverage;
- }
- @Override
- public String getDescription() {
- // 描述加糖的饮料
- return "加糖" + beverage.getDescription();
- }
- @Override
- public double cost() {
- // 计算加了糖的饮料的价钱
- return 1.0 + beverage.cost();
- }
- }
1.9 测试类
- /**
- * @author yylin
- *
- */
- // 测试类
- public class TestMain {
- public static void main(String[] args) {
- // 点一杯蜂蜜摩卡咖啡
- Beverage beverage1 = new Coffee();// 定义咖啡对象
- beverage1 = new Mocha(beverage1);// 用摩卡装饰
- beverage1 = new Honey(beverage1);// 用蜂蜜装饰
- System.out.println("顾客点了 (" + beverage1.getDescription() + ")\n 价格是:"
- + beverage1.cost() + "元");
- // 点一杯加糖奶茶
- Beverage beverage2 = new TeaMilk();// 定义奶茶对象
- beverage2 = new Sugar(beverage2);// 用糖装饰
- System.out.println("顾客点了 (" + beverage2.getDescription() + ")\n 价格是:"
- + beverage2.cost() + "元");
- }
- }
运行结果
2. JAVA I/O 中的装饰者模式
例题:
l 先从文件 test.txt 中读 Employee 对象的数据存到 HashMap,
l 再把 HashMap 中的 Employee 对象的数据存到一个新的文件 test2.txt.
代码:
2.1 Employee 实体类 Employee.java
- package com.nwpu;
- /**
- *
- * @author yylin
- *
- */
- public class Employee {
- private String id;
- private String name;
- private String department;
- public Employee() {
- }
- public Employee(String id, String name, String department) {
- this.id = id;
- this.name = name;
- this.department = department;
- }
- public String getId() {
- return id;
- }
- public void setId(String id) {
- this.id = id;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public String getDepartment() {
- return department;
- }
- public void setDepartment(String department) {
- this.department = department;
- }
- @Override
- public String toString() {
- return id+","+name+","+department;
- }
- }
2.2 测试类 TestFileIO.java
- package com.nwpu;
- import java.io.BufferedReader;
- import java.io.BufferedWriter;
- import java.io.FileInputStream;
- import java.io.FileOutputStream;
- import java.io.InputStreamReader;
- import java.io.OutputStreamWriter;
- import java.util.HashSet;
- import java.util.Set;
- /**
- *
- * @author yylin
- * 读取文件时:
- * 一行一行读取文件, 解决读取中文字符时出现乱码.
- * 流的关闭顺序: 先打开的后关, 后打开的先关,
- * 否则有可能出现 java.io.IOException: Stream closed 异常.
- *
- * 写入文件时:
- * 一行一行写入文件, 解决写入中文字符时出现乱码.
- * 流的关闭顺序: 先打开的后关, 后打开的先关,
- * 否则有可能出现 java.io.IOException: Stream closed 异常.
- */
- public class TestFileIO {
- public static void main(String[] args) {
- /**
- * read file
- */
- FileInputStream fis=null;// 文件输入流
- InputStreamReader isr=null;// 读入输入流
- BufferedReader br=null;// 对读入的文件流缓存
- Set<Employee> set=new HashSet<Employee>();
- try {
- String fileURL="E:/test/test.txt";
- fis=new FileInputStream(fileURL);
- // 解决读入中文乱码的问题 + 用 InputStreamReader 类装饰 FileInputStream 类
- isr=new InputStreamReader(fis,"UTF-8");
- // 用 BufferedReader 类装饰 BufferedReader 类
- br=new BufferedReader(isr);
- String line="";
- String arrs[]=null;
- // 按行读入
- while ((line=br.readLine())!=null) {
- System.out.println(line);// 输出读入的行
- arrs=line.split(",");
- // 注入对象
- set.add(new Employee(arrs[0],arrs[1],arrs[2]));
- }
- // 流的关闭顺序: 先打开的后关, 后打开的先关
- br.close();
- isr.close();
- fis.close();
- } catch (Exception e) {
- e.printStackTrace();
- }
- for (Employee e : set) {
- System.out.println(e.toString());
- }
- /**
- * write file
- */
- FileOutputStream fos=null;// 文件输出流
- OutputStreamWriter osw=null;// 写出输出流
- BufferedWriter bw=null;// 缓存写出的输出流
- try {
- String fileURL="E:/test/test2.txt";
- fos=new FileOutputStream(fileURL);
- // 解决中文乱码问题 + 用 OutputStreamWriter 类装饰 FileOutputStream 类
- osw=new OutputStreamWriter(fos,"UTF-8");
- // 用 BufferedWriter 类装饰 OutputStreamWriter 类
- bw=new BufferedWriter(osw);
- for (Employee e : set) {
- bw.write(e.toString()+"\r\n");
- }
- // 注意关闭的先后顺序, 先打开的后关闭, 后打开的先关闭
- bw.close();
- osw.close();
- fos.close();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
2.3 运行结果
程序运行前:
test.txt 中:
程序运行后:
test.txt 中:
来源: http://www.jianshu.com/p/cf2af73a77b4