案例引申
1、汽车栗子
大众旗下有好多款汽车,其中有奥迪A8L、帕萨特等等 ,这些汽车都有基础价格。汽车有一项可选美容项目叫贴膜,给汽车贴膜时还要另外收取费用。一个很符合生活的栗子,接下来我们就以面向对象的手段,以装饰者模式来实现下。
2、继承方式实现
借助面向对象思想会抽象出一个Car类
- 这个类有个desc方法用于描述是哪款汽车产品
- 这个类有个cost方法用于描述汽车价格。
由于汽车有不同产品,因此这些不同产品都需要继承Car这个父类,然后进行自己的实现。在自己的默认实现中通过desc来描述是哪款汽车。在cost中来描述默认汽车价格。
当然汽车还可以进行贴膜,贴膜是需要收取一定费用的,这就导致汽车的价格比基础价格高。不同的贴膜价格可能不同。
3、使用继承实现的缺点
扩展性不好
-
如上栗子中有两款贴膜,假如又新增了一款贴膜,则我们需要分别为AudiA8L、Passat定义新的子类。
-
如上栗子只有两款产品,两款贴膜,假如贴膜种类比较多,此时新研制一款汽车Porsche,则需要为Porsche定义一堆贴膜子类。
为了解决这些弊端,装饰者出现了。
装饰者
1、装饰者定义
在不改变现有对象结构的情况下,动态的给对象增添一些职责,拓展功能。
2、装饰者中的角色
- 抽象接口:定义一个抽象接口以规范接收附加责任的对象。
- 被装饰者:实现抽象接口,通过装饰者为其拓展功能。
- 抽象装饰者:继承或者实现抽象接口,并持有被装饰者实例。(面向接口编程,其实这个角色缺少不影响)
- 具体装饰者:继承或者实现抽象装饰者,由于持有被装饰者的引用,所以很轻松操作被装饰者方法。
3、实现步骤
大致步骤如下
(1)装饰者 和被装饰者同时实现同一个接口或者继承同一个类
(2)装饰者中要有被装饰着的引用
(3)对需要增强的方发进行增强。对不需要增强的方法调用原来的方法。
4、具体实现
(1)抽象接口
public interface IVolkswagen {
void desc();void cost();
}
(2) 被装饰者
/*** Create by SunnyDay on 2019/04/07* 产品:AudiA8*/
public class AudiA8 implements IVolkswagen {
@Overridepublic void desc() {
System.out.println("奥迪A8L");}@Overridepublic void cost() {
System.out.println("奥迪A8L:80W");}
}
/*** Create by SunnyDay 2022/10/30 20:29:40**/
public class Passat2023 implements IVolkswagen {
@Overridepublic void desc() {
System.out.println("大众帕萨特 2023款 280TSI 星空精英版");}@Overridepublic void cost() {
System.out.println("大众帕萨特:19w");}
}
(3) 抽象装饰
/*** Create by SunnyDay at 2022/10/28 17:18:49**/
public abstract class Decorator implements IVolkswagen {
protected final IVolkswagen iCar;public Decorator(IVolkswagen iCar) {
this.iCar = iCar;}@Overridepublic void desc() {
iCar.desc();}@Overridepublic void cost() {
iCar.cost();}
}
(4) 具体装饰
这里就简单写两个装饰类~
/*** Create by SunnyDay on 2019/04/07* 装饰者:黑车膜的奥迪*/
public class DecoratorAudiA8CarSkinBlack extends Decorator {
public DecoratorAudiA8CarSkinBlack(IVolkswagen iCar) {
super(iCar);}/*** 不需要操作的方法保持原状。* */@Overridepublic void desc() {
super.desc();}/*** 对需要操作的方法进行增强。* */@Overridepublic void cost() {
System.out.println("奥迪A8L:80.5W");}
}
/*** Create by SunnyDay on 2019/04/07* 装饰者:黑车膜的帕萨特*/
public class DecoratorPassat2023SkinBlack extends Decorator {
public DecoratorPassat2023SkinBlack(IVolkswagen iCar) {
super(iCar);}/*** 不需要操作的方法保持原状。* */@Overridepublic void desc() {
super.desc();}/*** 对需要操作的方法进行增强。* */@Overridepublic void cost() {
System.out.println("大众帕萨特:19.5W");}
}
(5)测试
public class Test {
public static void main(String[] args){
// 1、直接使用类,不使用装饰者。AudiA8 audiA8 = new AudiA8();audiA8.desc();audiA8.cost();System.out.println("---------------------");// 使用装饰者IVolkswagen decoratorAudiA8CarSkinBlack = new DecoratorAudiA8CarSkinBlack(audiA8);decoratorAudiA8CarSkinBlack.desc();decoratorAudiA8CarSkinBlack.cost();System.out.println("---------------------");// 使用装饰者IVolkswagen decoratorPassat2023SkinBlack = new DecoratorPassat2023SkinBlack(new Passat2023());decoratorPassat2023SkinBlack.desc();decoratorPassat2023SkinBlack.cost();}
}奥迪A8L
奥迪A8L:80W
---------------------
奥迪A8L
奥迪A8L:80.5W
---------------------
大众帕萨特 2023款 280TSI 星空精英版
大众帕萨特:19.5W
5、uml
小结
1、模式的优缺
(1)优点分析
装饰者模式通过组合的方式扩展对象的特性,这种方式允许我们在任何时候对对象的功能进行扩展甚至是运行时扩展,而若我们用继承来完成对类的扩展则只能在编译阶段实现,所以在某些时候装饰者模式比继承(inheritance)要更加灵活。
装饰者是完全遵循开闭原则,继承是静态的附加责任,装饰者则是动态的附加责任。
相对继承降低耦合,是继承模式的替代。
(2)缺点分析
会产生过多的相似的对象
2、使用场景总结
(1)不能再使用继承对系统进行拓展维护时
- 系统中有大量独立扩展,为支持其中一种扩展产生大量子类。
- 被final修饰的类
(2)不影响其他对象情况下以动态透明方式给对象添加职责
(3)当对象的功能可以动态的添加、撤销时。
3、 与静态代理区别
相同点:
(1)都要实现与目标类相同的业务接口
(2)都持有目标对象引用
(3)都可以在不修改目标类的前提下增强目标方法
不同点:
(1)目的不同,装饰者是为了增强目标对象。 静态代理的使用目的是为了保护和隐藏目标对象。
这点可看二者引用赋值那一块,代理持有的引用引用在类内部赋值,而装饰者是程序调用者调用装饰者类时外部传入。可见代理模式实现了对目标对象的保护。
(2)功能增强的实现者不同 装饰者设计模式中存在装饰者基类,其并不能实现增强,而是由具体的装饰者进行增强的,所以其存在着"装饰者链"。而静态代理中,一般不存在父子类的关系,具体的增强,就是由代理类实现的。无需其子类完成,所以不存在链的概念。
巩固
jdk源码巩固(file类研究下)
The end
参考1
参考2
code