23种设计模式之结构型设计模式


前言

本篇文章主要讲23种设计模式中的7种结构型设计模式,包括适配器模式,装饰者模式,代理模式,外观模式,桥接模式,组合模式,享元模式。

适配器模式

适配器模式是将一个类的方法接口转换成客户端期望的接口表示。我们可以约定,把客户端期望的接口叫做目标Targetable,被转换的类叫source。适配器模式可以分为:类的适配器模式,对象的适配器,接口的适配器。

类的适配器模式

已有的被转换的类:

1
2
3
4
5
6
7
public class SourceClass {

public void method1() {
System.out.print("Hi, I am a method in sourceClass");
}

}

期望的目标:

1
2
3
4
public interface Targetable {
void method1();
void method2();
}

实现目标,进行适配

1
2
3
4
5
6
7
public class AdapterClass extends SourceClass implements Targetable {

@Override
public void method2() {
System.out.print("Hi All, I am a method in adapterClass");
}
}

这样子就将SourceClass按照意愿Targetable适配转换成了AdapterClass,AdapterClass具有了SourceClass的所有的功能,同时也达到了扩展SourceClass。由于类的适配器模式是通过继承实现的,它具有了继承的优缺点。关于缺点,比如通过AdapterClass对象可以调用属于SourceClass而在Targetable接口中没有的方法。

对象的适配器模式

对象的适配器模式,就是将原来类的对象转换为目标接口的对象。对象适配器模式没有继承被转换类,而是持有被转换类的对象。这可以避免继承被带来的副作用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class AdapterObjectClass implements Targetable{

private SourceClass mSourceClass;

public AdapterObjectClass(SourceClass mSourceClass) {
this.mSourceClass = mSourceClass;
}

@Override
public void method2() {
System.out.print("hi all, i am a method in AdapterObjectClass");
}

@Override
public void method1() {
mSourceClass.method1();
}
}

接口的适配器模式

当一个接口有很多的抽象方法时,当我们写这个接口的实现类,必须实现该接口的全部方法。而有时候接口中并不是所有的抽象方法都是我们必须的,而我们只需要实现其中的某一些方法。为了解决这个问题,我们可以使用接口的适配器模式,引入一个抽象类,这个抽象类提供了接口所有抽象方法的空实现。我们可以继承这个抽象类,并只重写我们需要的方法即可。

比如,在上面我们只要Targetable的method2方法。

1
2
3
4
5
6
7
8
9
10
11
12
public abstract class AdapterInterfaceClass implements Targetable{

@Override
public void method1() {

}

@Override
public void method2() {

}
}
1
2
3
4
5
6
7
public class AdapterWraper extends AdapterInterfaceClass {

@Override
public void method1() {
System.out.print("hi all, I am a method in AdapterWraper class");
}
}

装饰者模式

装饰者模式的核心思想是,装饰者和被装饰者实现同一个接口,将被装饰者注入装饰者中,可以在装饰者中扩展被装饰者。

1
2
3
public interface Person {
void eat();
}

被装饰者:

1
2
3
4
5
6
7
public class Man implements Person {

@Override
public void eat() {
System.out.print("There is a man who is eating");
}
}

装饰者:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ManDecorator implements Person {

private Person mPerson;

public ManDecorator(Person person) {
mPerson = person;
}

@Override
public void eat() {
mPerson.eat();
drinkWater();
System.out.print("I finish my lunch");
}

private void drinkWater() {
System.out.print("Man is drinking water");
}
}

使用:

1
2
3
Man man = new Man();
ManDecorator manDecorator = new ManDecorator(man);
manDecorator.eat();

输出的结果:

1
2
3
There is a man who is eating
Man is drinking water
I finish my lunch

代理模式

注意区别代理模式和动态代理。

生活中代理的例子。比如如果你要租房子,你可能不知道该地区的房子信息,这时你可以找一个熟悉的人来帮忙,这个帮你的人就是代理;又比如,打官司时,我们可能并不精通法律知识,这时我们可以找一个代理律师来帮我们。等等。。对于,代理的工作可以抽象为一个接口。

1
2
3
public interface WorkInterface {
void rentHouse();
}

一个房东:

1
2
3
4
5
6
7
public class LandLady implements WorkInterface {

@Override
public void rentHouse() {
System.out.print("您好!我是房东。我这里有房子出租!");
}
}

代理房东的代理类:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Proxy implements WorkInterface {

private LandLady mLandLady;

public Proxy() {
mLandLady = new LandLady();
}

@Override
public void rentHouse() {
mLandLady.rentHouse();
}
}

租客去找代理租房子:

1
2
WorkInterface proxy = new Proxy();
proxy.rentHouse();

外观模式

在医院里的前台接待员就是一个外观模式的体现。由于病人来到医院可能对医院内部和流程并不熟悉,那么可以由熟悉这些的接待员来帮病人来完成这些事情。

部门1

1
2
3
4
5
6
7
8
9
public class ModuleA {

//提供给外部调用的方法
public void a1() {}

//内部完成工作的实现
private void a2() {}
private void a3() {}
}

部门2

1
2
3
4
5
6
7
8
9
public class ModuleB {

//提供给外部调用的方法
public void b1() {}

//内部完成工作的实现
private void b2() {}
private void b3() {}
}

部门3

1
2
3
4
5
6
7
8
9
public class ModuleC {

//提供给外部调用的方法
public void c1() {}

//内部完成工作的实现
private void c2() {}
private void c3() {}
}

外观类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ModuleFacade {

private ModuleA mModuleA = new ModuleA();
private ModuleB mMBModuleB = new ModuleB();
private ModuleC mMCModuleC = new ModuleC();

public void a1() {
mModuleA.a1();
}

public void b1() {
mMBModuleB.b1();
}

public void c1() {
mMCModuleC.c1();
}

}

当我们需要ModuleA,ModuleB, ModuleC的功能时,我们并不直接和他们打交道,也不需要了解部门的功能是如何实现的,而我们只需要去找外观类沟通即可。

外观模式的关键点是整合。

桥接模式

桥接模式,提供一个解耦或者连接抽象化和实现化的一个桥梁,使得二者可以独立变化。

一个接口作为桥,一个抽象类持有桥。桥和抽象类两者可以独立变化。

桥:

1
2
3
public interface Qiao {
void toArea();
}

抽象类:

1
2
3
4
public abstract class FromArea {
public Qiao qiao;
abstract public void fromArea();
}

QiaoC.java

1
2
3
4
5
6
7
public class QiaoC implements Qiao {

@Override
public void toArea() {
System.out.print("I want to go Area C");
}
}

QiaoD.java

1
2
3
4
5
6
7
public class QiaoD implements Qiao {

@Override
public void toArea() {
System.out.print("I want to go Area D");
}
}

FromAreaA.java

1
2
3
4
5
6
7
public class FromAreaA extends FromArea {

@Override
public void fromArea() {
System.out.print("I come from area A");
}
}

FromAreaB.java

1
2
3
4
5
6
7
public class FromAreaB extends FromArea {

@Override
public void fromArea() {
System.out.print("I come from area B");
}
}

使用:

1
2
3
4
5
6
7
8
9
10
11
12
FromAreaA fromAreaA = new FromAreaA();
QiaoC qiaoC = new QiaoC();
fromAreaA.qiao = qiaoC;

fromAreaA.fromArea();
fromAreaA.qiao.toArea();

QiaoD qiaoD = new QiaoD();
fromAreaA.qiao = qiaoD;

fromAreaA.fromArea();
fromAreaA.qiao.toArea();

从上面可以看出,Qiao和FromArea两者是独立变化的,它们的抽象和实现是分离的。

如果有更多的Qiao和FromArea的实现,只要扩展它们即可。

组合模式

组合模式,又叫“整体-部分设计模式”。它一般用于实现树形结构。

节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class TreeNode {

private String name;
private TreeNode parent;
private Vector<TreeNode> children = new Vector<>();

public TreeNode(String name) {
this.name = name;
}

public void setName(String name) {
this.name = name;
}

public String getName() {
return name;
}

public void setParent(TreeNode parent) {
this.parent = parent;
}

public TreeNode getParent() {
return parent;
}

public void addChild(TreeNode child) {
children.add(child);
}

public boolean removeChild(TreeNode child) {
return children.remove(child);
}

public Enumeration<TreeNode> getChildren() {
return children.elements();
}

}

整体,建立一棵树:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Tree {
TreeNode root = null;

public Tree(String name) {
root = new TreeNode(name);
}

public static void main(String[] args) {
Tree tree = new Tree("A");
TreeNode nodeB = new TreeNode("B");
TreeNode nodeC = new TreeNode("C");

nodeB.addChild(nodeC);
tree.root.addChild(nodeB);
System.out.println("build the tree finished!");
}
}

享元模式

享元模式主要是实现对象的共享。联想数据库的连接池。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class ConnectionPool {  

private Vector<Connection> pool;

/*公有属性*/
private String url = "jdbc:mysql://localhost:3306/test";
private String username = "root";
private String password = "root";
private String driverClassName = "com.mysql.jdbc.Driver";

private int poolSize = 100;
private static ConnectionPool instance = null;
Connection conn = null;

/*构造方法,做一些初始化工作*/
private ConnectionPool() {
pool = new Vector<Connection>(poolSize);

for (int i = 0; i < poolSize; i++) {
try {
Class.forName(driverClassName);
conn = DriverManager.getConnection(url, username, password);
pool.add(conn);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
}

/* 返回连接到连接池 */
public synchronized void release() {
pool.add(conn);
}

/* 返回连接池中的一个数据库连接 */
public synchronized Connection getConnection() {
if (pool.size() > 0) {
Connection conn = pool.get(0);
pool.remove(conn);
return conn;
} else {
return null;
}
}
}