一、装饰模式的定义和特点
在软件开发过程中,有时想用一些现存的组件。这些组件可能只是完成了一些核心功能。但在不改变其结构的情况下,可以动态地扩展其功能。所有这些都可以釆用装饰器模式来实现。
就像我们做菜,需要用到调料,菜,刀,火等一系列抽象的组件来最终完成一道菜。
装饰模式的定义:
指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。就增加功能来说,装饰模式比生成子类更加灵活。
特点:
- 装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用
- 通过使用不用装饰类及这些装饰类的排列组合,可以实现不同效果
- 装饰器模式完全遵守开闭原则
缺点
装饰器模式会增加许多子类,过度使用会增加程序得复杂性。
二、装饰模式的结构
装饰模式的结构一般包含以下几个角色
1. 抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象。
2. 具体构件(ConcreteComponent)角色:实现抽象构件,通过装饰角色为其添加一些职责。
3. 抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
4. 具体装饰(ConcreteDecorator)角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。
图示
三、咖啡点单案例演示
有一个需求,点一杯咖啡需要咖啡,材料等等,这个案例就很适合装饰模式,类似于穿衣,点餐,买包子,等等,我们怎么把他设计成装饰模式呢?
看类图
这个结构就是我已经设计好的一个装饰模式的类图,idea自动生成的,这里的Drink就是我们上面说的抽象构建角色,装饰者是Decorator,他是一个抽象装饰,下面他的子类就是具体的装饰者,那么具体构建中间我们提供了一个中间构建,提供了coffee的一些共性,可以放在这里,用的时候直接继承,他的下面就是相应的具体构件,具体被装饰者角色,装饰者与被装饰者共同继承自component抽象构件,需要用到装饰的就是我们点一杯咖啡,用装饰去包裹即可,层层包裹,案例如下:
比如我要点一份加糖加奶的拿铁咖啡
代码实例:
component抽象构件角色:
package com.decoratorPattern.starBucks; /** * @author wang * @version 1.0 * @packageName com.decoratorPattern.starBucks * @className Drink * @date 2021/12/28 10:28 * @Description 饮料构件类抽象component */ public abstract class Drink { private String description; private float price = 0.0f; public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public float getPrice() { return price; } public void setPrice(float price) { this.price = price; } /** * @Date 2021/12/28 10:30 * @Param * @Return float * @MetodName cost * @Author wang * @Description 计算花费,订单价格 */ public abstract float cost(); }
装饰者类:
package com.decoratorPattern.starBucks; /** * @author wang * @version 1.0 * @packageName com.decoratorPattern.starBucks * @className Decorator * @date 2021/12/28 10:40 * @Description 装饰者定义类,配料 */ public class Decorator extends Drink { private Drink drink; /** * @param drink * @Date 2021/12/28 10:42 * @Param * @Return null * @MetodName Decorator * @Author wang * @Description 传入一个被装饰者,由装饰者进行装饰 */ public Decorator(Drink drink) { this.drink = drink; } /** * @Date 2021/12/28 10:43 * @Param * @Return float * @MetodName cost * @Author wang * @Description 装饰者的价格加上被装饰者的价格 */ @Override public float cost() { return super.getPrice() + drink.cost(); } /** * @Date 2021/12/28 10:44 * @Param * @Return String * @MetodName getDescription * @Author wang * @Description 输出订单信息,包含装饰者,装饰者的价格,以及被装饰者的信息 */ @Override public String getDescription() { return drink.getDescription() + "\n加入的材料:" + super.getDescription() + "\t材料价格:" + super.getPrice() ; } }
package com.decoratorPattern.starBucks; /** * @author wang * @version 1.0 * @packageName com.decoratorPattern.starBucks * @className Coffee * @date 2021/12/28 10:31 * @Description 咖啡类 */ public class Coffee extends Drink{ @Override public float cost() { return super.getPrice(); } }
具体构件类:拿铁
package com.decoratorPattern.starBucks; /** * @author wang * @version 1.0 * @packageName com.decoratorPattern.starBucks * @className latte * @date 2021/12/28 10:32 * @Description 拿铁咖啡实类,被装饰者 */ public class Latte extends Coffee{ public Latte() { setDescription("拿铁咖啡"); setPrice(15.0f); } }
具体构件类:摩卡
package com.decoratorPattern.starBucks; /** * @author wang * @version 1.0 * @packageName com.decoratorPattern.starBucks * @className Mocha * @date 2021/12/28 10:36 * @Description 摩卡咖啡实类,被装饰者 */ public class Mocha extends Coffee { public Mocha() { setDescription("摩卡咖啡"); setPrice(12.2f); } }
其他同上,不过多展示
具体装饰类:牛奶
package com.decoratorPattern.starBucks; /** * @author wang * @version 1.0 * @packageName com.decoratorPattern.starBucks * @className Milk * @date 2021/12/28 10:47 * @Description 牛奶调味品,具体装饰者 */ public class Milk extends Decorator{ /** * @param drink * @Date 2021/12/28 10:42 * @Param * @Return null * @MetodName Decorator * @Author wang * @Description 传入一个被装饰者,由装饰者进行装饰 */ public Milk(Drink drink) { super(drink); setDescription("牛奶"); setPrice(1.0f); } }
具体装饰:糖
package com.decoratorPattern.starBucks; /** * @author wang * @version 1.0 * @packageName com.decoratorPattern.starBucks * @className sugar * @date 2021/12/28 10:50 * @Description 糖,装饰者 */ public class Sugar extends Decorator{ /** * @param drink * @Date 2021/12/28 10:42 * @Param * @Return null * @MetodName Decorator * @Author wang * @Description 传入一个被装饰者,由装饰者进行装饰 */ public Sugar(Drink drink) { super(drink); setDescription("糖"); setPrice(0.5f); } }
订单测试代码:
package com.decoratorPattern.starBucks; /** * @author wang * @version 1.0 * @packageName com.decoratorPattern.starBucks * @className OrderTest * @date 2021/12/28 10:51 * @Description 前台订单类 */ public class OrderTest { public static void main(String[] args) { //点一份加糖加奶的拿铁咖啡 System.out.println("+++++++没加任何东西+++++++"); Drink latte = new Latte(); System.out.println("当前总价:" + latte.cost()); System.out.println("coffee:" +latte.getDescription()); //加糖 System.out.println("+++++++加糖后+++++++"); latte = new Sugar(latte); System.out.println("当前总价:" + latte.cost()); System.out.println("coffee:" + latte.getDescription()); System.out.println("+++++++加奶后+++++++"); latte = new Milk(latte); System.out.println("当前总价:" + latte.cost()); System.out.println("coffee:" +latte.getDescription()); } } /** * +++++++没加任何东西+++++++ * 当前总价:15.0 * coffee:拿铁咖啡 * +++++++加糖后+++++++ * 当前总价:15.5 * coffee:拿铁咖啡 * 加入的材料:糖 材料价格:0.5 * +++++++加奶后+++++++ * 当前总价:16.5 * coffee:拿铁咖啡 * 加入的材料:糖 材料价格:0.5 * 加入的材料:牛奶 材料价格:1.0 * * Process finished with exit code 0 */
综上, 如果我们需要新的咖啡种类或者是新的调料,只需要新增类去继承coffee或者decorator类即可。
四、总结
装饰模式是为已有的功能动态的添加更多功能的一种方式,当系统需要新功能的时候,向旧的类中添加新的代码,这些新加的代码通常装饰了原有类的核心职责或主要行为。
优点:
把类中装饰功能从类中移除,这样可以简化原来的类,
有效的把类的核心职责和装饰功能分开了,而且可以去除相关类中的重复装饰逻辑
可代替继承。
到此这篇关于详解Java设计模式中的装饰模式的文章就介绍到这了,更多相关Java装饰模式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!