工厂模式(Factory Pattern)是我们在编码中经常会用到的一种设计模式,它属于一种创建型的设计模式,也就是说,对于一个面向对象型的语言来说,这是一种创建对象的比较好的方式。在客户端使用时,无需知道创建对象的逻辑,只需要根据自己的需求,获得一个共同的接口,这个接口变量指向的便是我们需要的实现类对象。
一、使用场景
需要根据不同的条件创建不同的对象,具体创建出什么样的对象,由我们客户端决定,这个工厂只需要提供一个简单的创建对象的接口就行。
比如:我们有一个后台服务程序,需要处理多种协议的报文,json,xml,定长报文,心跳报文,我们会为每一种协议的解析对应一个解析器的类,那么怎么根据不同的报文来获取不同的解析器的处理类,这里我们就可以考虑使用工厂模式,这样不仅简化了分发的流程,而且便于扩展,当以后新增通讯协议,不用修改主要的处理流程,只需根据要求增加对应的解析类即可。
图2 工厂模式
从图一图二的流程图可以知道,工厂模式使得客户端不用关注解析类是怎么创建的,只需要从工厂方法里取就好了,当增加协议时,图一会随着协议的增加,分支也会越来越多,图二基本保持不变。
二、简单工厂模式
那么现在我们使用简单工厂模式来实现上述的场景。
首先我们创建一个结果对象Request,用来接收解析成功后的结果。
/**
* Created by mitch on 2018/1/17.
*/
public class Request {
//被谁解析的
private String parser;
/*报文中的内容*/
private String name;
public String getParser() {
return parser;
}
public void setParser(String parser) {
this.parser = parser;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Request{");
sb.append("parser='").append(parser).append('\'');
sb.append(", name='").append(name).append('\'');
sb.append('}');
return sb.toString();
}
}
再创建一个解析类的统一接口RequestParser,用来接收工厂返回的实现类实例。
/**
* Created by mitch on 2018/1/17.
*/
public interface RequestParser {
Request parse(String message);
}
创建各个解析报文的实现类,并实现parse方法。
XML解析类XmlRequestParser
/**
* Created by mitch on 2018/1/17.
*/
public class XmlRequestParser implements RequestParser{
@Override
public Request parse(String message) {
System.out.println("XmlRequestParser正在解析...");
String name = "张三";//假设解析出来的内容为张三
Request request = new Request();
request.setName(name);
request.setParser("XmlRequestParser");
return request;
}
}
json解析类JsonRequestParser
/**
* Created by mitch on 2018/1/17.
*/
public class JsonRequestParser implements RequestParser {
@Override
public Request parse(String message) {
System.out.println("JsonRequestParser正在解析...");
String name = "张三";//假设解析出来的内容为张三
Request request = new Request();
request.setName(name);
request.setParser("JsonRequestParser");
return request;
}
}
最后创建解析器的工厂,用于生产不同的解析器类。
/**
* Created by mitch on 2018/1/17.
*/
public class ParserFactory {
public static RequestParser getPaser(String message){
String protocol = getProtocol(message);
if("xml".equals(protocol)){
return new XmlRequestParser();
}else if("json".equals(protocol)){
return new JsonRequestParser();
}else{
throw new RuntimeException(String.format("不支持的协议[%s]",protocol));
}
}
/**
* 简单模拟获取不同的报文的协议类型
*
* @param message
* @return
*/
private static String getProtocol(String message){
if(message.startsWith("{")&&message.endsWith("}")){
return "json";
}else if(message.startsWith("<")&&message.endsWith(">")){
return "xml";
}else {
return "unkown";
}
}
}
客户端代码:
/**
* Created by mitch on 2018/1/17.
*/
public class Client {
public static void main(String[] args) {
String message = "{name:张三}";
System.out.println(String.format("接收到消息:[%s]",message));
RequestParser parser = ParserFactory.getPaser(message);
Request request = parser.parse(message);
System.out.println(String.format("解析得到的结果[%s]",request));
}
}
运行得到结果:
图3 简单工厂方法运行结果
从工厂类的代码中,我们可以看到,随着协议越来越多,工厂类的if/else分支也会越来越多,那么我们可以通过配置文件加上反射的方式,如:配置文件中配置jsonParserClass=com.xx.JsonRequestParser等,通过协议类型获取到不同的class名称,再通过反射的方式获取到实例。这里不再详细说明,有兴趣的可以自己动手尝试。
三、工厂方法模式
看了简单工厂方法以后,我们发现,如果要新增协议时,需要修改工厂类的代码。这样对程序的扩展性还是不利,所以我们可以将原来的工厂作为一个抽象类,让其子类去实现创建对象,也就是很多个工厂类实现一个核心的抽象工厂类,核心抽象工厂类提供一个创建具体对象的抽象方法,让子类在这个方法中创建具体对象。
那么我们使用工厂方法模式以后,我们实例的工厂类和客户端的代码变成如下:
抽象工厂:ParserFactory修改为:
/**
* Created by mitch on 2018-01-17.
*/
public abstract class ParserFactory {
public abstract RequestParser createParser();
}
新增对应的具体功能的工厂:
XmlParserFactory
/**
* Created by mitch on 2018-01-17.
*/
public class XmlParserFactory extends ParserFactory {
@Override
public RequestParser createParser() {
//这里可以不再是简单的创建对象,还可以对对象进行初始化,
// 或者进行一些与创建对象相关的复杂逻辑
return new XmlRequestParser();
}
}
JsonParserFactory
/**
* Created by Mitch on 2018-01-17.
*/
public class JsonParserFactory extends ParserFactory {
@Override
public RequestParser createParser() {
//这里可以不再是简单的创建对象,还可以对对象进行初始化,
// 或者进行一些与创建对象相关的复杂逻辑
return new JsonRequestParser();
}
}
客户端代码修改为:
/**
* Created by mitch on 2018-01-17.
*/
public class Client {
public static void main(String[] args) {
//这里的工厂可以通过反射或者Spring动态配置
ParserFactory parserFactory = new XmlParserFactory();
String message = "<name>张三</name>";
System.out.println(String.format("接收到消息:[%s]",message));
Request request = parserFactory.createParser().parse(message);
System.out.println(request);
}
}
运行结果:
图4 工厂方法模式运行结果
工厂方法模式和简单工厂模式的最大的区别就是工厂方法模式多了一层具体的工厂类,客户端在调用时需要根据不同的需求获取不同的工厂类,再创建具体的对象。由此可见:
简单工厂模式的优缺点:简单,客户端做的事情非常少,除去了对具体对象的依赖。但将所有创建具体对象的代码挤在一块,不符合开闭原则,扩展性不好。
工厂方法模式优缺点:扩展性好,没有随着具体对象种类的增多而增加代码分支。但它将具体选择分发的逻辑转移到了客户端;而且增加了一层类关系,增加了一定的开发工作量。
四、抽象工厂模式
通过工厂方法模式我们可以知道,每一个工厂仅能创建一类对象,而且工厂方法模式中会出现大量的工厂类,无疑会增加复杂度和开发量。因此可以将工厂方法中的每个工厂类的职责变大,让它可以生产更多的对象。
假设我们系统现在不仅要支持xml、json、其他协议的解析,还要支持这些协议报文的组装和截取。那么如果按照工厂方法模式的话需要3种协议*3种功能=9个具体工厂类分别负责生产:解析、组装、截取这三种协议的解析类。这种开发体验肯定是很不好的,所以我们可以让每一个具体工厂类都支持生产处理这三种协议的对象,每一个具体工厂类对应一种操作,比如解析报文的工厂,组装报文的工厂,截取报文的工厂。
图5 抽象工厂模型
上图中,对同一种协议的各种操作属于一个产品等级结构,对各个协议的同一个操作为一个产品族,在抽象工厂模式中,每一个具体的工厂都可以生产一个产品族的对象,也就是说,解析工厂可以负责生产所有协议的解析对象,组装工厂可以负责生产所有协议的组装对象,以此类推。
现在有需要生产的对象如下:
每种协议对应的抽象处理类AbstractJsonHandler、AbstractXmlHandler
/**
* Created by mitch on 2018/1/18.
*/
public abstract class AbstractJsonHandler<T,K>{
public abstract T hand(K param);
}
/**
* Created by mitch on 2018/1/18.
*/
public abstract class AbstractXmlHandler<T,K>{
public abstract T hand(K param);
}
对每个协议进行具体操作的类JsonParser、JsonPackager、XmlPackager、XmlParser等
/**
* Created by mitch on 2018/1/18.
*/
public class JsonPackager extends AbstractJsonHandler<String,Request> {
@Override
public String hand(Request param) {
System.out.println("JsonPackager正在包装");
//对报文进行包装
return "{message:包装好的Json报文}";
}
}
/**
* Created by mitch on 2018/1/18.
*/
public class JsonParser extends AbstractJsonHandler<Request,String> {
@Override
public Request hand(String param) {
System.out.println("JsonMessageParser正在解析...");
String name = "张三";//假设解析出来的内容为张三
Request request = new Request();
request.setName(name);
request.setParser("JsonParser");
return request;
}
}
/**
* Created by mitch on 2018/1/18.
*/
public class XmlPackager extends AbstractXmlHandler<String,Request> {
@Override
public String hand(Request param) {
System.out.println("XmlPackager正在包装报文");
//包装xml报文
return "<message>包装好的xml报文</message>";
}
}
/**
* Created by mitch on 2018/1/18.
*/
public class XmlParser extends AbstractXmlHandler<Request,String> {
@Override
public Request hand(String param) {
System.out.println("XmlMessageParser正在解析...");
String name = "张三";//假设解析出来的内容为张三
Request request = new Request();
request.setName(name);
request.setParser("XmlParser");
return request;
}
}
抽象工厂MessageHandlerFactory,用来提供生产一个产品族的多个抽象方法
/**
* Created by mitch on 2018/1/18.
*/
public abstract class MessageHandlerFactory{
public abstract AbstractJsonHandler getJsonHandler();
public abstract AbstractXmlHandler getXmlHandler();
//其他协议
}
具体工厂,对应对一个产品族的处理:
PackageFactory 打包工厂
/**
* Created by mitch on 2018/1/18.
*/
public class PackageFactory extends MessageHandlerFactory{
@Override
public AbstractJsonHandler getJsonHandler() {
return new JsonPackager();
}
@Override
public AbstractXmlHandler getXmlHandler() {
return new XmlPackager();
}
}
ParserFactory工厂,解析工厂
/**
* Created by mitch on 2018/1/18.
*/
public class ParserFactory extends MessageHandlerFactory {
@Override
public AbstractJsonHandler getJsonHandler() {
return new JsonParser();
}
@Override
public AbstractXmlHandler getXmlHandler() {
return new XmlParser();
}
}
。
。
。
客户端代码
/**
* Created by mitch on 2018/1/18.
*/
public class Client {
public static void main(String[] args) {
//该工厂可以进行配置或者动态注入
MessageHandlerFactory factory = new PackageFactory();
AbstractJsonHandler<String,Request> jsonHandler = factory.getJsonHandler();
System.out.println(jsonHandler.hand(new Request()));
AbstractXmlHandler<String,Request> xmlHandler = factory.getXmlHandler();
System.out.println(xmlHandler.hand(new Request()));
}
}
运行结果
图6 抽象工厂运行结果
现在我们来梳理下抽象工厂中的类关系
MessageHandlerFactory
抽象工厂类,提供生产一个产品族的抽象方法
PackageFactory、ParserFactory
具体工厂类,生产具体的对象,结合了协议和处理方法
它们的关系为:
图7 工厂类关系图
AbstractJsonHandler、AbstractXmlHandler
需要创建的抽象类,接收具体工厂创建的对象
JsonPackager、JsonParser、XmlPackager、XmlParser
需要创建的具体对象
它们的关系为:
图8 产品关系图
通过代码我们不难发现,如果新增一种协议,每个具体工厂类都要新增一个对应协议的创建方法,不符合开闭原则,但是新增一种处理方法很简单,只用新增对应的操作类和具体工厂就可以。
所以我们在用这个模式时,要考虑我们的产品族中是否以后还会新增一个类型,相当于是否还有协议要增加。
如果协议要经常增加,但处理方法就解析组装截取这几个比较稳定,可以把产品族换成处理方式,将图5的横轴和纵轴调换一下,这样便可以了。设计模式具体应该怎么使用,一定要跟实际的场景相结合。
网友评论