结构型设计模式包括:适配器模式、装饰者模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
一、装饰者模式(Decorator):
动态的将责任附加到对象上,如果想要扩展功能,装饰者提供了比继承更有弹性的方案。下面是装饰者模式下的类图:
从类图中我们可以发现装饰者(Decorator)与被装饰者(或者在图中叫构件ConcreteComponent)他们之间都拥有一个公共的父类,继承与Component,而且不管是装饰者还是被装饰者他们都拥有共同的方法,再往下看,Decorator的子类中拥有一个指向他所装饰的对象的引用,当然在这一层中装饰者也可以扩展Component的属性。以上是一个理论类图模型,下面我们看一下一个实际的使用中的类图模型。
在图中,饮料Beverage有两类子类,一类是实际的基本饮料类型(图左),另一种则是对应的调料类型(图右),他们就分别对应了被装饰者和装饰着两种类型。以下是创建一个饮料时候的代码
public static void main(String args[]) {
Beverage beverage = new Espresso();
System.out.println(beverage.getDescription()
+ " $" + beverage.cost());//创建一个没有装饰的普通饮料
Beverage beverage2 = new DarkRoast();
beverage2 = new Mocha(beverage2);
beverage2 = new Mocha(beverage2);
beverage2 = new Whip(beverage2);
System.out.println(beverage2.getDescription()
+ " $" + beverage2.cost());//创建一个被三个调料修饰的饮料
Beverage beverage3 = new HouseBlend();
beverage3 = new Soy(beverage3);
beverage3 = new Mocha(beverage3);
beverage3 = new Whip(beverage3);
System.out.println(beverage3.getDescription()
+ " $" + beverage3.cost());//创建一个被三个调料修饰的饮料
}
可以看出来需要将被装饰者传入到装饰者对象中。
public class Mocha extends CondimentDecorator {
Beverage beverage;
public Mocha(Beverage beverage) {
this.beverage = beverage;
}
public String getDescription() {
return beverage.getDescription() + ", Mocha";
}
public double cost() {
return .20 + beverage.cost();
}
}
从上面的Beverage 属性,和cost方法的实现从可以看出装饰者模式的核心思想,就是利用被装饰对象的引用将责任动态的扩展被装饰者对象的功能实现了功能的扩展。
装饰者模式的一个典型应用就是Java中的IO,IO就是一层套一层的实现功能的扩展,但是从中也可以看出装饰者的弊端:小类太多记不过来。
装饰者模式中设计原则的体现:封装变化、多用组合,少用继承、针对接口编程、松耦合度设计、开发扩展,封闭修改。
二、适配器模式(Adapter):
适配器模式就是将一个类的接口,转化成客户期望的另一个接口,适配器让原来不兼容的类可以合作无间。
从图中可以看出,一个目标功能的适配器和真正的目标功能实现类具有相同的实现接口(Target),所适配器中具有和实现目标功能类相同的方法,而适配器的实现类在实现这些方法的功能的时候并不是自己实现,而是调用适配器中拥有的被适配对象的对应方法来实现具体的功能。即客户端中想要调用目标类的某个方法,但是目标类暂时不可用,而另外有一个类和目标类的方法实现了相同的功能,但是他们的API不兼容,那么我们就可以写一个适配器,适配器和目标类实现了相同的接口,适配器中拥有一个另一个类的具体实现的引用,适配器在实现目标接口中的方法是只需要将另一个类的对应方法的功能传递到适配器方法就行。这样在客户端看来他还是调用的目标方法,因为接口和方法都一样。
举个例子我们可以用已有的Enumeration接口功能区实现一个Iterator接口的功能(remove方法有点出入)。
另外装饰者和适配器模式比较像,但是记住适配器注重的“功能的隐形传递”装饰者注重的“功能的动态扩展”。
适配器可以用组合(单继承语言中)和继承(多继承语言中)实现。
三、外观模式:
提供统一的接口,用来访问子系统中的一群接口。外观定义了一个高层的接口让子系统对于使用者来说更容易使用,另外也将使用者从子系统中解耦。
外观模式虽然讲子系统中一个个繁杂的操作合并产生简单容易的接口,但是是也不会屏蔽子系统中的具体功能,调用者仍然可以去调用子系统中的具体功能。另外一个系统中可以有多个外观模式。
外观和适配器的区别:虽然两者都是包装接口但是外观注重简化大量复杂的接口,适配器注重转化接口。
四、组合模式:
允许你将对象成树形结构,组合能够让客户以一致性的方式处理个别对象以及对象的组合。