加糖加冰加牛奶——装饰模式

装饰模式

​ Decorator Pattern:属于GoF23种设计模式中结构型设计模式的一种,又被称为装饰者模式。

​ 简单的说,装饰模式/装饰者模式就是给一个现有的类,在不改变这个类的情况下,动态的给这个类进行扩展,添加新的东西,比如说添加新的功能。

动态的意思也就是说不需要这个类创建的时候指定,完全可以等这个类创建完成了之后,已经完全成为了一个“活蹦乱跳”的对象了之后,再给他添加新的东西,不需要写死,而是想要什么时候加就什么时候加,想要加什么就加什么。

结构

装饰者模式类图

  • 给出一张简易的装饰者模式的结构图片吧,分析一下下

    1. 有一个抽象对象,这个是干什么的呢?这个是被装饰者对象,简单的说就是我们需要在这个对象上进行装饰,这样就好理解了吧。

      至于为什么要对被装饰者进行抽象,因为要面向接口编程的嘛,也可以留出更大的扩展空间不是。

    2. 紧接着的就是具体的对象了,也就是真实的被装饰者。

    3. 装饰者模式主要的还是装饰,那么显然对于装饰的抽象是不可避免的,也不会就有一个功能需要装饰的吧,如果真的有那也要进行抽象以下,毕竟,也需,过不了多久你的PM和你说需求要改了呢?

      这个里面需要注意的就是维护了一个被装饰者对象,因为在实际的代码中我们应该对这个被装饰者进行包装,但是前提是我们不能修改这个被装饰者,也就是需要动态两个字的体现,具体的应用可以看后面的代码部分可能会好理解一点。

    4. 下面的就是所有的具体如何去装饰的实现类了。

举例说明
  1. 对于装饰着模式来说,最容易体会到的,那就是学习JAVA的IO流的时候了,那里面的一层层的包装,真的是眼花缭乱。

    比如想要生成一个字符缓冲流,应该如何???

    BufferedReader stringBuffer = new BufferedReader(new InputStreamReader(new FileInputStream("demo.txt")));
    

    哈哈,这就是层层装饰啊,从一个文本输入流,变成字符串输入流,变成带缓冲的字符缓冲流。

  2. OK,我们也可以面向生活一点儿,有没有小伙伴知道“加码子”是啥意思啊?早餐的时候,首先呢点了一碗素面,发现不行,太过于单调,有点了一个豆干,还是不行,紧接着一个鸡蛋,还是不行,有加了一份牛肉…越来越多,越来越多,原先的一碗素面已经是“五彩缤纷”了啊。

  3. 再来个小栗子,也是阔以的啊,比如去喝个奶茶,有原味奶茶的吧,但是没意思啊,味道不好,但是呢,我们可以加东西进去啊,珍珠或者椰果?加冰或者不加冰?都是可以的嘛

  4. 用笔者自己喜欢的方式的话,那就是游戏里面人物的时装了,想当初,毒奶粉的时候,笔者为了黄金天空套,又不想充钱,那是天天搬砖,天天搬砖换代币卷,一件一件凑齐的啊,看着自己的角色一件件穿上时装,嘿嘿装饰模式之下变得金闪闪啊(不是那个金闪闪啊,这里可没有咖喱棒)。

注意
  1. 其实呢这个装饰模式的结构是可以简化的,上面给出的是一个完整的结构。可以简化的部分很显然嘛,就是两个抽象类可以去掉撒。

    也就是说,只有一个被装饰者对象的时候,被装饰者的抽象类咱就不要了,抽象装饰直接继承这个对象就成了啊。

    还有一种呢就是只有一个具体的装饰实现类,那就直接不要抽象装饰类就行了呗,子类是可以顶替父类的嘛。

  2. 装饰着的优点的话,应该就是可以创建五花八门的各种各样的实际对象吧。这样子显得扩展更加的灵活,而且自由性很高很高的。

  3. 但是捏,东西虽好,也是不可以乱用的,可以看到这个位置有两个大分支线也就是两个抽象类的部分了,如果东西一多,那产生的小小的子类,估计可以看的你头疼…

一个小DEMO
  1. 场景

    一个简单的场景设计吧,记得那是一天下午,压马路的笔者看到了路边的奶茶店,正好口渴准备去点一杯,可是面前的价目表看的真的是眼花缭乱啊,只好准备自己搭配一个,嘿嘿,来一杯原味奶茶,加上椰果,加上冰,珍珠嘛,还是不需要了吧。

    用代码模拟一下这个场景的实现吧!

    抽象类还是接口,那就看大家的习惯吧。

  2. 首先定义被装饰者对象——奶茶的话,估摸着算是饮料???

    package com.decorator;
    /**
     * 装饰者模式——抽象对象——也就是被装饰者对象的抽象对象
     * DEMO中的话就是饮料了
     * @author WQ
     */
    public abstract class Drinks {
    	public abstract void show();
    }
    
  3. 来个奶茶类,这就是具体的被装饰者对象了!

    package com.decorator;
    /**
     * 装饰者模式——具体的被装饰者对象
     * DEMO中的话就是奶茶了
     * @author WQ
     */
    public class MilkyTea extends Drinks{
    	@Override
    	public void show() {
    		System.out.println("我是一杯奶茶啊!");
    	}
    }
    
  4. 定义个抽象的装饰类,这里的话抽象起来,算作配料???

    package com.decorator;
    /**
     * 装饰者模式——抽象装饰对象
     * DEMO中的话就是配料吧
     * @author WQ
     */
    public abstract class Batching extends Drinks{
    	/**
    	 * 维护一个被装饰着对象
    	 * (这里是面向接口编程)
    	 * 注意权限修饰符是protected 这个类的子类是可以直接使用的
    	 */
    	protected Drinks drink;
    	/**
    	 * 说明一下下
    	 * 抽象类是可以有构造方法的
    	 * 只是不能直接创建抽象类的实例对象而已
    	 * @param drink
    	 */
    	public Batching(Drinks drink) {
    		this.drink = drink;
    	}
    	
    	@Override
    	public void show() {
    		drink.show();
    	}
    }
    
  5. 真实的配料也需要上场了啊,这部分就是具体的装饰类了。

    package com.decorator;
    /**
     * 装饰者模式——具体装饰类对象
     * 这是珍珠哦
     * @author WQ
     */
    public class Pearl extends Batching{
    	/**
    	 * 自然的需要维护一个被装饰者对象的嘛
    	 * @param drink
    	 */
    	public Pearl(Drinks drink) {
    		super(drink);
    	}
    	/**
    	 * 这里还需要覆盖掉父类的方法
    	 * 父类里面只是对被装饰着对象调用了一下,
    	 * 我们需要装饰啊
    	 */
    	@Override
    	public void show() {
    		/**
    		* 这里首先是调用了父类的方法的
    		* 我们是对原有的对象进行包装,
    		* 那么应该保证的是原有对象的功能应该是完善的,
    		* 如果这里不调用的话,那么我们编写出来的结果就没有被装饰的类了。
    		*/
    		super.show();
    		decorate(drink);
    	}
    	/**
    	 * 这个位置就是具体的被装饰的逻辑了,
    	 * 虽然这里只有一个小小的输出,
    	 * 但是实现更为复杂的逻辑的时候还是需要抽出来成为一个方法的
    	 */
    	private void decorate(Drinks drink) {
    		System.out.print("加珍珠~");
    	}
    }
    //--------------------------------------------------------------------
    package com.decorator;
    /**
     * 装饰者模式——具体装饰类对象
     * 这是椰果哦
     * @author WQ
     */
    public class Coconut extends Batching{
    
    	public Coconut(Drinks drink) {
    		super(drink);
    	}
    	
    	@Override
    	public void show() {
    		super.show();
    		decorate(drink);
    	}
    
    	private void decorate(Drinks drink) {
    		System.out.print("加椰果~");
    	}
    }
    //----------------------------------------------------------------
    package com.decorator;
    /**
     * 装饰者模式——具体装饰类对象
     * 这是冰哦
     * @author WQ
     */
    public class Ice extends Batching{
    
    	public Ice(Drinks drink) {
    		super(drink);
    	}
    
    	@Override
    	public void show() {
    		super.show();
    		decorate(drink);
    	}
    
    	private void decorate(Drinks drink) {
    		System.out.print("加冰~");
    	}
    }
    
  6. 编个测试类

    package com.decorator;
    /**
     * 装饰者模式——测试类部分
     * @author WQ
     */
    public class Main {
    	public static void main(String[] args) {
    		//被装饰者对象
    		Drinks drink = new MilkyTea();
    		//这一次来个全加
    		Batching batching1 = new Pearl(drink);
            //注意传进去的参数不是drink了,而是被装饰了以后的对象了啊
            //对象已经被打包了,原来的对象穿了一套“衣服”了
    		Batching batching2 = new Coconut(batching1);
            //这也是为啥装饰类也要继承被装饰类的原因哦
    		Batching batching3 = new Ice(batching2);
    		batching3.show();
    		System.out.println();
    		//换个我们熟悉的方式的
    		Batching batching = new Ice(new Pearl(new MilkyTea()));
    		batching.show();
    	}
    }
    
  7. 测试结果

    我是一杯奶茶啊!
    加珍珠~加椰果~加冰~
    我是一杯奶茶啊!
    加珍珠~加冰~
    

完成!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值