Decorator Pattern-ALOOGY WORLD (aloogychendl23.asia)
题目要求
喜羊羊逃命游戏:喜羊羊被灰太狼追,喜羊羊最多5条命,灰太狼每咬到喜羊羊一次,喜羊羊就要少一条命。在逃的过程中喜羊羊可以吃到三种苹果,吃“红苹果”可以给喜羊羊加上保护罩,吃“绿苹果”可以加快喜羊羊奔跑速度,吃“黄苹果”可以使喜羊羊趟着水跑。应用装饰模式,用JAVA控制台应用程序实现该设计。绘制该模式的UML图。
提示:
这个例子如果用类的继承来实现的话那可就麻烦了,你需要为喜羊羊派生321=6个子类(有保护罩的喜羊羊,奔跑速度加快的喜羊羊,会趟水的喜羊羊,既有保护罩又会趟水的喜羊羊,奔跑速度快且会趟水的喜羊羊,有保护罩且奔跑速度快的喜羊羊,有保护罩、奔跑速度快且会趟水的喜羊羊),如果使用装饰模式的那就不用派生诸多子类了,当喜羊羊每吃到一个苹果,我们就用装饰模式给喜羊羊加一个动态增加一个新功能即可。
代码编写:
1.抽象构件
package sheep; //抽象构件 interface Sheep { void action(); int getLives(); boolean hasShield(); void setHasShield(boolean hasShield); boolean canSwim(); void setCanSwim(boolean canSwim); }
2.具体构件
package sheep; //具体构件 class BasicSheep implements Sheep { private int lives; private boolean hasShield; private boolean canSwim; public BasicSheep(int initialLives) { this.lives = initialLives; this.hasShield = false; this.canSwim = false; } @Override public void action() { System.out.println("喜羊羊被咬了!生命值减少!当前生命值:" + this.lives); if (this.hasShield) { System.out.println("保护罩抵消了一次伤害!"); this.hasShield = false; // 失去保护罩 } else { this.lives--; } } @Override public int getLives() { return lives; } @Override public boolean hasShield() { return hasShield; } @Override public boolean canSwim() { return canSwim; } @Override public void setHasShield(boolean hasShield) { this.hasShield = hasShield; } @Override public void setCanSwim(boolean canSwim) { this.canSwim = canSwim; } }
3.抽象装饰类
package sheep; //抽象装饰类 abstract class SheepDecorator implements Sheep { protected Sheep sheep; public SheepDecorator(Sheep sheep) { this.sheep = sheep; } @Override public void action() { sheep.action(); } @Override public int getLives() { return sheep.getLives(); } @Override public boolean hasShield() { return sheep.hasShield(); } @Override public boolean canSwim() { return sheep.canSwim(); } @Override public void setHasShield(boolean hasShield) { sheep.setHasShield(hasShield); } @Override public void setCanSwim(boolean canSwim) { sheep.setCanSwim(canSwim); } }
4.具体装饰类-保护罩
package sheep; //具体装饰类 - 保护罩 class ShieldDecorator extends SheepDecorator { public ShieldDecorator(Sheep sheep) { super(sheep); } @Override public void action() { super.action(); System.out.println("吃到红苹果!喜羊羊获得保护罩!"); this.setHasShield(true); } }
5.具体装饰类-加速
package sheep; //具体装饰类 - 加速 class SpeedUpDecorator extends SheepDecorator { public SpeedUpDecorator(Sheep sheep) { super(sheep); } @Override public void action() { super.action(); System.out.println("吃到绿苹果!喜羊羊奔跑速度加快!"); } }
6.具体装饰类 - 趟水
package sheep; //具体装饰类 - 趟水 class SwimDecorator extends SheepDecorator { public SwimDecorator(Sheep sheep) { super(sheep); } @Override public void action() { super.action(); System.out.println("吃到黄苹果!喜羊羊可以趟水逃跑!"); } }
7. 测试函数MAIN
package sheep; //主程序 public class Main { public static void main(String[] args) { // 创建基础喜羊羊对象 Sheep basicSheep = new BasicSheep(5); System.out.println("初始生命值:" + basicSheep.getLives() + ",是否有保护罩:" + basicSheep.hasShield() + ",是否能趟水:" + basicSheep.canSwim()); // 吃到红苹果,添加保护罩 SheepDecorator shieldSheep = new ShieldDecorator(basicSheep); shieldSheep.action(); System.out.println("当前生命值:" + shieldSheep.getLives() + ",是否有保护罩:" + shieldSheep.hasShield() + ",是否能趟水:" + shieldSheep.canSwim()); // 再次吃到绿苹果,加速 SheepDecorator speedUpSheep = new SpeedUpDecorator(shieldSheep); speedUpSheep.action(); System.out.println("当前生命值:" + speedUpSheep.getLives() + ",是否有保护罩:" + speedUpSheep.hasShield() + ",是否能趟水:" + speedUpSheep.canSwim()); // 再次吃到黄苹果,趟水 SheepDecorator swimSheep = new SwimDecorator(speedUpSheep); swimSheep.action(); System.out.println("当前生命值:" + swimSheep.getLives() + ",是否有保护罩:" + swimSheep.hasShield() + ",是否能趟水:" + swimSheep.canSwim()); // 判断是否逃脱 if (basicSheep.getLives() > 0) { System.out.println("喜羊羊成功逃脱了!剩余生命值:" + basicSheep.getLives()); } else { System.out.println("喜羊羊没有逃脱,被抓到了!生命值已经耗尽!"); } } }
测试结果
UML
装饰模式的基本概念
一个简陋的房子,它可以让人在里面居住,为人遮风避雨,但如果给它进行装修,那么它的居住环境就更加宜人了。程序中的对象也与房子十分类似,首先有一个相当于“房子”的对象,然后经过不断装饰,不断对其增加功能,它就变成了使用功能更加强大的对象。像这样不断的给对象添加功能的模式称为装饰模式。
1.装饰模式的概述
在软件设计中,装饰模式是一种用于替代继承的技术,通过无需定义子类的方式动态地增强对象的功能,使用对象间关联关系替代类之间的继承关系。在装饰模式中,引入了装饰类,装饰类不仅可以调用原有类中的方法,还可以添加新的方法以扩展原有类的功能。
2.装饰模式的基本组成
-
Component(抽象构件类):该角色通常是抽象类或者接口,它是具体构件类和抽象装饰类的共同父类,声明了具体构件类需要实现的方法,它的引入可以使客户端一致地处理未被装饰的对象以及装饰之后的对象,实现对客户端的透明操作。
-
ConcreteComponent(具体构件类):该角色是抽象构件类的子类,实现了在抽象构件类中声明的方法,装饰类可以给该角色添加职责。
-
Decorator(抽象装饰类):它也是抽象构件类的子类,用于给具体构件类添加新功能,但具体功能实现交由其子类完成。它还维护了一个指向抽象构件类的引用,通过这个引用可以调用装饰前对象的原有方法,通过子类扩展这个方法,来达到给具体构件类装饰的效果。
-
ConcreteDecorator(具体装饰类):它是抽象装饰类的子类,负责向构件添加新的功能。具体装饰类可以调用抽象装饰类中定义的方法,还可以添加新的方法,用以扩展对象的行为。
3.装饰模式的优缺点与适用环境:
装饰模式降低了系统的耦合度,可以动态的添加或删除原有对象的职责,使具体装饰类和具体构件类可以独立变化、新增,减少类爆炸,提高系统的可扩展性和可维护性,装饰模式是取代继承复用的有效方式之一。
优点:
-
可以动态的给对象添加新功能,通过运行时选择不同的具体装饰类给对象添加不同的新功能
-
使用对象关联方式替代继承复用方式,符合合成复用原则,大大减少了类爆炸
-
可以进行多次装饰,通过对具体装饰类进行排列组合,可以创造出很多种不同的新行为
-
具体构件类和具体装饰类可以独立变化,扩展新功能时,不需要修改系统现有代码,符合开闭原则。
缺点:
-
缺点:
-
装饰模式会生成大量的功能类似的小对象(具体装饰类)
-
装饰模式可以进行多次装饰达到对原有对象功能的增强,但在排查、定位问题时也会特别麻烦,需要逐级排查,提高问题排查成本。
适用环境:
希望以透明、动态的方式给单个对象添加职责
不能通过继承的方式对现有系统的类进行功能扩展时,可以考虑使用装饰模式进行功能扩展。不能使用继承方式的原因有两大类:一是系统现有大量独立的类,如果通过继承方式进行扩展,可能会出现类爆炸;二是原有类被final关键字修饰,无法修改。
小结:
装饰模可以动态地给一个对象增加一些职责。就扩展功能而言,装饰模式提供了一种比使用子类更加灵活的替代方案
装饰模式包含抽象构件、具体构件、抽象装饰类和具体装饰类四个角色
装饰模式分为透明装饰模式和半透明装饰模式。透明装饰模式下,客户端可以一致地对待装饰前和装饰后对象;半透明装饰模式下,客户端可以单独调用具体装饰类的新功能
装饰模式优点是可以动态给对象添加新功能、减少系统类爆炸、扩展功能符合开闭原则。缺点是会生成大量小对象,一定程度上会影响系统的性能,还会增加系统的排查问题成本
当希望可以动态、透明地给对象添加功能或不能通过继承方式扩展类功能时,可以考虑使用装饰模式。