命令模式

命令模式的封装性非常好,把请求方(Invoker)和执行方(Receiver)分开,扩展性也非常好。

定义

将一个请求封装成一个对象,从而使得不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。

命令模式通用类图

实现

Receiver

抽象接收者,定义每个接收者都必须完成的业务。

1
2
3
public abstract class Receiver{
public abstract void doSomething();
}

具体的Receiver

1
2
3
4
5
6
7
public class Concrete1Reveiver extends Receiver{
public void doSomething(){}
}

public class Concrete2Reveiver extends Receiver{
public void doSomething(){}
}

Command

不同的命令实现都需要继承该类,实现自己的execute逻辑。

1
2
3
public abstract class Command{
public abstract void execute();
}

具体的命令实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ConcreteCommand1 extends Command{
private Receiver receiver;
public ConcreteCommand1(Receiver receiver){
this.receiver = receiver;
}
public void execute(){
this.receiver.doSomething();
}
}

public class ConcreteCommand2 extends Command{
private Receiver receiver;
public ConcreteCommand2(Receiver receiver){
this.receiver = receiver;
}
public void execute(){
this.receiver.doSomething();
}
}

Invoker

1
2
3
4
5
6
7
8
9
public class Invoker{
private Command command;
public void setCommand(Command command){
this.command = command;
}
public void action(){
this.command.execute();
}
}

Use

1
2
3
Invoker invoker = new Invoker();
invoker.setCommand(new ConcreteCommand1(new Concrete1Reveiver()));
invoker.action();

模式应用

优点

  • 解耦
    调用者角色与接收者角色之间没有任何依赖关系,调用者实现功能时只需要调用Command抽象类的execute方法即可,不需要了解是哪个接收者执行。
  • 可扩展性好
    Command子类可以非常容易的扩展,调用者Invoker和高层的模块Client不产生严重的代码耦合。
  • 与其他模式混合使用
    与责任链模式结合,实现命令族解析任务
    与模版方法结合,减少Command子类膨胀的问题。

缺点

Command的子类会随着命令的增多逐步膨胀。

命令的撤销

反命令

通过相反的命令来实现命令的撤销。

结合备忘录模式

结合备忘录模式还原最后状态,折中发昂发适合接收者为状态的变更情况,而不适合事件处理。

代码实例

假设有个录音机,录音机面板有三个按钮, play, rewind, stop。

需要根据三个按钮作出相应的反馈。

Receiver

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class AudioPlayer {

public void paly(){
System.out.println("Play");
}

public void rewind(){
System.out.println("Rewind");
}

public void stop(){
System.out.println("Stop");
}
}

Command

不同的Command调用不同的Receiver命令。

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
public abstract class Command {

protected static AudioPlayer audioPlayer = new AudioPlayer();

public abstract void execute();

}

public class PlayCommand extends Command {
@Override
public void execute() {
super.audioPlayer.paly();
}
}

public class RewindCommand extends Command {
@Override
public void execute() {
audioPlayer.rewind();
}
}

public class StopCommand extends Command {
@Override
public void execute() {
audioPlayer.stop();
}
}

Invoker

用于转发Command

1
2
3
4
5
6
7
8
9
10
11
public class KeyPad {
private Command command;

public void setCommand(Command command) {
this.command = command;
}

public void action() {
this.command.execute();
}
}

Use

1
2
3
4
5
6
7
KeyPad keyPad = new KeyPad();

keyPad.setCommand(new RewindCommand());
keyPad.action();

keyPad.setCommand(new StopCommand());
keyPad.action();

如果后期需要增加一个按钮,stopAndRewind(),直接增加一个Command子类即可,其他地方不需要动。

1
2
3
4
5
6
7
8
public class StopAndRewindCommand extends  Command {

@Override
public void execute() {
audioPlayer.stop();
audioPlayer.rewind();
}
}

模版方法模式

抽象类定义一组方法的执行顺序,具体的功能由子类来实现。
它仅仅使用了Java的继承机制,但使用非常广泛。
一般多个子类有公有的方法,并且逻辑基本相同的时候使用。

定义

定义一个操作中的算法框架,再将一些步骤延迟到子类中。使得子类可以不改变算法的结构就可以定义该算法的某些特定步骤。

模版方法模式通用类图

实现

基本方法

一些基本的操作,由子类实现具体的逻辑,在模版方法中被调用。

模版方法

模版方法可以有一个或者多个,一般是一个具体的方法,包含了基本方法的调用逻辑。

抽象模版类中包含了一个模版方法goToSchool和三个基本方法toSchool, study, goHome.
这样,每个学生继承该抽象类实现自己逻辑即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public abstract class StudentBehavor{
protected abstract void toSchool();
protected abstract void study();
protected abstract void goHome();
public void goToSchool(){
//do something.
toSchool();
// do something.
study();
// do something.
goHome();
// do something.
}

}

应用

优点

  • 封装不变部分,扩展可变部分
    把认为不变的部分算法封装到父类实现,可变部分通过继承来扩展。

  • 提取公共部分代码,便于维护

  • 行为由父类控制,子类实现
    基本方法由子类实现,子类可以扩展增加相应的功能,符合开闭原则。

缺点

在复杂的项目中,增加代码的阅读难度。

责任链模式

责任链模式的核心在链上, 链是有多个处理者组成的。就像流水线上的质检员。

定义

使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的关系。
将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。

责任链模式通用类图

实现

抽象事件

抽象事件类型表明这类型的事件是需要被处理的。

1
2
3
public interface IEvent {
int getType();
}

具体的事件

不同类型的子类事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class EventClick implements IEvent {
public int getType() {
return 1;
}
}
public class EventEnter implements IEvent {
public int getType() {
return 2;
}
}
public class EventBlank implements IEvent {
public int getType() {
return 3;
}
}

抽象的事件处理器

定义了只有Ievent类型的事件会被处理。

1
2
3
public interface IEventHandler {
boolean check(IEvent event);
}

具体的事件处理器

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
public class ClickEventHandler implements IEventHandler {

public boolean check(IEvent event) {
if (event.getType() != 1){
return false;
}
System.out.println("Father check Ok. ");
return true;
}
}

public class EnterEventHandler implements IEventHandler {

public boolean check(IEvent event) {
if (event.getType() == 2) {
System.out.println("Husband Ok.");
return true;
}
return false;
}
}

public class BlankEventHandler implements IEventHandler {

public boolean check(IEvent event) {
if (event.getType() != 3) {
return false;
}

System.out.println("Son Ok");
return true;

}
}

事件处理链

用于组织事件处理器,符合条件的处理器处理完成后或者没有处理器处理都会停止。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class EventHandlerChain {
private List<IEventHandler> eventChains = new ArrayList<IEventHandler>();
public void add(IEventHandler eventHandler) {
eventChains.add(eventHandler);
}
public boolean check(IEvent event) {
for (IEventHandler eventChain : this.eventChains) {
if (eventChain.check(event)) {
return true;
}
}
return false;
}

}

Use

可以随意组织责任链的顺序,然后检查需要检查的类型。

1
2
3
4
5
6
7
8
EventHandlerChain chain = new EventHandlerChain();
chain.add(new ClickEventHandler());
chain.add(new EnterEventHandler());
chain.add(new BlankEventHandler());

chain.check(new EventClick());
chain.check(new EventEnter());
chain.check(new EventBlank());

应用

优点

将请求和处理分开。请求着可以不用知道是谁处理的,处理者可以不用知道请求着的全貌。
两者解耦,提高了系统的灵活性。

缺点

性能问题, 最差的情况下,请求从头遍历到尾,链条特别长的时候,性能损耗是个大的问题。
因此在使用的过程中要注意责任链的长短。
调试不方便。

在广告系统中,由不同的事件来触发不同类型的广告时根据责任链模式来处理。
每个Handler可以专注自己的业务处理逻辑。具体Handler还可以抽象一个抽象类,把公用的逻辑写在抽象类里面,每个具体的实现类只负责业务的处理即可(优化为模版方法模式)。

迭代器模式

迭代器模式提供了遍历容器的方便性,容器只要管理增减元素即可,需要遍历的时候交由迭代器进行。

比如Set、Map、List等容器的iterator。

定义

迭代器是为容器服务的,它提供一种方法访问一个容器对象中的各个元素,而又不需要暴露该对象的内部细节。

迭代器模式通用类图

实现

Iterator

抽象迭代器负责定义访问和遍历元素的接口。
first() 获取第一个元素;
next() 访问下一个元素;
hasNext() 是否还有下一个;

1
2
3
4
5
public interface Iterator<E>{
E next();
boolean hasNext();
boolean remove();
}

ConcreteIterator

具体迭代器要实现迭代器接口,完成容器元素的遍历。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ConcreteIterator implements Iterator<E>{
private Vector<E> v = new Vector<>();
public int cursor = 0;
public ConcreteIterator(Vector _vector){
this.v = _vector;
}
public boolean hasNext(){
return this.cursor != this.v.size();
}

public E next(){
return this.hasNext() ? this.v.get(this.cursor++) : null;
}

public boolean remove(){
this.v.remove(this.cursor);
return true;
}
}

Aggregate

抽象容器角色负责提供创建具体迭代器角色的接口, 必然提供一个类似于createIterator()的方法。

1
2
3
4
5
public interface Aggregate<E>{
void add(E e);
void remove(E e);
Iterator iterator();
}

Concrete Aggregate

具体容器实现容器接口定义的方法,创建出容纳迭代器的对象。

1
2
3
4
5
6
7
8
9
10
11
12
public class ConcreteAggrate implements Aggregate<E>{
Vector v = new Vector();
public void add(E e){
this.add(e);
}
public Iterator<E> iterator(){
return new ConcreteIterator<>(this.v);
}
public void remove(E e){
this.remove(e);
}
}

组合模式

也叫合成模式或者部分整体模式,主要用来描述部分与整体的关系。

定义

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

组合模式的通用类图

Component

定义参加组合对象的共有方法和属性,可以定义一些默认的行为或者属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public abstract class Component{
private Component parent = null;
private ArrayList<Component> list = new ArrayList<>();
public void add(Component c){
c.parent = this;
this.list.add(c);
}

public void remove(Component c){
this.list.remove(c);
}

public List<Component> getChildren(){
return this.list;
}

public Component getParent(){
return this.parent;
}
}

Composite

树枝对象,作用是组合树枝节点和叶子结点形成一个树形结构。

1
2
3
public class Composite extends Component{

}

Leaf

叶子对象,下面没有分支,最小的遍历单位。

1
2
3
public class Leaf extends Component{

}

应用

优点

  • 高层模块调用简单
    一颗属性结构中的所有节点都是Component,局部和整体对调用者来说没有任何区别,也就是说高层模块不必关心自己处理的是单个对象还是整个组合结构,简化了高层模块的代码。
  • 节点自由度增加
    使用组合模式后,如果想增加一个树枝节点、树叶节点都非常容易。
    如何开闭原则,利于维护。

缺点

(安全模式)直接使用了实现类(可以扩展)。与依赖倒置原则冲突,限制了接口的影响范围。

扩展

透明的组合模式

以上的例子就是透明的组合模式,吧用来组合使用的方法放到抽象类中,无论叶子对象还是树枝对象都有相同的结构,通过getChildren();判断当前节点是否为叶子节点还是根节点。

透明模式的好处是遵循了依赖倒置原则

安全的组合模式

安全模式是将叶子节点和树枝节点彻底分开,树枝节点单独拥有用来组合的方法。

Component

定义参加组合对象的共有方法和属性,可以定义一些默认的行为或者属性。

1
2
3
4
5
6
public abstract class Component{
private Component parent = null;
public Component getParent(){
return this.parent;
}
}
Composite

树枝对象,作用是组合树枝节点和叶子结点形成一个树形结构。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Composite extends Component{
private ArrayList<Component> list = new ArrayList<>();
public void add(Component c){
c.parent = this;
this.list.add(c);
}

public void remove(Component c){
this.list.remove(c);
}

public List<Component> getChildren(){
return this.list;
}
}
Leaf

叶子对象,下面没有分支,最小的遍历单位。

1
2
3
public class Leaf extends Component{

}

访问者模式

访问者模式是一种集中规整模式,适合用于重构项目时(重构时需求已经清晰,原有的功能点也明确)
通过访问者模式可以很容易达到功能集中化。
还可以与其他模式混编建立一套自己的过滤器和拦截器。

定义

封装一些用于某种数据结构中的各个元素的操作,可以在不改变数据结构的前提下定义作用于这些元素的新操作。

访问者模式通用类图

角色

Visitor 抽象访问者

抽象类或者接口,声明访问者可以访问哪些元素,具体到程序中就是visit方法的参数定义哪些对象是可以被访问的。

1
2
3
4
public interface IVisitor{
public void visit(ConcreteElement1 el1);
public void visit(ConcreteElement2 el2);
}

ConcreteVisitor 具体访问者

影响访问者访问到一个类后该做什么事情。

1
2
3
4
5
6
7
8
public class Visitor implements IVisitor{
public void visit(ConcreteElement1 el1){
el1.doSomething();
}
public void visit(ConcreteElement2 el2){
el2.doSomething();
}
}

Element 抽象元素

接口或者抽象类,声明接受哪一类访问者访问。
抽象元素有两类方法, 一类是本身的业务逻辑。另一类是允许访问者来访问的方法。

1
2
3
4
5
6
public abstract class Element{
//业务逻辑
public abstract void doSomething();
//接受访问者
public abstract void accept(IVisitor visitor);
}

ConcreteElement 具体元素

实现accept方法, 通常是 visitor.visit(this)模式。

1
2
3
4
5
6
7
8
9
10
11
12
public class ConcreteElement1 extends Element{
public void doSomething(){}
public void accept(IVisitor visitor){
visitor.visit(this);
}
}
public class ConcreteElement2 extends Element{
public void doSomething(){}
public void accept(IVisitor visitor){
visitor.visit(this);
}
}

ObjectStructure结构对象

元素生产者,一般容纳在多个不同类、不同接口的容器,如List/Set/Map。

1
2
3
4
5
public class ObjectStructure{
public static Element createElement(){
return new Random().nextInt(10) > 5 ? new ConcreteElement1() : new ConcreteElement2();
}
}

使用

1
2
3
for(int i = 0; i < 10 ; i++){
ObjectStructure.createElement().accept(new Visitor());
}

优点

  • 符合单一职责原则
  • 优秀的扩展性
  • 灵活

缺点

  • 违背了迪米特法则,Element具体的细节需要公布给Visitor
  • 具体元素变更比较困难,元素增加/减少字段Visitor都需要修改
  • 违背了依赖倒置原则,访问者依赖的是具体的元素,而不是抽象元素。

使用场景

当一个对象结构包含很多类对象,它们有不同的接口,需要对这些对象实施一些依赖于其具体类的操作。

双分派

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public abstract class AbsActor{
public void act(Role role){
System.out.println("Actor can act any role.");
}
public void act(KungFuRole role){
System.out.println("Actor can act Kungfu Role.");
}
}

public class YongActor extends AbsActor{
public void act(KungFuRole role){
System.out.println("Yong Man Lov Act KungFu Role.");
}
}

public class OldActor extends AbsActor{
public void act(KungFuRole role){
System.out.println("Old Man Can Not Act KungFu Role.")
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
public interface Role{
public void accept(AbsActor actor);
}
public class KungFuRole extends Role{
public void accept(AbsActor actor){
actor.act(this);
}
}
public class IdiotRole extends Role{
public void accept(AbsActor actor){
actor.act(this);
}
}
1
2
3
4
AbsActor actor = new OldActor();
Role role = new KungFuRole();
actor.act(role); //oldActor.act(Role)
actor.act(new KungFuRole()); //oldActor.act(KungFuRole);
1
2
3
4
AbsActor actor = new OldActor();
Role role = new KungFuRole();
role.accept(actor); //KungFuRole.accept(OldActor);
//OldActor.act(KungFuRole)

状态模式

在状态模式中, 类的行为时给予它的状态改变的。折中类型的设计模式属于行为型模式。

定义

当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类。

状态模式通用类图

实现

抽象状态角色State

接口或者抽象类,负责对象状态的定义,并且封装环境角色以实现状态切换。

1
2
3
4
5
6
7
8
9
10
11
12
13
public interface IState {
void open();
void close();
void run();
void stop();
}

public abstract class BaseState implements IState {
protected Car context;
public void setContext(Car context) {
this.context = context;
}
}

具体状态角色ConcreteState

每个具体状态必须完成两个职责:本状态下要做的事情以及本状态如何过渡到其他状态。

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
public class OpenState extends BaseState {
public void open() {
super.context.setCurrentState(Car.OPEN_STATE);
System.out.println("车门已开启");
}
public void close() {
super.context.setCurrentState(Car.CLOSE_STATE);
System.out.println("关闭车门。。。");
}
public void run() {
System.out.println("开门状态下不允许飙车start");
}
public void stop() {
System.out.println("开门状态下不允许飙车Stop");
}
}


public class StopState extends BaseState {
public void open() {
super.context.setCurrentState(Car.OPEN_STATE);
System.out.println("停车后开启车门");
}
public void close() {
super.context.setCurrentState(Car.CLOSE_STATE);
System.out.println("停车后关闭车门");
}
public void run() {
super.context.setCurrentState(Car.RUN_STATE);
System.out.println("停车后再次跑起来");
}
public void stop() {
super.context.setCurrentState(Car.STOP_STATE);
System.out.println("停车后。。停止不同");
}
}

public class RunState extends BaseState {
public void open() {
System.out.println("飙车时不能开门");
}
public void close() {
System.out.println("飙车时车门已关闭");
}
public void run() {
super.context.setCurrentState(Car.RUN_STATE);
System.out.println("飙车Run");
}
public void stop() {
super.context.setCurrentState(Car.STOP_STATE);
System.out.println("飙车 stop");
}
}

public class CloseState extends BaseState {
public void open() {
super.context.setCurrentState(Car.OPEN_STATE);
System.out.println("开启车门。。。");
}
public void close() {
System.out.println("车门已经关闭!");
}
public void run() {
this.context.setCurrentState(Car.RUN_STATE);
System.out.println("汽车开始运行。。。");
}
public void stop() {
this.context.setCurrentState(Car.STOP_STATE);
System.out.println("汽车停止。");
}
}

环境角色Context

定义客户端需要的接口,并且负责具体状态的切换。

把状态对象声明为静态常量,有几个状态对象就声明几个静态常量。
环境角色具有状态抽象角色定义的所有行为,具体执行使用委托方式。

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
public class Car implements IState {

public static final BaseState OPEN_STATE = new OpenState();
public static final BaseState CLOSE_STATE = new CloseState();
public static final BaseState RUN_STATE = new RunState();
public static final BaseState STOP_STATE = new StopState();
private BaseState currentState;
public Car() {
this.setCurrentState(CLOSE_STATE);
}
public BaseState getCurrentState() {
return currentState;
}
public Car setCurrentState(BaseState currentState) {
this.currentState = currentState;
this.currentState.setContext(this);
return this;
}
public void open() {
this.currentState.open();
}
public void close() {
this.currentState.close();
}
public void run() {
this.currentState.run();
}
public void stop() {
this.currentState.stop();
}
}

使用

结合建造者模式将已有的状态按照一定的顺序再重新组装。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Car car = new Car();

car.setCurrentState(Car.CLOSE_STATE);

car.run(); //汽车开始运行。。。
car.stop(); //飙车 stop

car.run(); //停车后再次跑起来
car.open(); //飙车时不能开门

car.stop(); //飙车 stop

car.open(); //停车后开启车门
car.close(); //关闭车门。。。

应用

优点

  • 结构清晰
    避免过多的switch case和if else,降低了程序的复杂性,提高系统的可维护性。
  • 遵循设计原则
    遵循了开闭原则以及单一职责原则,每个状态都是一个子类,要增加状态就要增加子类,修改状态时只修改一个子类即可。
  • 封装性良好
    状态变换防止到类的内部来实现,外部调用不需要知道类内部如何实现状态和行为的变换。

缺点

  • 状态过多时子类膨胀

使用场景

  • 行为跟随状态改变而改变的场景
  • 条件、分支判断语句的替代

代理模式

代理模式也叫委托模式,许多其他的模式如状态模式、策略模式、访问者模式本质上是在更特殊的场合采用了代理模式。
在日常应用中,代理模式可以提供非常好的访问控制。

定义

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

比如,我们的DB的访问权限不能直接公开,一般会公开一个接口来提供对这些数据。

代理模式通用类图

实现

抽象主题Subject

可以是抽象类或者接口,普通的业务类型定义。

1
2
3
public interface Subject{
public void request();
}

具体主题RealSubject

委托角色或者被代理角色。业务逻辑的具体执行者。

1
2
3
4
5
public class RealSubject implements Subject{
public void request(){
//do something.
}
}

代理主题Proxy

代理类、委托类, 负责对真实角色的调用,把所有抽象主题类定义的方法限制委托给真实主题角色实现,并且在真实主题角色处理完毕前后做预处理和善后处理工作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Proxy implements Subject{
private Subject subject = null;

public Proxy(){
this.subject = new Proxy();
}

public Proxy(Subject subject){
this.subject = subject;
}

public void request(){
this.before();
this.subject.request();
this.after();
}

private void before(){
//before
}
private void after(){
//after.
}
}

应用

优点

  • 职责清晰
    真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事物,通过后期的代理完成一件事物,附带的结果就是编程简洁清晰。
  • 扩展性好
    具体主题角色是随时都会发生变化的,只要它实现了接口,代理类可以在不做任何修改的情况下使用。
  • 智能化
    用于动态代理。

扩展

普通代理

客户端只能访问代理角色,而不能访问真实角色。屏蔽了真实角色的变更对高层模块的影响。该模式适合对扩展性要求较高的场合。一般通过编程规范类约束来禁止new一个真实的角色。

这种方式调用者只需要知道代理即可, 不需要知道代理了谁。

普通代理的实现者:

1
2
3
4
5
6
7
8
9
10
11
12
public class GamePlayer implements IGamePlayer{
private String name = "";
public GamePlayer(IGamePlayer _gamePlayer, String _name)throws Exception{
if(_gamePlayer == null || !(_gamePlayer instanceof GameProxy)){
throw new Exception("Create Not Allowed!");
}
this.name = _name;
}
public void killMonster(){
//do...
}
}

普通代理的代理者:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class GamePlayerProxy implements IGamePlayer{
private IGamePlayer gamePlayer = null;
public GamePlayerProxy(String name){
try{
gamePlayer = new GamePlayer(this,name);
}catch(Exception e){
//TODO
}
}

public void killMonster(){
this.gamePlayer.killMonster();
}
}

强制代理

只有通过真实角色指定的代理类才可以访问。

1
2
3
4
public interface IGamePlayer{
void killMonster();
public IGamePlayer getProxy();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class GamePlayer implements IGamePlayer{
private String name = "";
private IGamePlayer proxy = null;
public GamePlayer(String _name){
this.name = _name;
this.proxy = new GamepPlayerProxy(this);
}
public IGamePlayer getProxy(){
return this.proxy;
}
public void killMonster(){
//do...
}
}
1
2
3
4
5
6
7
8
9
public class GamePlayerProxy implements IGamePlayer{
private IGamePlayer gamePlayer = null;
public GamePlayerProxy(IGamePlayer _gamePlayer){
this.gamePlayer = _gamePlayer;
}
public void killMonster(){
this.gamePlayer.killMonster();
}
}

动态代理

在实现阶段不需要关心代理谁, 在运行阶段才指定代理哪个对象。

通过InvocationHandler接口,所有方法都由该Handler来接管实际的业务处理。

在不改变已有代码结构的情况下增强或者控制对象的行为。

动态代理Handler类:

1
2
3
4
5
6
7
8
9
public class MyInvocationHandler implements InvocationHandler{
private Object target = null;
public MyInvocationHandler(Object _obj){
this.target = _obj;
}
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable{
return method.invoke(this.target, args);
}
}

通知接口与实现

1
2
3
4
5
6
7
8
public interface IAdvice{
public void exec();
}
public class BeforeAdvice implements IAdvice{
public void exec(){
//....
}
}

动态代理类:

1
2
3
4
5
6
7
8
public class DynamicProxy<T>{
public static <T> T newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h){
if(condition){ //在condition连接点执行BeforeAdvice通知。
(new BeforeAdvice()).exec();
}
return (T)Proxy.newProxyInstance(loader, interface, h);
}
}

Client:

1
2
3
4
Subject subject = new RealSubject();
InvocationHandler handler = new MyInvocationHandler(subject);
Subject proxy = DynamicProxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), handler);
proxy.killMonster();

扩展

  • CGLib
1
2
3
4
5
6
7
8
9
10
public class LogInterceptor implements MethodInterceptor {

public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

System.out.println("Before Interceptor1");
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("After Interceptor1");
return result;
}
}
1
2
3
4
5
6
7
8
9
10
public class LogInterceptor2 implements MethodInterceptor {

public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

System.out.println("Before Interceptor2");
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("After Interceptor2");
return result;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
public class LogCallBackFilter implements CallbackFilter {

public int accept(Method method) {
if ("upgrade".equals(method.getName())){
System.out.println("Upgrade Filter");
return 0;
}

System.out.println("Kill Monster Filter");
return 1;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
LogInterceptor logInterceptor = new LogInterceptor();

LogInterceptor2 logInterceptor2 = new LogInterceptor2();

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(GamePlayer.class);
enhancer.setCallbacks(new Callback[]{logInterceptor,logInterceptor2, NoOp.INSTANCE});

enhancer.setCallbackFilter(new LogCallBackFilter());

IGamePlayer gamePlayer = (IGamePlayer)enhancer.create(new Class[]{String.class}, new Object[]{"Lisi"});

gamePlayer.killMonster();
gamePlayer.upgrade();
  • ASM
  • AOP

扩展阅读
cglib动态代理、asm学习笔记
Java动态代理详解

策略模式

策略模式使用的就是面向对象的继承和多态机制。

定义

定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。

策略模式通用类图

实现

Strategy 抽象策略角色

策略算法的抽象,通常是接口,定义每个策略或者算法必须具有的方法和属性。

1
2
3
public interface Strategy{
void doSomething();
}

ConcreteStrategy 具体策略角色

实现抽象策略的具体算法。

1
2
3
4
5
6
7
8
9
10
11
public class ConcreteStrategy1 implements Strategy{
public void doSomething(){
//Do Strategy1.
}
}

public class ConcreteStrategy2 implements Strategy{
public void doSomething(){
//Do Strategy2.
}
}

Context 封装角色

封装上下文,屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化。
策略模式的重点就是封装角色,它借用了代理模式的思路。

1
2
3
4
5
6
7
8
9
public class Context{
private Strategy strategy = null;
public Context(Strategy strategy){
this.strategy = strategy;
}
public void doAny(){
this.strategy.doSomething();
}
}

Use

1
2
3
Strategy strategy = new ConcreteStrategy1();
Context context = new Context(strategy);
context.doAny(strategy);

应用

优点

  • 算法可以自由切换
    只需要实现策略接口类,通过封装角色对其进行封装即可。
  • 避免使用多重条件判断
    1
    2
    3
    4
    5
    6
    if (condition1){
    strategy1.do();
    }else if(condition2){
    strategy2.do();
    }
    // .....
  • 扩展性好

缺点

  • 策略类数量多
  • 所有策略类都需要对外暴露
    上层模块必须知道有哪些策略, 然后才能决定使用哪一个策略,与迪米特法则相违背。
    (迪米特法则:一个类对于其他类知道的越少越好)

注意事项

当具体的策略数量超过一定个数时,考虑使用混合模式,解决策略类膨胀和对外暴露的问题,否则会带来很大的系统维护成本。

使用场景

  • 多个类只有在算法或者行为上稍有不同的场景
  • 算法需要自由切换的场景
  • 需要屏蔽算法规则的场景

策略枚举

当策略枚举不经常发生变化时可以使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public enum StrategyEnum {

ADD("+") {
@Override
public int exec(int a, int b) {
return a + b;
}
},
SUB("-") {
@Override
public int exec(int a, int b) {
return a - b;
}
};

private String opt;
StrategyEnum(String opt) {
this.opt = opt;
}

abstract int exec(int a, int b);
}

对象池模式

一种特殊的工厂对象,一个对象池包含一组已经初始化过且可以使用的对象。用户可以从池中取得对象,并对其进行操作,在不需要的时候将其归还给对象池。

使用对象池的目的是为了提升性能,当需要创建比较耗费时间的对象时尤为明显。

对象池通用类图

角色和职责

对象池的管理者

管理者来维护对象池,包括初始化对象池、扩充对象池的大小、获取对象时的策略、重置归还对象的状态。

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
public class PoolMgr{

private int init ;

private int max;

private int factor;

private Colection c; //

public void init(){
//init
}
//根据策略来动态的生成对象。。
//如果对象不足可以增加对象池的对象,或者blocking直到有对象释放。
public Object aquire(){

return c.remove();
}

public void release(Object o){
if(o == null) return;

reset(o);
c.add(o);
}

}

对象池的使用者

使用者从管理者处获取对象,使用完成后必须归还对象避免一致占用。

1
2
3
4
5
6
Object o = null;
try{
o = PoolMgr.getInstance();
}finaly{
PoolMgr.release(o);
}

优点

复用池中的对象,消除创建/回收对象所产生的内存开销,CPU开销以及IO开销。
常见的有:Socket的链接池、线程池、数据库连接池等。

缺点

多线程并发环境中,需要考虑线程安全问题,需要在数据结构上进行同步或者为锁竞争产生阻塞,这种开销处理不好会比创建销毁独享的开销高很多。
由于池中的对象数量有限,对象池小可能会成为性能瓶颈,太大占用内存资源高;
轻量级对象的创建/销毁不需要使用对象池,徒增系统的复杂度;

Common Pool2

Commons Pool2核心部分由三个基础接口和相关的实现类实现:

PooledObject

池化对象,对池中的对象进行封装,封装对象的状态和一些其他信息。
PooldedObject 有两个实现类,DefaultPoolObject时普通通用的实现,PooledSoftReference使用SoftReference封装了对象,供SoftReferenceObjectPool使用。

池化对象类图

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
public interface PooledObject<T> extends Comparable<PooledObject<T>> {
// 获取封装的对象
T getObject();

// 对象创建的时间
long getCreateTime();

// 对象上次处于活动状态的时间
long getActiveTimeMillis();

// 对象上次处于空闲状态的时间
long getIdleTimeMillis();

// 对象上次被借出的时间
long getLastBorrowTime();

// 对象上次返还的时间
long getLastReturnTime();

// 对象上次使用的时间
long getLastUsedTime();

// 将状态置为 PooledObjectState.INVALID
void invalidate();

// 更新 lastUseTime
void use();

// 获取对象状态
PooledObjectState getState();

// 将状态置为 PooledObjectState.ABANDONED
void markAbandoned();

// 将状态置为 PooledObjectState.RETURNING
void markReturning();
}

PooledObjectFactory

池化对象工厂,负责对象的创建、初始化、销毁和验证工作,Factory对象由ObjectPool持有并使用。
因为对象的创建、初始化、销毁和仰正的工作无法通用,Commons Pool2并没有提供PooledObjectFactory的默认子类实现,只提供了抽象子类BasePooledObjectFactory
可以实现create和wrap两个方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public interface PooledObjectFactory<T> {
// 创建一个池对象
PooledObject<T> makeObject() throws Exception;

// 销毁对象
void destroyObject(PooledObject<T> p) throws Exception;

// 验证对象是否可用
boolean validateObject(PooledObject<T> p);

// 激活对象,从池中取对象时会调用此方法
void activateObject(PooledObject<T> p) throws Exception;

// 钝化对象,向池中返还对象时会调用此方法
void passivateObject(PooledObject<T> p) throws Exception;
}

ObjectPool

持有对象并提供取/还等操作.

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
 public interface ObjectPool<T> {
// 从池中获取一个对象,客户端在使用完对象后必须使用 returnObject 方法返还获取的对象
T borrowObject() throws Exception, NoSuchElementException,
IllegalStateException;

// 将对象返还到池中。对象必须是从 borrowObject 方法获取到的
void returnObject(T obj) throws Exception;

// 使池中的对象失效,当获取到的对象被确定无效时(由于异常或其他问题),应该调用该方法
void invalidateObject(T obj) throws Exception;

// 池中当前闲置的对象数量
int getNumIdle();

// 当前从池中借出的对象的数量
int getNumActive();

// 清除池中闲置的对象
void clear() throws Exception, UnsupportedOperationException;

// 关闭这个池,并释放与之相关的资源
void close();

...
}

对象池配置

对象池配置提供了对象池初始化所需要的参数,Common Pool2中的基础配置类时BaseObjectPoolConfig, 有GenericObjectPoolConfig和GenericKeyedObjectPoolConfig,分别为GenericObjectPool和GenericKeyedObjectPool使用。

参数 解释 默认值
lifo 连接池存放池对象的方式; true:放在空闲队列的嘴钱买呢, false:放在空闲队列的最后面 true
fairness 从池中获取/返还对象时是否使用公平锁机制 false
maxWaitMillis 获取资源的等待时间。blockWhenExhausted为true时有效。-1代表无时间限制,一致阻塞到有可用的资源
minEvictableIdleTimeMillis 对象空闲的最小时间,打到此值后空闲对象可能会被移除,-1表示不移除 30分钟
softMinEvictableIdletimeMillis 对象空闲的最小时间,并且池中之少保留有minidle所指定的个数
numTestsPerEvictionRun 资源回收线程执行一次回收操作回收资源的个数 3
evictionPolicyClassName 资源回收策略 org.apache.commons.pool2.impl.DefaultEvictionPolicy
testOnCreate 创建对象时是否调用factory.validateObject false
testOnBorrow 获取对形势是否调用factory.validateObject false
testOnReturn 返还对象时是否调用factory.validateObject false
timeBetweenEvictionRunsMills 回收资源线程的执行周期,默认-1表示不启用回收资源线程 -1
blockWhenExhausted 资源耗尽时,是否阻塞等待获取资源 true
testWhileIdel 池中的限制对象是否由逐出器验证,无法验证的对象将从池中删除销毁 false

池化对象的状态

参数值 描述
IDEL 在池中,处于空闲状态
ALLOCATED 被使用中
EVICTION 正在被逐出器验证
VALIDAITTON 正在验证
INVALID 驱逐测试或者验证失败并将被销毁
ABANDONED 对象被客户端拿出后,长时间未返回池中,或者没有调用use方法,被标记为抛弃

Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class StringBufferFactory extends BasePooledObjectFactory<StringBuffer> {
// 创建一个新的对象
@Override
public StringBuffer create() {
return new StringBuffer();
}

// 封装为池化对象
@Override
public PooledObject<StringBuffer> wrap(StringBuffer buffer) {
return new DefaultPooledObject<>(buffer);
}

// 使用完返还对象时将 StringBuffer 清空
@Override
public void passivateObject(PooledObject<StringBuffer> pooledObject) {
pooledObject.getObject().setLength(0);
}
}

使用

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
 // 创建对象池配置
GenericObjectPoolConfig config = new GenericObjectPoolConfig();
// 创建对象工厂
PooledObjectFactory factory = new StringBufferFactory();
// 创建对象池
ObjectPool<StringBuffer> pool = new GenericObjectPool<>(factory, config);
StringReader in = new StringReader("abcdefg");
StringBuffer buf = null;
try {
// 从池中获取对象
buf = pool.borrowObject();

// 使用对象
for (int c = in.read(); c != -1; c = in.read()) {
buf.append((char) c);
}
return buf.toString();
} catch (Exception e) {
try {
// 出现错误将对象置为失效
pool.invalidateObject(buf);
// 避免 invalidate 之后再 return 抛异常
buf = null;
} catch (Exception ex) {
// ignored
}
throw e;
} finally {
try {
in.close();
} catch (Exception e) {
// ignored
}

try {
if (null != buf) {
// 使用完后必须 returnObject
pool.returnObject(buf);
}
} catch (Exception e) {
// ignored
}
}

参考:
Apache Common Pool2 对象池应用浅析
一个广为人知但鲜有人用的技巧:对象池