写在模式学习之前
什么是设计模式:在我们进行程序设计时,逐渐形成了一些典型问题和问题的解决方案,这就是软件模式;每一个模式描述了一个在我们程序设计中经常发生的问题,以及该问题的解决方案;当我们碰到模式所描述的问题,就可以直接用相应的解决方法去解决这个问题,这就是设计模式。
设计模式就是抽象出来的东西,它不是学出来的,是用出来的;或许你根本不知道任何模式,不考虑任何模式,却写着最优秀的代码,即使以“模式专家”的角度来看,都是最佳的设计,不得不说是“最佳的模式实践”,这是因为你积累了很多的实践经验,知道“在什么场合代码应该怎么写”,这本身就是设计模式。
有人说:“水平没到,学也白学,水平到了,无师自通”。诚然,模式背熟,依然可能写不出好代码,更别说设计出好框架;OOP理解及实践经验到达一定水平,同时也意味着总结了很多好的设计经验,但"无师自通",却也未必尽然,或者可以说,恰恰是在水平和经验的基础上,到了该系统的学习一下“模式”的时候了,学习一下专家总结的结果,印证一下自己的不足,对于提高水平还是很有帮助的。
本系列的设计模式学习笔记,实际是对于《Java与模式》这本书的学习记录。
适配器(Adapter)模式定义
适配器模式把一个类的接口(源)变换成客户端所期待的另一种接口(目的),从而使 原本 因接口不匹配而无法在一起工作的两个类能够在一起工作。
名称的由来:
就像变压器,把一种电压变换成另一种电压。美国的生活用电110 V,中国的220 V,在中国使用美国的电器,就必须有一个能把220 V电压转换成110 V电压的变压器。而这正像是本模式所做的事,因此此模式也常常被称为变压器模式。
适配器模式的两种形式:
适配器模式有“类的适配器模式”和“对象的适配器模式”两种不同的形式。
类的适配器模式
在这种方式中,适配器(Adapter)和源(Adaptee)是继承的关系。
模式所涉及的角色有:
(1)目标(Target)角色:这就是所期待得到的接口。注意,由于这里讨论的是类适配器模式,因此目标不可以是类。
(2)源(Adaptee)角色:现有需要适配的接口。
(3)适配器(Adapter)角色:适配器类是本模式的核心。适配器把源接口转换成目标接口。显然,这一角色不可以是接口,而必须是具体类。
代码实现:
interface Target
{
void sampleOp1(); //源类有这个方法
void sampleOp2();
}
class Adaptee
{
public void sampleOp1(){};
}
class Adapter extends Adaptee implements Target
{
public void sampleOp2(){}; //由于源类没有这个方法,因此适配器类补充上这个方法
}
对象的适配器模式
在这种方式中,适配器(Adapter)和源(Adaptee)是委派的关系,“适配器”委派“源”,可以通过构造函数或者set方法把“源”传入适配器。
模式所涉及的角色有:
(1)目标(Target)角色:这就是所期待得到的接口。目标也可以是具体的或抽象的类。
(2)源(Adaptee)角色:现有需要适配的接口。
(3)适配器(Adapter)角色:适配器类是本模式的核心。适配器把源接口转换成目标接口。显然,这一角色不可以是接口,而必须是具体类。
代码实现:
interface Target
{
void sampleOp1(); //源类有这个方法
void sampleOp2();
}
class Adaptee
{
public void sampleOp1(){};
}
class Adapter implements Target
{
private Adaptee adaptee;
public Adapter(Adaptee adaptee)
{
super();
this.adaptee = adaptee;
}
//源类有这个方法,直接委派
public void sampleOp1()
{
adaptee.sampleOp1();
}
public void sampleOp2(){}; //由于源类没有这个方法,因此适配器类补充上这个方法
}
在什么情况下使用适配器模式
(1)系统需要使用现有的类,而此类的接口不符合系统的需要。
(2)想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。这些源类不一定有很复杂的接口。
(3)在设计里,如果需要改变多个已有的子类的接口,对于“类的适配器模式”,需要做多个适配器类,分别继承各个子类,这不实际,更经常使用“对象的适配器模式”。
JDK1.2之前没有Iterator(集合迭代器),只有Enumeration,如下代码演示一个例子,提供一个“从Enumeration到Iterator”适配器。
import java.util.*;
public class Enumeration2Iterator implements Iterator
{
Enumeration enu;
public Enumeration2Iterator(Enumeration enu)
{
this.enu = enu;
}
public boolean hasNext()
{
return enu.hasMoreElements();
}
public Object next()
{
return enu.nextElement();
}
public void remove()
{
throw new UnsupportedOperationException();
}
}
缺省适配(Default Adapter)模式
(1)适配器模式还有一个特例,被称作“缺省适配(Default Adapter)模式”,“缺省适配模式”为一个接口提供缺省实现,这里子类型可以从这个缺省实现进行扩展,而不必从原有接口进行扩展。缺省适配模式,是一种平庸化的适配器模式,它对目标接口提供了“平庸化”的实现或者空实现。
(2)缺省适配模式的中心是一个缺省适配类。这个类应当是抽象类,因为这个类不应当实例化,它的实例化也没有用处。
(3)但是缺省适配类所提供的方法却应当是具体的方法,而不是抽象的方法,因为按照模式的用意,这些方法之所以存在,就是为了提供默认实现,以便缺省适配类的具体子类可以按照需要只实现需要实现的方法,忽略不需要实现的方法。
代码演示:
interface 和尚
{
void 吃斋();
void 念经();
void 习武();
}
//这个抽象的天孤星就是一个适配器类,鲁智深借助于“适配器模式”实现了“和尚”接口,达到剃度的目的
abstract class 天孤星 implements 和尚
{
public void 吃斋() {}
public void 念经() {}
public void 习武() {}
}
class 鲁智深 extends 天孤星
{
public void 习武()
{
//do something
}
}
更多了解
(1)如下代码所示的CollectionData也是一个适配器设计模式的实例,它将Generator适配到CollectionData的构造器上。Java编程思想的作者Bruce Eckel认为,这也许并非是适配器的严格定义,但它符合适配器思想的基本精神。
(2)代码如下
import java.util.*;
//Generator是个工具类,用于实例化对象的
interface Generator<T>
{
T next();
}
class BasicGenerator<T> implements Generator<T>
{
private Class<T> type;
public BasicGenerator(Class<T> type)
{
this.type = type;
}
public T next()
{
try
{
return type.newInstance();
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
public static <T> Generator<T> create(Class<T> type)
{
return new BasicGenerator<T>(type);
}
}
//CollectionData是个适配器类,用于把Generator适配到CollectionData上
class CollectionData<T> extends ArrayList<T>
{
public CollectionData(Generator<T> gen,int quality)
{
for(int i=0;i<quality;i++)
{
add(gen.next());
}
}
public static <T> CollectionData<T> list(Generator<T> gen,int quality)
{
return new CollectionData<T>(gen,quality);
}
}
结构模式(Structural Pattern)小结
结构模式(Structural Pattern)一共有七种,分别是:适配器模式、装饰模式、合成模式、代理模式、享元模式、门面模式、桥梁模式。
大致总结如下:
最大特点 | 典型应用 | |
适配器模式 | 利用对象的功能,并转换其接口 | 日常工作,入目尽是适配器,DAO适配,Cache功能适配,等等 |
装饰模式 | 对象层面的功能增强,接口不变 | Java I/O类库的设计 |
合成模式 | 树枝、叶子同样对待 | 分类树、权限树等 |
代理模式 | 代表人 | WebService 的本地代理,权限访问代理,引用计数代理等 |
享元模式 | 共享对象,减小内存占用 | 编译器系统,Java String |
门面模式 | 统一对外接口,方便调用 | 基于SOA框架编程中,不同系统之间的接口 |
桥梁模式 | 解耦 | 大多数的驱动器,包括JDBC Driver |