设计模式笔记(四)——装饰者模式

1. 是什么——定义

装饰者模式是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。

2. 为什么——特点

1)  装饰对象和真实对象有相同的接口。这样客户端对象就能以和真实对象相同的方式和装饰对象交互。

2)  装饰对象包含一个真实对象的引用。

3)  装饰对象接受所有来自客户端的请求,它把这些请求转发给真实的对象。

4)  装饰对象可以在转发这些请求以前或以后增加一些附加功能。这样就确保了在运行时,不用修改给定对象的结构就可以在外部增加附加的功能。在面向对象的设计中,通常是通过继承来实现对给定类的功能扩展。

3. 什么时候用——适用性

1)  需要扩展一个类的功能,或给一个类添加附加职责。

2)  需要动态的给一个对象添加功能,这些功能可以再动态的撤销。

3)  需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实。

4)  当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。

4. UML图

194516_vzA2_3041734.png
 

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. 装饰者模式与代理模式的区别

装饰者模式可以互相装饰,而代理模式有很明确的主从关系。

转载于:https://my.oschina.net/LinkedBear/blog/1787712

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值