设计模式
7、适配器模式的概念及Java实现
我们常常在开发中遇到各个系统之间的对接问题,然而每个系统的数据模型或多或少均存在差别,因此可能存在修改现有对象模型的情况,这将影响到系统的稳定。若想在不修改原有代码结构(类的结构)的情况下完成友好对接,就需要用到适配器模式。
适配器模式(Adapter Pattern)通过定义一个适配器类作为两个不兼容的接口之间的桥梁,将一个类的接口转换成用户期望的另一个接口,使得两个或多个原本不兼容的接口可以基于适配器类一起工作。
适配器模式主要通过适配器类实现各个接口之间的兼容,该类通过依赖注入或者继承实现各个接口的功能并对外统一提供服务,可形象地使用图来表示适配器模式。

在适配器模式的实现中有三种角色:Source、Targetable、Adapter。Source是待适配的类,Targetable是目标接口,Adapter是适配器。我们在具体应用中通过Adapter将Source的功能扩展到Targetable,以实现接口的兼容。适配器的实现主要分为三类:类适配器模式、对象适配器模式、接口适配器模式。
7.1、类适配器模式
在需要不改变(或者由于项目原因无法改变)原有接口或类结构的情况下扩展类的功能以适配不同的接口时,可以使用类的适配器模式。适配器模式通过创建一个继承原有类(需要扩展的类)并实现新接口的适配器类来实现。具体的UML设计如图所示。

具体实现如下。
(1)定义Source类:
java
public class Source {
private final static Log logger = LogFactory.getLog(Source.class);
public void editTextFile() {//text文件编辑
logger.info("a text file editing");
}
}
以上代码定义了待适配的Source类,在该类中实现了一个编辑文本文件的方法editTextFile()。
(2)定义Targetable接口:
java
public interface Targetable {
void editTextFile();
void editWordFile();
}
以上代码定义了一个Targetable接口,在该接口中定义了两个方法,editTextFile和editWordFile,其中editTextFile是Source中待适配的方法。
(3)定义Adapter继承Source类并实现Targetable接口:
java
public class Adapter extends Source implements Targetable{
private final static Log logger = LogFactory.getLog(Adapter.class);
@Override
public void editWordFile() {
logger.info("a word file editing");
}
}
以上代码定义了一个Adapter类并继承了Source类实现Targetable接口,以完成对Source类的适配。适配后的类既可以编辑文本文件,也可以编辑Word文件。
(4)使用类的适配器:
java
public static void main(String[] args) {
Targetable target = new Adapter();
target.editTextFile();
target.editWordFile();
}
在使用适配器时只需定义一个实现了Targetable接口的Adapter类并调用target中适配好的方法即可。从运行结果可以看出,我们的适配器不但实现了编辑Word文件的功能,还实现了编辑文本文件的功能,具体的执行结果如下:
java
[INFO] Source - a text file editing
[INFO] Adapter - a word file editing
7.2、对象适配器模式
对象适配器模式的思路和类适配器模式基本相同,只是修改了Adapter类。Adapter不再继承Source类,而是持有Source类的实例,以解决兼容性问题。具体的UML设计如图所示。

具体实现如下。
(1)适配器类的定义如下:
java
public class ObjectAdapter implements Targetable {
private final static Log logger = LogFactory.getLog(ObjectAdapter.class);
private Source source;
public ObjectAdapter(Source source){
super();
this.source = source;
}
@Override
public void editTextFile() {
this.source.editTextFile();
}
@Override
public void editWordFile() {
logger.info("a word file editing");
}
}
以上代码定义了一个名为ObjectAdapter的适配器,该适配器实现了Targetable接口并持有Source实例,在适配editTextFile()的方法时调用Source实例提供的方法即可。
(2)使用对象适配器模式:
java
Source source = new Source();
Targetable target = new ObjectAdapter(source);
target.editWordFile();
target.editTextFile();
在使用对象适配器时首先需要定义一个Source实例,然后在初始化ObjectAdapter时将Source实例作为构造函数的参数传递进去,这样就实现了对象的适配。执行结果如下:
java
[INFO] ObjectAdapter - a word file editing
[INFO] Source - a text file editing
7.3、接口适配器模式
在不希望实现一个接口中所有的方法时,可以创建一个抽象类AbstractAdapter实现所有方法,在使用时继承该抽象类按需实现方法即可。具体的UML设计如图所示。

具体实现如下。
(1)定义公共接口Sourceable:
java
public interface Sourceable {
void editTextFile();
void editWordFile();
}
以上代码定义了Sourceable接口,并在接口中定义了两个方法,editTextFile()和editWordFile()。
(2)定义抽象类AbstractAdapter并实现公共接口的方法:
java
public abstract class AbstractAdapter implements Sourceable{
@Override
public void editTextFile() {
}
@Override
public void editWordFile() {
}
}
以上代码定义了Sourceable的抽象实现类AbstractAdapter,该类对Sourceable进行了重写,但是不做具体实现。
(3)定义SourceSub1类按照需求实现editTextFile():
java
public class SourceSub1 extends AbstractAdapter{
private final static Log logger = LogFactory.getLog(SourceSub1.class);
@Override
public void editTextFile() {
logger.info("a text file editing");
}
}
以上代码定义了SourceSub1类并继承了AbstractAdapter,由于继承父类的子类可以按需实现自己关心的方法,因此适配起来更加灵活,这里SourceSub1类实现了editTextFile()。
(4)定义SourceSub2类按照需求实现editWordFile():
java
public class SourceSub2 extends AbstractAdapter{
private final static Log logger = LogFactory.getLog(SourceSub2.class);
@Override
public void editWordFile() {
logger.info("a word file editing");
}
}
以上代码定义了SourceSub2类,继承了AbstractAdapter并实现了editWordFile()。
(5)使用接口适配器:
java
public static void main(String[] args) {
Sourceable source1 = new SourceSub1();
Sourceable source2 = new SourceSub2();
source1.editTextFile();
source2.editWordFile();
}
使用接口适配器时按照需求实例化不同的子类并调用实现好的方法即可。以上代码的运行结果如下:
java
[INFO] SourceSub1 - a text file editing
[INFO] SourceSub2 - a word file editing
8、装饰者模式的概念及Java实现
装饰者模式(Decorator Pattern)指在无须改变原有类及类的继承关系的情况下,动态扩展一个类的功能。它通过装饰者来包裹真实的对象,并动态地向对象添加或者撤销功能。
装饰者模式包括Source和Decorator两种角色,Source是被装饰者,Decorator是装饰者。装饰者模式通过装饰者可以为被装饰者Source动态添加一些功能。具体的UML设计如图所示。

具体实现如下。
(1)定义Sourceable接口:
java
public interface Sourceable {
public void createComputer();
}
以上代码定义了一个Sourceable接口,该接口定义了一个生产电脑的方法createComputer()。
(2)定义Sourceable接口的实现类Source:
java
public class Source implements Sourceable{
private final static Log logger = LogFactory.getLog(Source.class);
@Override
public void createComputer() {
logger.info("create computer by Source");
}
}
以上代码定义了Sourceable接口的实现类Source并实现了其createComputer()。
(3)定义装饰者类Decorator:
java
public class Decorator implements Sourceable{
private Sourceable source;
private final static Log logger = LogFactory.getLog(Decorator.class);
public Decorator(Sourceable source){
super();
this.source = source;
}
@Override
public void createComputer() {
source.createComputer();
//在创建完电脑后给电脑装上系统
logger.info("make system.");
}
}
以上代码定义了装饰者类Decorator,装饰者类通过构造函数将Sourceable实例初始化到内部,并在其方法createComputer()中调用原方法后加上了装饰者逻辑,这里的装饰指在电脑创建完成后给电脑装上相应的系统。注意,之前的Sourceable没有给电脑安装系统的步骤,我们引入装饰者为Sourceable扩展了安装系统的功能。
(4)使用装饰者模式:
java
public static void main(String[] args) {
Sourceable source = new Source();
Sourceable obj = new Decorator(source);
obj.createComputer();
}
在使用装饰者模式时,需要先定义一个待装饰的Source类的source对象,然后初始化构造器Decorator并在构造函数中传入source对象,最后调用createComputer(),程序在创建完电脑后还为电脑安装了系统。运行结果如下:
java
[INFO] Source - create computer by Source
[INFO] Decorator - make system.
9、代理模式的概念及Java实现
代理模式指为对象提供一种通过代理的方式来访问并控制该对象行为的方法。在客户端不适合或者不能够直接引用一个对象时,可以通过该对象的代理对象来实现对该对象的访问,可以将该代理对象理解为客户端和目标对象之间的中介者。
在现实生活也能看到代理模式的身影,比如企业会把五险一金业务交给第三方人力资源公司去做,因为人力资源公司对这方面的业务更加熟悉,等等。
在代理模式下有两种角色,一种是被代理者,一种是代理(Proxy),在被代理者需要做一项工作时,不用自己做,而是交给代理做。比如企业在招人时,不用自己去市场上找,可以通过代理(猎头公司)去找,代理有候选人池,可根据企业的需求筛选出合适的候选人返回给企业。具体的UML设计如图所示。

具体实现如下。
(1)定义Company接口及其实现类HR:
java
public interface Company {
void findWorker(String title);
}
public class HR implements Company {
private final static Log logger = LogFactory.getLog(HR.class);
@Override
public void findWorker(String title) {
logger.info("i need find a worker, title is:"+title);
}
}
以上代码定义了一个名为Company的接口,在该接口中定义了方法findWorker(),然后定义了其实现类HR,实现findWorker()以负责公司的具体招聘工作。
(2)定义Proxy:
java
public class Proxy implements Company {
private final static Log logger = LogFactory.getLog(Proxy.class);
private HR hr;
public Proxy(){
super();
this.hr = new HR();
}
@Override
public void findWorker(String title) {//需要代理的方法
hr.findWorker(title);
//通过猎头找候选人
String worker = getWorker(title);
logger.info("find a worker by proxy, worker name is :"+worker);
}
private String getWorker(String title) {
Map<String, String> workerList = new HashMap<String, String>() {
{ put("Java", "张三"); put("Python", "李四"); put("Php", "王五"); }
};
return workerList.get(title);
}
}
以上代码定义了一个代理类Proxy,用来帮助企业寻找合适的候选人。其中Proxy继承了Company并持有HR对象,在其HR发出招人指令(findWorker)后,由代理完成具体的寻找候选人工作并将找到的候选人提供给公司。
(3)使用代理模式:
java
public static void main(String[] args) {
Company compay = new Proxy();
compay.findWorker("Java");
}
在使用代理模式时直接定义一个代理对象并调用其代理的方法即可,运行结果如下:
java
[INFO] HR - i need find a worker, title is:Java
[INFO] Proxy - find a worker by proxy, worker name is :张三
10、外观模式的概念及Java实现
外观模式(Facade Pattern)也叫作门面模式,通过一个门面(Facade)向客户端提供一个访问系统的统一接口,客户端无须关心和知晓系统内部各子模块(系统)之间的复杂关系,其主要目的是降低访问拥有多个子系统的复杂系统的难度,简化客户端与其之间的接口。外观模式将子系统中的功能抽象成一个统一的接口,客户端通过这个接口访问系统,使得系统使用起来更加容易。具体的使用场景如图所示。

简单来说,外观模式就是将多个子系统及其之间的复杂关系和调用流程封装到一个统一的接口或类中以对外提供服务。这种模式涉及3种角色。
- 子系统角色:实现了子系统的功能。
- 门面角色:外观模式的核心,熟悉各子系统的功能和调用关系并根据客户端的需求封装统一的方法来对外提供服务。
- 客户角色:通过调用Facade来完成业务功能。
以汽车的启动为例,用户只需按下启动按钮,后台就会自动完成引擎启动、仪表盘启动、车辆自检等过程。我们通过外观模式将汽车启动这一系列流程封装到启动按钮上,对于用户来说只需按下启动按钮即可,不用太关心具体的细节。具体的UML设计如图所示。

具体实现如下。
(1)定义Dashboard类:
java
public class Dashboard {
private final static Log logger = LogFactory.getLog(Dashboard.class);
public void startup(){
logger.info("dashboard startup......");
}
public void shutdown(){
logger.info("dashboard shutdown......");
}
}
以上代码定义了Dashboard类来代表仪表盘,并定义了startup()和shutdown()来控制仪表盘的启动和关闭。
(2)定义Engine类:
java
public class Engine {
private final static Log logger = LogFactory.getLog(Engine.class);
public void startup(){
logger.info("engine startup......");
}
public void shutdown(){
logger.info("engine shutdown......");
}
}
以上代码定义了Engine类来代表发动机,并定义了startup()和shutdown()来控制发动机的启动和关闭。
(3)定义SelfCheck类:
java
public class SelfCheck {
private final static Log logger = LogFactory.getLog(SelfCheck.class);
public void startupCheck(){
logger.info(" startup check finished.");
}
public void shutdowncheck(){
logger.info("shutdown check finished.");
}
}
以上代码定义了SelfCheck类来代表汽车自检器,并定义了startupCheck()和shutdowncheck()来控制汽车启动后的自检和关闭前的自检。
(4)定义门面类Starter:
java
public class Starter {
private final static Log logger = LogFactory.getLog(Starter.class);
private Dashboard dashboard;
private Engine engine;
private SelfCheck selfCheck;
public Starter(){
this.dashboard = new Dashboard();
this.engine = new Engine();
this.selfCheck = new SelfCheck();
}
public void startup(){
logger.info("car begine startup");
engine.startup();
dashboard.startup();
selfCheck.startupCheck();
logger.info("car startup finished");
}
public void shutdown(){
logger.info("car begine shutdown");
selfCheck.shutdowncheck();
engine.shutdown();
dashboard.shutdown();
logger.info("car shutdown finished");
}
}
以上代码定义了门面类Starter,在Starter中定义了startup方法,该方法先调用engine的启动方法启动引擎,再调用dashboard的启动方法启动仪表盘,最后调用selfCheck的启动自检方法完成启动自检。
(5)使用外观模式:
java
public static void main(String[] args) {
Starter starter = new Starter();
starter.startup();
System.out.println("*******************");
starter.shutdown();
}
在使用外观模式时,用户只需定义门面类的实例并调用封装好的方法或接口即可。这里调用starter的startup()完成启动,运行结果如下:
java
[INFO] Starter - car begine startup
[INFO] Engine - engine startup......
[INFO] Dashboard - dashboard startup......
[INFO] SelfCheck - startup check finished.
[INFO] Starter - car startup finished
*******************
[INFO] Starter - car begine shutdown
[INFO] SelfCheck - shutdown check finished.
[INFO] Engine - engine shutdown......
[INFO] Dashboard - dashboard shutdown......
[INFO] Starter - car shutdown finished
11、桥接模式的概念及Java实现
桥接模式(Bridge Pattern)通过将抽象及其实现解耦,使二者可以根据需求独立变化。这种类型的设计模式属于结构型模式,通过定义一个抽象和实现之间的桥接者来达到解耦的目的。
桥接模型主要用于解决在需求多变的情况下使用继承造成类爆炸的问题,扩展起来不够灵活。可以通过桥接模式将抽象部分与实现部分分离,使其能够独立变化而相互之间的功能不受影响。具体做法是通过定义一个桥接接口,使得实体类的功能独立于接口实现类,降低它们之间的耦合度。
我们常用的JDBC和DriverManager就使用了桥接模式,JDBC在连接数据库时,在各个数据库之间进行切换而不需要修改代码,因为JDBC提供了统一的接口,每个数据库都提供了各自的实现,通过一个叫作数据库驱动的程序来桥接即可。下面以数据库连接为例介绍桥接模式,具体的UML设计如图所示。

具体实现如下。
(1)定义Driver接口:
java
public interface Driver {
void executeSQL();
}
以上代码定义了Driver接口,在该接口中定义了一个执行SQL语句的方法,用于处理不同数据库的SQL语句。
(2)定义Driver接口的MySQL实现类MysqlDriver:
java
public class MysqlDriver implements Driver{
private final static Log logger = LogFactory.getLog(MysqlDriver.class);
@Override
public void executeSQL() {
logger.info( "execute sql by mysql driver");
}
}
以上代码定义了Driver的实现类MysqlDriver,并基于MySQL实现了其执行SQL语句的方法。
(3)定义Driver接口的Oracle实现类OracleDriver:
java
public class OracleDriver implements Driver{
private final static Log logger = LogFactory.getLog(OracleDriver.class);
@Override
public void executeSQL() {
logger.info( "execute sql by oracle driver");
}
}
以上代码定义了Driver的实现类OracleDriver,并基于Oracle实现了其执行SQL语句的方法。
(4)定义DriverManagerBridge:
java
public abstract class DriverManagerBridge {
private Driver driver;
public void execute(){
this.driver.executeSQL();
}
public Driver getDriver() {
return driver;
}
public void setDriver(Driver driver) {
this.driver = driver;
}
}
以上代码定义了抽象类DriverManagerBridge,用于实现桥接模式,该类定义了Driver的注入,用户注入不同的驱动器便能实现不同类型的数据库的切换。
(5)定义MyDriverBridge:
java
public class MyDriverBridge extends DriverManagerBridge {
public void execute() {
getDriver().executeSQL();
}
}
在以上代码中,MyDriverBridge用于实现用户自定义的功能,也可以直接使用DriverManagerBridge提供的功能。
(6)使用桥接模式:
java
public static void main(String[] args) {
DriverManagerBridge driverManagerBridge = new MyDriverBridge() ;
//设置MySQL驱动
driverManagerBridge.setDriver(new MysqlDriver());
driverManagerBridge.execute();
//切换到Oracle驱动
driverManagerBridge.setDriver(new OracleDriver());
driverManagerBridge.execute();
}
在以上代码中使用了桥接模式,定义了一个DriverManagerBridge,然后注入不同的驱动器,以实现在不同类型的数据库中实现驱动的切换和数据库SQL语句的执行。具体的执行代码如下:
java
[INFO] MysqlDriver - execute sql by mysql driver
[INFO] OracleDriver - execute sql by oracle driver
12、组合模式的概念及Java实现
组合模式(Composite Pattern)又叫作部分整体模式,主要用于实现部分和整体操作的一致性。组合模式常根据树形结构来表示部分及整体之间的关系,使得用户对单个对象和组合对象的操作具有一致性。
组合模式通过特定的数据结构简化了部分和整体之间的关系,使得客户端可以像处理单个元素一样来处理整体的数据集,而无须关心单个元素和整体数据集之间的内部复杂结构。
组合模式以类似树形结构的方式实现整体和部分之间关系的组合。下面以实现一个简单的树为例介绍组合模式。具体的UML设计如图所示。

具体实现如下。
(1)定义TreeNode:
java
public class TreeNode {
private String name;
private TreeNode parent;
private Vector<TreeNode> children = new Vector<TreeNode>();
public TreeNode(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public TreeNode getParent() {
return parent;
}
public void setParent(TreeNode parent) {
this.parent = parent;
}
//添加子节点
public void add(TreeNode node){
children.add(node);
}
//删除子节点
public void remove(TreeNode node){
children.remove(node);
}
//获取子节点
public Enumeration<TreeNode> getChildren(){
return children.elements();
}
}
以上代码定义了TreeNode类来表示一个树形结构,并定义了children来存储子类,定义了方法add()和remove()来向树中添加数据和从树中删除数据。
(2)使用TreeNode:
java
public static void main(String[] args) {
TreeNode nodeA = new TreeNode("A");
TreeNode nodeB = new TreeNode("B");
nodeA.add(nodeB);
logger.info(JSON.toJSONString(nodeA));
}
以上代码演示了TreeNode的使用过程,定义了nodeA和nodeB,并将nodeB作为nodeA的子类,具体运行结果如下:
java
[INFO] CompositeDemo - {"children":[{"children":[], "name":"B"}], "name":"A"}
从以上代码中可以看到一棵包含了nodeA和nodeB的树,其中nodeB为nodeA的子节点。