[TOC]
介绍
背景
GOF
在 1994 年,由 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides 四人合著出版了一本名为 Design Patterns - Elements of Reusable Object-Oriented Software(中文译名:设计模式 - 可复用的面向对象软件元素) 的书,该书首次提到了软件开发中设计模式的概念。
四位作者合称 GOF(全拼 Gang of Four)。他们所提出的设计模式主要是基于以下的面向对象设计原则。
- 对接口编程而不是对实现编程。
- 优先使用对象组合而不是继承。
设计模式
- 创建型模式
- 结构型模式
- 行为型模式
本篇介绍的工厂模式属于设计模式中的构造者模式。
工厂模式
工厂模式提供了一种将对象的实例化过程封装在工厂类中的方式。通过使用工厂模式,可以将对象的创建与使用代码分离解耦,提供一种统一的接口来创建不同类型的对象。
在工厂模式中,我们在创建对象时不会对客户端使用时暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
场景
我目前面对一个业务功能场景,需要基于Netty,使用WebSocket+Stomp协议来处理消息。其中对消息帧的解析处理,需要根据不同消息数据帧(StompFrame)的类型采取不同的处理方式。这些类型有:建立连接(Connect)、断开连接(DisConnect)、发送(Send)、订阅(Subcribe)、取消订阅(Unsubcribe)等,但是消息通道是同一个,这个场景就是典型的创建和使用需要分离,使用统一的接口来处理不同类型的消息。
在编写代码实现功能过程中,有多种解决方案。如下:
- 方案1:判断选择if-else
一般情况,最直观会去采用类型标记+if-else或switch条件判断来,伪码如下:
if(commnd.equal(StompCommand.CONNECT)){
processor = new ConnectProcesser();
}else if(commnd.equal(StompCommand.DISCONNECT)){
processor = new DisConnectProcesser();
}else if(){
...
}
...
- 方案2:工厂模式
定义一个接口IStompProcessor,抽象消息处理方法process(ctx,frame),然后使用一个映射表Map,将类型与实际处理对象建立映射。伪码如下:
1.抽象接口:
public interface IStompProcesser{ void process(ctx,frame)}
2.每个具体实现:
public ConnectProcesser extends IStompProcesser{
void process(ctx,frame){
...connect的具体实现..
}
}
public SendProcesser extends IStompProcesser{
void process(ctx,frame){
...send的具体实现..
}
...
}
3.工厂类:
public StompProcesserManager{
Map<StompCommand, IStompProcesser> map = new HashMap<StompCommand, IStompProcesser>(7);
map.put(StompCommand.CONNECT, new ConnectProcesser());
map.put(StompCommand.SEND, new SendProcesser());
map.put(StompCommand.SUBSCRIBE, new SubscribeProcesser());
map.put(StompCommand.UNSUBSCRIBE, new UnsubscribeProcesser());
map.put(StompCommand.ERROR, new ErrorProcesser());
map.put(StompCommand.DISCONNECT, new DisconnectProcesser());
processers = Collections.unmodifiableMap(map);
}
4.具体调用:
//IStompProcesser为抽象接口。通过StompProcesserManager将map的处理消息对象取出来
IStompProcesser stompProcesser = StompProcesserManager将.getProcesser(frame.command());
StompFrame returnFrame = stompProcesser.process(ctx, frame);
- 方案3:枚举工厂模式(抽象工厂模式)
这个比方案2更加清晰、易读,区别是将每一个类型标记与处理器对象通过枚举类进行封装,而不是通过map数据结构。
与方法2工厂模式相比,第1,2步定义抽象接口和具体实现类都是一样的。
在设计工厂类时,通过枚举封装。实现类的插入和调用都通过枚举类来实现。
/**定义泛型工厂类*/
public enum Type{
CONNECT(1, StompCommand.CONNECT, ConnectProcessor::new),
SEND(2, StompCommand.SEND, SendProcessor::new),
...
public final int index;
public final StompCommand command;
public final StompProcessorFunction factory;
Type(int index, StompCommand command, StompProcessorFunction factory) {
this.index = index;
this.command = command;
this.factory = factory;
}
public int getIndex() {
return this.index;
}
public StompCommand getCommand() {
return this.command;
}
public static final Type[] MAPPING = new Type[]{
CONNECT
};
//通过类型标记command获取枚举
public static Type fromCommand(StompCommand command) throws IllegalArgumentException{
switch (command){
case CONNECT:
return CONNECT;
case SEND:
return SEND;
case SUBSCRIBE:
return SUBSCRIBE;
case UNSUBSCRIBE:
return UNSUBSCRIBE;
case DISCONNECT:
return DISCONNECT;
}
throw new IllegalArgumentException("Unknown command: " + command);
}
}
4.定义一个枚举工厂类创建方法。通过采用Java8之后的函数方法,来具体实现类的构造。
/**
* @desc 处理器工厂方法
*/
@FunctionalInterface
public interface StompProcessorFunction {
IStompProcessor create();
}
6.调用
通过传入类型标记msg.command(),获取需要的枚举类型Type,通过Type得到具体工程类factory,通过factory得到具体的实现类IStompProcessorImpl。
IStompProcessor iStompProcessor = IStompProcessor.Type.fromCommand(msg.command()).factory.create();
iStompProcessor.process(ctx,msg);
类图

1.接口及实现类
IStomProcessor:接口
ConnectProcessor:接口实现类
2.枚举工厂
Type:每个具体实现类的容器。可以通过枚举或者Map来映射。将类型Command和IStompProcessorFunction或者IStomProcessor建立映射。
3.函数方法
StompProcessorFunction:每个具体实现类的构造函数类。输出返回IStomProcessor
4.调用实现
通过类型command和工厂类Type,构建具体实现类。
Demo代码
一个生成不同厂商汽车的案列: