顾名思义就是原型模式工厂模式的结合。在GOF的书里面,抽象工厂那节中对这个有描述,不过内容很少,所以看的时候也没注意到。

说个实际的问题:系统需要向用户发送短信,但是根据场景的不同,发送的短信格式也不同,比如重置密码和发送临时验证码的短信格式就有区别,而之前的开发人员使用了策略模式,每个内容都有一个类,现在也不方便去改动这些。但是随需求的增加,发送的场景在不断增加,策略+工厂方法模式的情况下需要去增加更多的if-else,导致扩展困难,也使得调用时候不直观。

本人自认为自己小脑瓜没有能力想出好办法,只好去翻下书籍,无意中看到了抽象工厂内介绍原型工厂的内容,想着可以借鉴。如果依靠一定规范的传入参数,通过if-else来判断需要使用的算法闲代码忒长,那么不为什么不直接传入算法呢。当然传入算法的类的话太费资源,但是传个Class或Class Name不适问题。

参数就是用枚举,这比String参数好,原因是String可以传入任何字符串,而枚举只能是枚举内的指定值,枚举代码如下:

public enum MessageBuilderEnum {  
  
    RESET_PASSWORD_SMS(ResetPwSmsMsgSendServiceImpl.class),  
    RESET_PASSWORD_EMAIL(ResetPwMailMsgSendServiceImpl.class),  
    SMS_FEE(SmsFeeMsgSendServiceImpl.class),  
    TEMP_CODE_SMS(TempCodeSmsMsgSendServiceImpl.class),  
    UNIVERSAL_SMS(BoundMsgSendServiceImpl.class);  
      
    private Class<?> builderClass;  
      
    MessageBuilderEnum(Class<?> builderClass) {  
        this.builderClass = builderClass;  
    }  
      
    public Class<?> getBuidlerClass() {  
        return builderClass;  
    }  
}

再看下功能实现的代码

private static ConcurrentHashMap<String,
    AbstractMsgSendServiceImpl> messageBuilder =
    new ConcurrentHashMap<String, AbstractMsgSendServiceImpl>();  
  
public MessageSendServiceImpl() {  
    for (MessageBuilderEnum builderClass 
        : MessageBuilderEnum.values()) {  
        try {  
            messageBuilder.put(builderClass.getBuidlerClass()
                .getSimpleName(),  
                    (AbstractMsgSendServiceImpl) builderClass
                        .getBuidlerClass().newInstance());  
        } catch (InstantiationException e) {  
            logger.error(e.getMessage(), e);  
        } catch (IllegalAccessException e) {  
            logger.error(e.getMessage(), e);  
        }  
    }  
}  
  
@Override  
public String send(ServMessage message,
    MessageBuilderEnum builderEnum) {  
    AbstractMsgSendServiceImpl builder = messageBuilder  
            .get(builderEnum.getBuidlerClass()
            .getSimpleName()).clone();  
    if (builder != null) {  
        return builder.send(message);  
    } else {  
        return null;  
    }  
}

工厂方法是在构造函数下,在初始化的时候就为枚举下所有的类初始化,然后保存在Map下,用于当作原型。在需要使用的时候再对原型clone一下就是了。

其实可以在需要使用时候初始化。这里使用原型是听说原型创建新对象比较快,当然本人没仔细测试过,有时间再研究。

原型工厂的工厂可以是抽象工厂模式,也可以是工厂方法模式。