设计模式之枚举工厂模式

142 阅读4分钟

[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代码

一个生成不同厂商汽车的案列:

gitee.com/larry_jay/d…

参考:

www.runoob.com/design-patt…