第三章 结构型模式
1、结构型模式概述
结构型模式描述如何将类或对象按某种布局组成更大的结构。
- 类结构型模式:采用
继承机制
来组织接口和类, - 对象结构型模式:采用
组合或聚合
来组合对象。 - 由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。
结构型模式分为以下7种:
-
代理(Proxy)模式:客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。
-
适配器(Adapter)模式:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
-
桥接(Bridge)模式:将
抽象与实现分离
,使他们可以独立的变化。用组合关系代替继承关系来实现,从而降低了抽象和实现这2个可变维度的耦合度。 -
装饰(Decorator)模式:动态的给对象
增加一些职责
,即增加其额外的功能。 -
外观(Facade)模式:为多个复杂的子系统
提供一个一致的接口
, 使这些子系统更加容易被访问。 -
享元(Flyweight)模式:运用共享技术来有效地
支持大量细粒度 对象的复用
。 -
组合(Composite)模式:
将对象组合成树状层次结构
,使用户对单个对象和组合对象具有一致的访问性。
注:以上7种结构型模式,除了适配器模式分为类结构型模式和对象结构型模式2种,其他的全部属于对象结构型模式。
2、装饰者模式
(1)模式的定义与特点
-
定义:
指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式。
-
优点:
- 采用装饰模式
扩展对象的功能比采用继承方式更加灵活
; 可以设计出多个不同的具体装饰类
,创造出多个不同行为的组合。
- 采用装饰模式
3. 缺点:装饰模式增加了许多子类,如果过度使用会使程序变得很复杂。
(2)模式的结构与实现
- 结构
- 抽象构件(Component)角色 :定义一个抽象接口以规范准备接收附加责任的对象。
- 具体构件(Concrete Component)角色 :实现抽象构件,通过装饰角色为其添加一些职责。
- 抽象装饰(Decorator)角色 : 继承或实现抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
- 具体装饰(ConcreteDecorator)角色 :实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。
结构图如下:
- 实现
例:设计一个“人”类,再使用装饰模式赋予“人”类更强大的本领如“天上飞”、“水下游”以及“力如牛”等而成为“超人”
抽象构件角色:人类接口
// 抽象构件:人类接口,定义了人的规范
public interface Human {
// 人具有的能力
void ability();
}
具体构件角色:人会说话写字
// 具体构件,人会说话,写字
public class man implements Human {
@Override
public void ability() {
System.out.println("说话,写字");
}
}
抽象装饰者角色:超能力
// 抽象装饰者:超能力
public class Powers implements Human {
// 聚合人类
Human human;
public Powers(Human human) {
this.human = human;
}
@Override
public void ability() {
// 人类原有的能力
human.ability();
}
}
具体装饰者角色:天上飞的能力
// 具体装饰者:天上飞的能力
public class Fly extends Powers{
public Fly(Human human) {
super(human);
}
// 增加超能力
public void ability() {
// 1. 原来的能力
super.ability();
// 2. 增加能力
setFly();
}
public void setFly(){
System.out.println("天上飞");
}
}
具体装饰者角色:水上游
// 具体装饰者:水上游的能力
public class Water extends Powers{
public Water(Human human) {
super(human);
}
// 增加超能力
public void ability() {
// 1. 原来的能力
super.ability();
// 2. 增加能力
setWater();
}
public void setWater(){
System.out.println("水上游");
}
}
具体装饰者角色:力如牛
// 具体装饰者:力如牛的能力
public class Cattle extends Powers{
public Cattle(Human human) {
super(human);
}
// 增加超能力
public void ability() {
// 1. 原来的能力
super.ability();
// 2. 增加能力
setCattle();
}
public void setCattle(){
System.out.println("力如牛");
}
}
测试类
public class Client {
public static void main(String[] args) {
// 1. 创建人类,
Human human=new man();
// 2.此时具有的能力有
System.out.println("man 此时有的能力有:");
human.ability();
System.out.println("============");
// 3. 增加超能力fly
System.out.println("man 此时有的能力有:");
human=new Fly(human);
human.ability();
System.out.println("============");
// 4. 增加能力water
System.out.println("man 此时有的能力有:");
human=new Water(human);
human.ability();
// 5.增加能力cattle
System.out.println("man 此时有的能力有:");
human=new Cattle(human);
human.ability();
}
}
(3)应用场景
-
当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。
-
不能采用继承的情况主要有两类:
- 第一类是系统中存在大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长;
- 第二类是因为类定义不能继承(如final类)
-
在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
-
当对象的功能要求可以动态地添加,也可以再动态地撤销时。
(4)扩展
(1)JDK源码解析
IO流中的包装类使用到了装饰者模式。BufferedInputStream
,BufferedOutputStream
,BufferedReader
,BufferedWriter
。
我们以BufferedWriter举例来说明,先看看如何使用BufferedWriter
public class Demo {
public static void main(String[] args) throws Exception{
//创建BufferedWriter对象
//创建FileWriter对象
FileWriter fw = new FileWriter("C:\\Users\\Think\\Desktop\\a.txt");
BufferedWriter bw = new BufferedWriter(fw);
//写数据
bw.write("hello Buffered");
bw.close();
}
}
结构:装饰者模式的精髓就是子类继承的同时再聚合
BufferedWriter使用装饰者模式对Writer子实现类进行了增强,添加了缓冲区,提高了写数据的效率
(2)代理和装饰者的区别
-
相同点:
- 都要实现与目标类相同的业务接口
- 在两个类中都要声明目标对象
- 都可以在不修改目标类的前提下增强目标方法
-
不同点:
- 目的不同
装饰者是为了增强目标对象
静态代理是为了保护和隐藏目标对象 - 获取目标对象构建的地方不同
装饰者是由外界传递进来,可以通过构造方法传递
静态代理是在代理类内部创建,以此来隐藏目标对象
- 目的不同