设计模式(二)

结构型模式:代理/适配器/装饰/享元/桥接/组合/门面

代理模式

为其他对象提供一种代理以控制对这个对象的访问

使用场景

当不想直接访问某个对象或访问存在困难时,可以通过一个代理对象来间接访问,
为了保证客户端使用的透明性,委托对向与代理对象需要实现相同的接口

1
2
3
4
5
6
7
8
9
10
11
//公共接口
interface ProxyInterface {
void test();
}
//被代理类
class A implements ProxyInterface {
@Override
public void test() {
System.out.println("被代理类");
}
}

静态代理

代理者的代码由程序员自己或通过一些自动化工具生成的固定代码,再对其进行编译,
即我们的代码在运行前代理类的class编译文件就已经存在

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//静态代理类
class MyProxy implements ProxyInterface {
private ProxyInterface m;
//需实现相同的接口,注入被代理类
public MyProxy(ProxyInterface m) {
this.m = m;
}
@Override
public void test() {
System.out.println("静态代理=>前执行");
m.test(); //代用被代理类方法
System.out.println("静态代理=>后执行");
}
}
//main方法执行
A a = new A();
new MyProxy(a).test(); //静态代理

动态代理

通过反射机制动态的生成代理者的对象,常用的有jdk动态代理、cglib动态代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//jdk动态代理类
class DynamicProxy implements InvocationHandler {
private Object obj;
public DynamicProxy(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("动态代理=>前执行");
//调用被代理类对象的方法
Object result = method.invoke(obj, args);
System.out.println("动态代理=>后执行");
return result;
}
}

main方法调用

1
2
3
4
5
6
7
8
A a = new A();
//动态代理
InvocationHandler invocationHandler = new DynamicProxy(a);
ProxyInterface mProxy = (ProxyInterface) Proxy.newProxyInstance(
a.getClass().getClassLoader(),
a.getClass().getInterfaces(),
invocationHandler);
mProxy.test();

适配器模式

把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的二个类能够在一起工作

使用场景

接口不兼容、建立一个可重复使用的类用于类与类之间的合作、需要一个统一的输出接口但输入类型不可预知

1
2
3
4
5
6
7
8
9
10
//目标接口 - 200V适配为5V
interface FiveVolt {
int getVolt5(); //5V
}
//Adaptee,需要被适配的对象
class Volt200 {
public int getVolt200(){
return 200;
}
}

类适配器模式

1
2
3
4
5
6
7
//Adapter角色
class VoltAdapter extends Volt200 implements FiveVolt {
@Override
public int getVolt5() {
return 5;
}
}

对象适配器模式

Adapter不是使用继承关系连接到Adaptee,而是使用组合关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class VoltAdapter2 implements FiveVolt {
private Volt200 mVolt200;
public VoltAdapter2(Volt200 mVolt200) {
this.mVolt200 = mVolt200;
}
public int getVolt200() {
return mVolt200.getVolt200();
}
@Override
public int getVolt5() {
return 5;
}
}

装饰模式

动态的给一个对象添加一些额外的职责,是继承关系的替代方式之一

使用场景

需要动态且透明的扩展类的功能时

抽象组件类

1
2
3
abstract class Component {
public abstract void operate();
}

具体实现类

1
2
3
4
5
6
class ConcreteComponent extends Component {
@Override
public void operate() {
System.out.println("被装饰的方法");
}
}

抽象装饰类

1
2
3
4
5
6
7
8
9
10
11
12
abstract class Decorator extends Component {
private Component component; //必须
public Decorator(Component component) {
this.component = component;
}
@Override
public void operate() {
component.operate();
}
}

装饰者实现类

1
2
3
4
5
6
7
8
9
10
11
12
class ConcreteDecorator extends Decorator {
public ConcreteDecorator(Component component) {
super(component);
}
@Override
public void operate() {
System.out.println("装饰方法=>前。。。");
super.operate();
System.out.println("装饰方法=>后。。。");
}
}

具体main方法实现

1
2
3
4
5
//构造被装饰的组件对象
Component component = new ConcreteComponent();
//装饰模式
Decorator decorator = new ConcreteDecorator(component);
decorator.operate();

享元模式

用以尽可能减少内存使用量,使用共享对象可有效支持大量的细粒度对象

享元对象中的部分状态是可以共享,可共享的状态成为内部状态,不会随着环境的改变而改变,
不可共享状态称之为外部状态,会随环境的改变而改变

经典享元模式中会建立一个对象容器Map,键为享元对象的内部状态,值为享元对象本身,
客户端程序通过这个内部状态从享元工厂中获取享元对象,若有缓存则使用缓存对象,
否则创建一个享元对象并且放进容器中,这样就避免了创建过多对象的问题

使用场景

  1. 系统中存在大量的相似对象
  2. 细粒度的对象都具备较接近的外部状态,而且内部状态与环境无关
  3. 需要缓冲池的场景
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//享元模式举例
class TicketFactroy {
//map作为缓存容器
static Map<String,Ticket> map = new ConcurrentHashMap<>();
public static Ticket getTicket(String from ,String to) {
String key = from + "-" + to;
if(map.containsKey(key)) {
//使用缓存
return map.get(key);
}else{
//若无缓存则重新创建一个对象
TrainTicket ticket = new TrainTicket(from, to);
//加入缓存
map.put(key, ticket);
return ticket;
}
}
}

桥接模式

将抽象部分与实现部分分离,使他们都可以进行独立地变化

使用场景

一个类存在二个独立变化的维度,且这二个维度都需要进行扩展
对于那些不希望使用继承或者因为多层次继承导致系统类的个数急剧增加的系统,也可以使用

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
//实现部分的抽象接口
interface Implementor {
void operate();
}
//具体实现类
class ConcreteImplementor implements Implementor {
@Override
public void operate() {
//...
}
}
//抽象部分实现
abstract class Abstraction {
//用于引用实现部分的对象
private Implementor implementor;
public Abstraction(Implementor implementor) {
this.implementor = implementor;
}
// 通过调用实现部分的具体方法实现具体的功能
public void operation() {
implementor.operate();
}
}
//抽象部分子类
class RefinedAbstraction extends Abstraction {
public RefinedAbstraction(Implementor implementor) {
super(implementor);
}
//对父类抽象部分的方法进行扩展
public void refinedOperation() {
//...
}
}

组合模式

将对象组合成树形结构以表示”整体-部分”的层次结构,使得对单个对象和组合对象的使用具有一致性

使用场景

表示对象的部分-整体层次结构时或从一个整体中能够独立出部分模块或功能的场景

抽象根节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
abstract class Component {
protected String name;
public Component(String name) {
this.name = name;
}
public abstract void doSomething(); //具体逻辑方法由子类实现
/**
* 添加子节点
* @param child
*/
public abstract void addChild(Component child);
/**
* 移除子节点
* @param child
*/
public abstract void removeChild(Component child);
/**
* 获取子节点
* @param child
*/
public abstract Component getChild(int index);
}

具体枝干结点

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
class Composite extends Component {
/**
* 存储结点的容器
*/
private List<Component> list = new ArrayList<>();
public Composite(String name) {
super(name);
}
@Override
public void doSomething() {
System.out.println(name);
if(list != null) {
for (Component c : list) {
c.doSomething();
}
}
}
@Override
public void addChild(Component child) {
list.add(child);
}
@Override
public void removeChild(Component child) {
list.remove(child);
}
@Override
public Component getChild(int index) {
return list.get(index);
}
}

叶子结点

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
class Leaf extends Component {
public Leaf(String name) {
super(name);
}
@Override
public void doSomething() {
System.out.println(name);
}
@Override
public void addChild(Component child) {
throw new UnsupportedOperationException("叶子结点没有子节点");
}
@Override
public void removeChild(Component child) {
throw new UnsupportedOperationException("叶子结点没有子节点");
}
@Override
public Component getChild(int index) {
throw new UnsupportedOperationException("叶子结点没有子节点");
}
}

具体main方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static void main(String[] args) {
//构造一个根结点
Component root = new Composite("Root");
//构造枝干结点
Component branch1 = new Composite("Branch1");
Component branch2 = new Composite("Branch2");
//构造叶子结点
Component leaf1 = new Leaf("Leaf1");
Component leaf2 = new Leaf("Leaf2");
//添加
branch1.addChild(leaf1);
branch2.addChild(leaf2);
root.addChild(branch1);
root.addChild(branch2);
//执行方法
root.doSomething();
}

门面模式/外观模式

要求一个子系统外部与其内部的通信必须通过一个统一的对象进行,门面模式提供一个高层次的接口,使子系统更加易用

使用场景

为一个复杂子系统提供一个简单接口或当需构建一个层次结构的子系统时

具体代码参看:https://github.com/Lutils/DesignPattern