一、简介
一种结构型模式,它可以在不改变原有对象的情况下,动态的给一个对象扩展新的功能。
一般包括:
- 抽象的被装饰者类(里面定义一些将要被扩展的功能)、
- 被装饰者类(实现基础功能)、
- 抽象的装饰者类(继承抽象的被装饰者类,一般包含一个将要被扩展的被装饰者和声明一些装饰者的特定功能)
- 装饰者类(传入一个被装饰者,重写方法,实现功能扩展)
需要注意的是装饰者模式也建立在继承的基础之上的,类似于继承(装饰者和被装饰者继承同一个类,以便装饰者扩展被装饰者的功能以及装饰者作为被装饰者)+聚合复用(复用被装饰者的功能)
即扩展的功能AB=装饰者中重写功能A(){被装饰者的功能A+额外的功能B},其中原有功能A相当于被装饰者,额外的功能B相当于装饰者,由于装饰者和被装饰者继承了同一个类,扩展的功能AB又可以作为被装饰者装饰,如此一直嵌套下去,其行为类似于递归,返回的条件就是基础功能A。
二、优缺点
它的优点是:
它比继承更加灵活。
因为在有些情况下,需要扩展的功能的种类很多,比如对于基础功能A,我们想要得到AB,AC,ABB等扩展的功能,势必生成很多子类,增加系统的复杂性,同时,使用继承实现功能扩展,我们必须事先知道所有要实现的扩展功能,然后以每个扩展功能分别对应一个子类的形式来实现它们,也就是这些功能是编译时就确定了,是静态的。
而使用装饰者模式可以在运行时决定,通过使用不同装饰类以及这些类的排列组合,可以动态的实现不同的扩展功能。另外在扩展功能时也符合开闭原则
即如果想扩展被装饰者类的行为,无须修改装饰者抽象类,只需继承装饰者抽象类,实现额外的一些装饰或者叫行为即可对被装饰者进行包装
它的缺点是动态装饰时,多层装饰时会更复杂(使用继承来拓展功能会增加类的数量,使用装饰者模式不会像继承那样增加那么多类的数量但是会增加对象的数量,当对象的数量增加到一定的级别时,无疑会大大增加我们代码调试的难度)
三、举例
1.定义抽象的被装饰者(面条)
abstract class Noodle{
public abstract String getDesc();
public abstract int getPrice();
}
2.定义具体的被装饰者(炒面),被装饰者的初始状态或者称为主体
class FriedNoodle extends Noodle{
@Override
public String getDesc() {
return "炒面 ";
}
@Override
public int getPrice() {
return 10;
}
}
3.定义抽象的装饰者(和面相关的一些调料)
abstract class Others extends Noodle{
private Noodle noodle;//将要被装饰的被装饰者
public Others(Noodle noodle){
this.noodle=noodle;
}
public Noodle getNoodle() {
return noodle;
}
public abstract void printInfo();
}
4.定义具体的装饰者(如鸡蛋,肉丝)——鸡蛋
class NoodleWithEgg extends Others{
public NoodleWithEgg(Noodle noodle){
super(noodle);//初始化被装饰者
}
@Override
public void printInfo() {
if(getNoodle() instanceof Others){
((Others)getNoodle()).printInfo();
}
System.out.println("装饰者为:鸡蛋,"+"被装饰者为:"+getNoodle().getDesc());
}
@Override
public String getDesc() {
return getNoodle().getDesc()+"加一个鸡蛋 ";
}
@Override
public int getPrice() {
return getNoodle().getPrice()+1;
}
}
4.定义具体的装饰者(如鸡蛋,肉丝)——肉丝
class NoodleWithMeat extends Others{
public NoodleWithMeat(Noodle noodle){
super(noodle);//初始化被装饰者
}
@Override
public void printInfo() {
if(getNoodle() instanceof Others){
((Others)getNoodle()).printInfo();
}
System.out.println("装饰者为:肉丝,"+"被装饰者为:"+getNoodle().getDesc());
}
@Override
public String getDesc() {
return getNoodle().getDesc()+"加一份肉丝 ";
}
@Override
public int getPrice() {
return getNoodle().getPrice()+2;
}
}
测试:
public class 装饰者模式 {
public static void main(String[] args) {
//订一份炒粉,加两个鸡蛋,一份肉丝(最外层的装饰者是肉丝,被装饰者是炒粉+两个鸡蛋)
//1.先做好主体——炒粉
FriedNoodle friedNoodle=new FriedNoodle();
//2.用一个鸡蛋对炒粉进行装饰
NoodleWithEgg egg1_Noodle=new NoodleWithEgg(friedNoodle);
//3.再用一个鸡蛋对上面的(1个鸡蛋+炒粉)进行装饰
NoodleWithEgg egg2_Noodle=new NoodleWithEgg(egg1_Noodle);
//4.再用一份肉丝对上面的(2个鸡蛋+炒粉)进行装饰,大功告成
NoodleWithMeat myNoodle=new NoodleWithMeat(egg2_Noodle);
//显示我们的订餐信息,及价格
System.out.println(myNoodle.getDesc()+"价钱:"+myNoodle.getPrice());
//显示我们的装饰过程
myNoodle.printInfo();
}
}
运行结果:
炒面 加一个鸡蛋 加一个鸡蛋 加一份肉丝 价钱:14
装饰者为:鸡蛋,被装饰者为:炒面
装饰者为:鸡蛋,被装饰者为:炒面 加一个鸡蛋
装饰者为:肉丝,被装饰者为:炒面 加一个鸡蛋 加一个鸡蛋
四、扩展
装饰者和代理模式
装饰者模式关注的是对象的动态添加功能。代理模式关注的是对对象的控制访问,对它的用户隐藏对象的具体信息。