设计模式之 装饰模式
概述:
装饰模式(Decorator Pattern) 又叫装饰者模式;装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
重点(参与者) :
Component 被装饰对象的基类
ConcreteComponent 具体被装饰者对象
Decorator 装饰者抽象类
ConcreteDecorator 具体装饰者
案例:
好的,二话不说,举例来讲述装饰者模式,其他理论放到文章最后。
玩游戏的朋友应该都知道,王者荣耀有个英雄叫做荆轲,最近改名叫做阿珂了;
就以阿珂为例子,我们想让阿珂开局先装备上小打野刀,5分钟过后再装备一件无尽,10分钟以后再装备一件黑切,那么我们只单纯的用继承或者使用接口之类的很难在不修改后台类或者接口代码的情况下实现,那么就引出我们要说的装饰者模式:
1,首先阿珂是个英雄,我们提取出来一个英雄基类,也就是Component 被装饰对象的基类
/** * 英雄基类(被装饰者基类) */ public abstract class Hero { //描述 String desc = "一个英雄"; //获取英雄描述的方法 public String getHeroDesc() { return desc; } //英雄拥有的装备 public abstract String getEquipment(); }
2,其次我们有一个具体的阿珂英雄类 继承英雄基类,也就是ConcreteComponent 具体被装饰者对象
/** * 英雄阿珂(被装饰者) 继承英雄基类(被装饰者基类) */ public class HeroAke extends Hero { public HeroAke() { desc = "英雄阿珂"; } //创建了英雄 就拥有了上场作战的功能 @Override public String getEquipment() { return "上场作战"; } }
3,既然阿珂需要装备,那么我们就还需要一个装备的父类,也就是Decorator 装饰者抽象类
/** * 装备基类(装饰者基类,继承被装饰者的基类,原因在于为了确保是同一类型 可以进行多重装饰) */ public abstract class Equipment extends Hero { //这个抽象方法名称和被装饰者基类的方法名称相同 用来描述自身 public abstract String getHeroDesc(); }
4,有了装备的父类,那么我们就需要有具体的装备(小打野刀,无尽,黑切),也就是ConcreteDecorator 具体装饰者
4.1,小打野刀类
/** * 小打野刀类(具体装饰者类) */ public class SmallFieldKnife extends Equipment { Hero h; public SmallFieldKnife(Hero hero) { this.h = hero; } //英雄描述 @Override public String getHeroDesc() { return h.getHeroDesc() + "购买了小打野刀-"; } //英雄装备 @Override public String getEquipment() { return h.getEquipment() + "装备并使用小打野刀--"; } }
4.2,无尽类
/** * 无尽之刃(具体装饰者类) */ public class InfinitySword extends Equipment { Hero h; public InfinitySword(Hero hero) { this.h = hero; } //英雄描述 @Override public String getHeroDesc() { return h.getHeroDesc() + "购买了无尽之刃-"; } //英雄装备 @Override public String getEquipment() { return h.getEquipment() + "装备并使用无尽之刃--"; } }
4.3,黑切类
/** * 黑色切割者(具体装饰者类) */ public class BlackCutter extends Equipment { Hero h; public BlackCutter(Hero hero) { this.h = hero; } //英雄描述 @Override public String getHeroDesc() { return h.getHeroDesc() + "购买了黑切-"; } //英雄装备 @Override public String getEquipment() { return h.getEquipment() + "装备并使用黑切--"; } }
战场 客户端代码:
public class BattleGround { public static void main(String[] args) { //创建一个阿珂 让他上场作战(被装饰者) Hero ake = new HeroAke(); System.out.println("------游戏开始-------"); //装饰过程 //1,单独使用小打野刀装饰阿珂,阿珂装备并可以使用小打野刀 Equipment sfk = new SmallFieldKnife(ake); System.out.println(sfk.getHeroDesc() + "===" + sfk.getEquipment()); //2,在1的基础上 让阿珂装备并可以使用无尽 //这里就可以看出装饰者基类继承被装饰者基类的意义所在: 为了确保是同一类型 可以进行多重装饰 System.out.println("------5分钟后------"); Equipment wujin = new InfinitySword(sfk); System.out.println(wujin.getHeroDesc() + "===" + wujin.getEquipment()); //3,在2的基础上 让阿珂装备并可以使用黑切 System.out.println("------10分钟后------"); Equipment heiqie = new BlackCutter(wujin); System.out.println(heiqie.getHeroDesc() + "===" + heiqie.getEquipment()); } }
运行结果:
好了,到现在我们就可以在客户端用代码根据需要用装饰者一层层的来装饰被装饰者了。
特点:
1.装饰对象和真实对象有相同的接口。这样客户端对象就能以和真实对象相同的方式和装饰对象交互。
2.装饰对象包含一个真实对象的引用(reference)
3.装饰对象接受所有来自客户端的请求。它把这些请求转发给真实的对象。
4.装饰对象可以在转发这些请求以前或以后增加一些附加功能。这样就确保了在运行时,不用修改给定对象的结构就可以在外部增加附加的功能。在面向对象的设计中,通常是通过继承来实现对给定类的功能扩展。
适用性,以下情况使用Decorator模式:
优点:
1. Decorator模式与继承关系的目的都是要扩展对象的功能,但是Decorator可以提供比继承更多的灵活性。
2. 通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。
缺点:
1. 这种比继承更加灵活机动的特性,也同时意味着更加多的复杂性。
2. 装饰模式会导致设计中出现许多小类,如果过度使用,会使程序变得很复杂。
3.装饰模式是针对抽象组件(Component)类型编程。但是,如果你要针对具体组件编程时,就应该重新思考你的应用架构,以及装饰者是否合适。当然也可以改变Component接口,增加新的公开的行为,实现“半透明”的装饰者模式。在实际项目中要做出最佳选择。