1. 是什么——定义
装饰者模式是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
2. 为什么——特点
1) 装饰对象和真实对象有相同的接口。这样客户端对象就能以和真实对象相同的方式和装饰对象交互。
2) 装饰对象包含一个真实对象的引用。
3) 装饰对象接受所有来自客户端的请求,它把这些请求转发给真实的对象。
4) 装饰对象可以在转发这些请求以前或以后增加一些附加功能。这样就确保了在运行时,不用修改给定对象的结构就可以在外部增加附加的功能。在面向对象的设计中,通常是通过继承来实现对给定类的功能扩展。
3. 什么时候用——适用性
1) 需要扩展一个类的功能,或给一个类添加附加职责。
2) 需要动态的给一个对象添加功能,这些功能可以再动态的撤销。
3) 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实。
4) 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
4. UML图
5. 怎么用——使用方法
需求:
模拟人穿衣服
5.1 没有任何原则的初始设计
public class Demo1 {
public static void main(String[] args) {
Person person = new Person();
person.setName("二哈");
person.wearTshirt();
person.wearJeans();
person.wearShoes();
}
}
class Person {
private String name;
public void wearTshirt() {
System.out.println(name + "穿了一件T恤");
}
public void wearSweater() {
System.out.println(name + "穿了一件毛衣");
}
public void wearJeans() {
System.out.println(name + "穿了一条裤子");
}
public void wearShoes() {
System.out.println(name + "穿了一双鞋");
}
// public ..... -- 违反开放-封闭原则
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
可以看到,如果要增加新的衣服,就需要扩展方法,这就违背了“开放-封闭原则”。
5.2 将“服装”抽象
public class Demo2 {
public static void main(String[] args) {
Person person = new Person();
Clothes Tshirt = new TShirt();
Clothes shoes = new Shoes();
Clothes superman = new SupermanClothes();
person.wear(superman);
person.wear(Tshirt);
person.wear(shoes);
}
}
class Person {
private String name;
public void wear(Clothes clothes) {
System.out.println(name + "穿了" + clothes.getName());
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
abstract class Clothes {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class TShirt extends Clothes {
}
class Shoes extends Clothes {
}
class SupermanClothes extends Clothes {
}
这样确实达到效果了,但每次穿衣服的时候总感觉是当着众人的面穿,而且逻辑也不对:每次都是人在穿,但实际情况是:穿了一件后,下一件衣服是套在原来那件衣服上!需要改进!
5.3 使用装饰者模式
public class Demo3 {
public static void main(String[] args) {
Person person = new Person();
person.setName("二狗");
Clothes tshirt = new Tshirt();
person.wear(tshirt);
Clothes coat = new Coat();
tshirt.wear(coat);
coat.show();
}
}
class Person {
private String name;
public void show() {
System.out.println(name);
}
public void wear(Clothes clothes) {
clothes.setWornClothes(this);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
abstract class Clothes extends Person {
private Person wornClothes; //要被穿的衣服/人
private String clothesName;
@Override
public void show() {
System.out.println(clothesName + "\t");
wornClothes.show();
}
public String getClothesName() {
return clothesName;
}
public void setClothesName(String clothesName) {
this.clothesName = clothesName;
}
public Person getWornClothes() {
return wornClothes;
}
public void setWornClothes(Person wornClothes) {
this.wornClothes = wornClothes;
}
}
class Tshirt extends Clothes {
public Tshirt () {
this.setClothesName("T恤");
}
}
class Coat extends Clothes {
public Coat() {
this.setClothesName("大衣");
}
}
class Jeans extends Clothes {
public Jeans() {
this.setClothesName("裤子");
}
}
6. 真实案例
需求:
a) 扩展BufferedReader类,使其每次读取文本信息时可以在文本前打印行号
b) 扩展BufferedReader类,使其每次读取文本信息时可以在文本后打印省略号
c) 扩展BufferedReader类,使其每次读取文本信息时可以在文本前和文本后打印引号
6.1 没有使用装饰者模式
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
public class Demo1 {
public static void main(String[] args) throws Exception {
BufferedReader br = new ReaderLineNumAndSusp(new InputStreamReader(new FileInputStream("D:/test.txt"), "GBK"));
String readline = null;
while ((readline = br.readLine()) != null) {
System.out.println(readline);
}
br.close();
}
}
//需求1:增强BufferedReader类,使每次读取的内容前面加上行号
class ReaderLineNum extends BufferedReader {
private int lineNum = 1;
public ReaderLineNum(Reader in) {
super(in);
}
@Override
public String readLine() throws IOException {
String readline = super.readLine();
if (readline == null) {
return null;
}
readline = lineNum + readline;
lineNum++;
return readline;
}
}
//需求2:增强BufferedReader类,使每次读取的内容后面加上省略号
class ReaderSusp extends BufferedReader {
public ReaderSusp(Reader in) {
super(in);
}
@Override
public String readLine() throws IOException {
String readline = super.readLine();
if (readline == null) {
return null;
}
readline = readline + "……";
return readline;
}
}
//需求3:增强BufferedReader类,使每次读取的内容前后加上双引号
class ReaderQuot extends BufferedReader {
public ReaderQuot(Reader in) {
super(in);
}
@Override
public String readLine() throws IOException {
String readline = super.readLine();
if (readline == null) {
return null;
}
readline = "“" + readline + "”";
return readline;
}
}
//需求4:增强BufferedReader类,使每次读取的内容前加行号,内容后加省略号
class ReaderLineNumAndSusp extends BufferedReader {
private int lineNum = 1;
public ReaderLineNumAndSusp(Reader in) {
super(in);
}
@Override
public String readLine() throws IOException {
String readline = super.readLine();
if (readline == null) {
return null;
}
readline = lineNum + readline + "……";
lineNum++;
return readline;
}
}
//需求5:。。。
6.2 使用装饰者模式
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
public class Demo2 {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("D:/test.txt"), "GBK"));
BufferedReader brLineNum = new ReaderLineNum(br);
BufferedReader brSusp = new ReaderSusp(brLineNum);
String readline = null;
while ((readline = brSusp.readLine()) != null) {
System.out.println(readline);
}
brSusp.close();
}
}
class ReaderLineNum extends BufferedReader {
private int lineNum = 1;
private BufferedReader baseReader;
public ReaderLineNum(Reader in) {
super(in);
this.baseReader = (BufferedReader) in;
}
@Override
public String readLine() throws IOException {
String readline = baseReader.readLine();
if (readline == null) {
return null;
}
readline = lineNum + readline;
lineNum++;
return readline;
}
}
class ReaderSusp extends BufferedReader {
private BufferedReader baseReader;
public ReaderSusp(Reader in) {
super(in);
this.baseReader = (BufferedReader) in;
}
@Override
public String readLine() throws IOException {
String readline = baseReader.readLine();
if (readline == null) {
return null;
}
return readline + "……";
}
}
可以实现三个类完成7种功能。
7. 装饰者模式与建造者模式的区别
建造者模式需要有一套完整的建造过程和顺序
装饰者模式更随意,灵活(哪怕内裤外穿。。。)
8. 装饰者模式与代理模式的区别
装饰者模式可以互相装饰,而代理模式有很明确的主从关系。