设计模式本质上就是“SOLID设计原则”在实际应用中的具体体现,我们在实际开发中要尽量面向抽象编程、面向接口编程。顾客->菜单<-厨师,顾客面向菜单点菜,厨师面向菜单做菜,顾客点的菜必须在菜单范围内,厨师能做的菜也必须在菜单范围内,菜单即接口,这就是面向接口编程思想,做项目的时候,先定义接口,再定义实现接口的类,才算是面向抽象编程、面向接口编程。
装饰者模式的作用:能够在不修改目标类也不使用继承的情况下,动态地对类进行扩展,它是通过创建一个包装对象(装饰者)来达到增强目标类的目的。
装饰者模式的实现有两个要求:
1、装饰者类与目标类要实现相同的接口,或继成自同一类
2、装饰者类中要有目标类的引用作为成员变量,实际赋值一般通过带参构造器完成
第一步:基本装饰者模式
本例中只能对目标类进行单一装饰,核心部分是通过带参构造方法把目标类传到装饰者类当中,然后在装饰者类中对目标类进行装饰(即增强)。
程序结构图如下:
public interface ISomeService {
public String doSome();
}
public class SomeService implements ISomeService {
@Override
public String doSome() {
return "abcabc";
}
}
public class SomeServiceDecorator implements ISomeService {
private ISomeService target;
public SomeServiceDecorator() {}
public SomeServiceDecorator(ISomeService target) {
this.target = target;
}
@Override
public String doSome() {
return target.doSome().toUpperCase();
}
}
public class MyTest {
public static void main(String[] args) {
ISomeService target = new SomeService();
ISomeService decorator = new SomeServiceDecorator(target);
System.out.println(decorator.doSome());
}
}
第二步:装饰者链
本例中可以对目标类进行多种装饰,核心部分是通过带参构造方法把目标类传到装饰者类当中,然后在装饰者类中对目标类进行装饰(即增强)。
之所以在这里能够形成装饰者链,是因为带参构造器中的参数是接口类型的,这样才能接收多种类型的目标类,这也算是体现了面向抽象编程、面向接口编程的好处吧。
第二步与第一步的主要区别是装饰者类的命名方式不同,如果单纯从代码的角度来考虑,第一步也可以实现链式装饰,但是第一步中的装饰者类名是“SomeServiceDecorator”,如果想实现链式装饰难道再建一个“SomeServiceDecorator2”?这显然不合适,“SomeServiceDecorator”表示对SomeService进行综合装饰,就不应该再出现其他装饰者;如果想对目标类进行逐步装饰的话,就应该使用第二步的命名方式,用具体的装饰功能进行命名,这样才更符合OOP思想,也更具有可读性。
不同的命名方式体现了不同的设计思想,现在我对这一理念有了更丰富的理解。
程序结构图如下:
public interface ISomeService {
public String doSome();
}
public class SomeService implements ISomeService {
@Override
public String doSome() {
return "abcabc";
}
}
public class ToUpperDecorator implements ISomeService {
private ISomeService target;
public ToUpperDecorator() {}
public ToUpperDecorator(ISomeService target) {
this.target = target;
}
@Override
public String doSome() {
return target.doSome().toUpperCase();
}
}
public class ReplaceDecorator implements ISomeService {
private ISomeService target;
public ReplaceDecorator() {}
public ReplaceDecorator(ISomeService target) {
this.target = target;
}
@Override
public String doSome() {
return target.doSome().replace("A", "*");
}
}
public class MyTest {
public static void main(String[] args) {
ISomeService target = new SomeService();
ISomeService toUpper = new ToUpperDecorator(target);
ISomeService replace = new ReplaceDecorator(toUpper);
System.out.println(replace.doSome());
}
}
第三步:进一步抽象
有没有发现ToUpperDecorator、ReplaceDecorator中都有一个ISomeService类型的target变量,这可是重复代码哇,为了构建稳定、高效、可维护的程序,我们应该尽量减少重复代码。在这里我们为这些装饰者建一个实现了ISomeService接口的父类SomeServiceWrapper,父类中有target变量,此时子类就不需要实现ISomeService了,直接继承SomeServiceWrapper即可。
注意装饰链的装饰顺序:
ISomeService replace = new ReplaceDecorator(toUpper);是把toUpper作为参数传到replace中,简化之后的执行顺序大概是这样的xxx.toUpper().replace(),因此是toUpper()先执行,replace()后执行。
public interface ISomeService {
public String doSome();
}
public class SomeService implements ISomeService {
@Override
public String doSome() {
return "abcabc";
}
}
public class SomeServiceWrapper implements ISomeService {
private ISomeService target;
public SomeServiceWrapper() {}
public SomeServiceWrapper(ISomeService target) {
this.target = target;
}
@Override
public String doSome() {
return target.doSome();
}
}
public class ToUpperDecorator extends SomeServiceWrapper {
public ToUpperDecorator() {}
public ToUpperDecorator(ISomeService target) {
super(target);
}
@Override
public String doSome() {
return super.doSome().toUpperCase();
}
}
public class ReplaceDecorator extends SomeServiceWrapper {
public ReplaceDecorator() {}
public ReplaceDecorator(ISomeService target) {
super(target);
}
@Override
public String doSome() {
return super.doSome().replace("A", "*");
}
}
public class MyTest {
public static void main(String[] args) {
ISomeService target = new SomeService();
ISomeService toUpper = new ToUpperDecorator(target);
ISomeService replace = new ReplaceDecorator(toUpper);
System.out.println(replace.doSome());
}
}