装饰模式
装饰模式(Decotator),动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生产子类更为灵活。
说白了,这个ConcreteComponent具体对象,就是我们最终需要的。我们可以通过Decorator来丰富ConcreteComponent具体对象,也就是给它添加一些职责,但这个职责的顺序是可以变化的(个人感觉这也是跟建造者模式的最大区别,不然这两个设计模式还是有点像)
大话设计模式里有这么一句话感觉特别好,学习设计模式要善于变通,如果只有一个ConcreteComponent类而没有抽象的Component类,那么Decorator类可以是ConcreteComponent的一个子类。同样道理,如果只有一个ConcreteDecorator类,那么就没有必要建立一个单独的Decorator类,而可以把Decorator和ConcreteDecorator的责任合并成一个类。
装饰模式主要是为了有效地把类的核心职责和装饰功能区分开,因为我们有了Decorator装饰抽象类,可以从外部类来扩展功能。
我们以动物行为来写个例子:
我们知道动物都有技能(吃,喝,睡觉),我们现在有这么一个具体的狗对象类,那么我们在给狗丰富技能时,如何让丰富技能这个操作分离出去?这个时候就可以用到装饰模式了。
1.一个抽象的Animal动物类,拥有一个技能抽象方法。
考虑下为什么这里不用接口?这是因为如果用接口的话,下面的装饰类就不能运用多态特性。
/**
* 抽象动物基类,动物一些基本技能
* @author lizhibiao
* @date 2019/2/1 15:38
*/
public abstract class Animal
{
/**
* 动物技能抽象方法
*/
public abstract void skill();
}
2.一个具体动物对象类,这里是狗(当然我们还可以继续增加猫类,鸟类。。。)
让狗对象类继承抽象的动物类,并实现skill()方法。
/**
* 具体的狗对象,实现抽象父类
* @author lizhibiao
* @date 2019/2/1 15:22
*/
public class Dog extends Animal
{
@Override
public void skill()
{
System.out.println("动物都会跑");
}
}
3.增加一个抽象的技能装饰类,这个类最主要是通过传入具体对象类(例如:狗),实现抽象的skill()方法,调用具体对象的技能方法。
/**
* 抽象的技能装饰类
* 注意这里一定要重新抽象类的抽象方法
* @author lizhibiao
* @date 2019/2/1 15:31
*/
public abstract class DecoratorBehavior extends Animal
{
private Animal animal;
/**
* 普通的实例化方法,这里传入的是Animal的子类,也就是具体对象
* @param animal animal
*/
public void setAnimal(Animal animal)
{
//多态,Animal指向子类的实例
//为什么可以多态,因为继承关系可以向上转型,也就是子类可以向上转型成父类,除了能够引用父类的共性外,还可以使用子类强大的功能。
this.animal = animal;
}
/**
* 重新实现技能方法,这里是调用具体对象的方法(例如,当前是狗类,那么调用的是狗类的“动物都会跑”)
*/
@Override
public void skill()
{
if (null != animal)
{
//这里调用的是具体对象的skill()方法
//例如,当前的animal是狗类,那么调用的是狗的skill
//例如,当前的animal是猫类,那么调用的是猫的skill
animal.skill();
}
}
}
4.我们要给狗添加警戒和睡觉的功能,于是添加两个技能类
继承自DecoratorBehavior,实现skill方法,并先调用父类方法,然后调用自己特有方法
/**
* 狗的看家技能
* @author lizhibiao
* @date 2019/2/1 15:53
*/
public class Guard extends DecoratorBehavior
{
@Override
public void skill()
{
//先实现父类的方法
super.skill();
canGuard();
}
private void canGuard()
{
System.out.println("狗还会看家");
}
}
/**
* 睡觉技能
* @author lizhibiao
* @date 2019/2/1 16:39
*/
public class Sleep extends DecoratorBehavior
{
@Override
public void skill()
{
//实现父类的所有技能
super.skill();
sleep();
}
/**
* 睡觉方法
*/
private void sleep()
{
System.out.println("睡觉真是件舒服的事情!");
}
}
5.客户端测试类
/**
* 客户端测试类
* @author lizhibiao
* @date 2019/2/1 16:17
*/
public class Client
{
public static void main(String[] args)
{
//先new一个具体狗对象类
Dog dog = new Dog();
//new一个警戒技能
Guard guard = new Guard();
//子类狗调用父类DecoratorBehavior的setAnimal()方法,将狗对象类传入,多态
//此时的DecoratorBehavior的成员属性Animal已经是子类狗对象类
guard.setAnimal(dog);
//new一个睡觉技能
Sleep sleep = new Sleep();
//将警戒对象传入父类DecoratorBehavior的setAnimal()方法
//此时的DecoratorBehavior的成员属性Animal已经变成警戒对象
sleep.setAnimal(guard);
//按照Dog的skill()方法--->Guard的canGuard()方法--->sleep的sleep()顺序执行
sleep.skill();
}
}
这里的执行顺序比较难理解,我们来理理思路,执行到下面代码:
sleep.setAnimal(guard)
sleep.skill()
到这里了其实当前的类成员属性Animal已经是Guard警戒对象类了
所以sleep.skill(),这里会先调用警戒类Guard的skill()方法,再调用sleep()睡觉方法
/**
* 睡觉技能
* @author lizhibiao
* @date 2019/2/1 16:39
*/
public class Sleep extends DecoratorBehavior
{
@Override
public void skill()
{
//实现父类的所有技能
super.skill();
sleep();
}
而警戒类又会先去调用Dog的skill()方法“动物都会跑”,再去调用警戒方法canGuard()
/**
* 狗的看家技能
* @author lizhibiao
* @date 2019/2/1 15:53
*/
public class Guard extends DecoratorBehavior
{
@Override
public void skill()
{
//先实现父类的方法
super.skill();
canGuard();
}
所以打印的顺序结果如下:
动物都会跑
狗还会看家
睡觉真是件舒服的事情!
最后总结下:
我们可以看出,装饰模式虽然将技能的丰富给分离出去了,但是我们发现,每次增加一个技能时都需要增加一个小类(例如:我要再给增加会啃骨头、会找到回家的路。。。就需要再增加类),这样会产生很多小对象。