设计模式-简单工厂
生活中,原始社会自给自足(没有工厂),农耕社会小作坊(简单工厂,民间酒坊),工业革命流水线(工厂方法,自产自销),现代产业链代工厂(抽象工厂,富士康)。人们不再关心产品时如何生产的,他们只需要知道自己要什么,工厂就会给什么。至于怎么生产,那是工厂的事情。
1. 概述
在日常开发中,凡是需要生成复杂对象的地方,都可以尝试考虑使用工厂模式来代替。简单工厂模式有一个具体的工厂类,一个产品的抽象类或者接口,用来规范产品。将所有的具体产品的生产都罗列到工厂中。
1.1. 优点
- 很方便的创建出相应的产品。工厂和产品的职责区分明确;
- 客户端无需知道所创建具体产品的类名,只需知道参数即可;
- 也可以引入配置文件,在不修改客户端代码的情况下更换和添加新的具体产品类;
1.2. 缺点
- 每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
1.3. 使用场景
- 简单工厂模式的工厂类单一,负责所有产品的创建,职责过重,一旦异常,整个系统将受影响。且工厂类代码会非常臃肿,违背高聚合原则;
- 使用简单工厂模式会增加系统中类的个数(引入新的工厂类),增加系统的复杂度和理解难度;
- 系统扩展困难,一旦增加新产品不得不修改工厂逻辑,在产品类型较多时,可能造成逻辑过于复杂;
- 简单工厂模式使用了 static 工厂方法,造成工厂角色无法形成基于继承的等级结构;
2. 代码实例
在做 TCP 服务数据解析时,我们通常需要将接收到的数据转化为不同格式的数据报文。我们抽象处理器来处理此类业务。
package com.skystep.设计模式.工厂模式.简单工厂;
public interface Handler {
// 协议转换 将接受到的报文转化为另一种格式的报文
void change();
}
现在我们试着写一个转化为JSON格式的处理。
package com.skystep.设计模式.工厂模式.简单工厂;
public class JsonHandler implements Handler {
@Override
public void change() {
// 进行json格式解析
System.out.println("JSON-DATA");
}
}
我们也可以实现一个xml格式的处理器。
package com.skystep.设计模式.工厂模式.简单工厂;
public class XmlHandler implements Handler {
@Override
public void change() {
// 进行json格式解析
System.out.println("XML-DATA");
}
}
当然我们还可以增加一个默认的处理器,作为报文的默认处理器。
package com.skystep.设计模式.工厂模式.简单工厂;
public class DefaultHandler implements Handler {
@Override
public void change() {
// 进行json格式解析
System.out.println("DEFAULT-DATA");
}
}
实现一个简单工厂,把所有的处理的生产罗列到工厂类中。
package com.skystep.设计模式.工厂模式.简单工厂;
public class HandlerFactory {
public static Handler getHandler(HandlerType handlerType) {
Handler ret;
if (handlerType == HandlerType.JSON_HANDLER) {
ret = new JsonHandler();
} else if (handlerType == HandlerType.XML_HANDLER) {
ret = new XmlHandler();
} else {
ret = new DefaultHandler();
}
return ret;
}
}
package com.skystep.设计模式.工厂模式.简单工厂;
public enum HandlerType {
JSON_HANDLER(1, "JSON_HANDLER"),
XML_HANDLER(2, "XML_HANDLER"),
DEFAULT_HANDLER(0, "DEFAULT_HANDLER");
private Integer code;
private String desc;
HandlerType(Integer code, String desc) {
this.code = code;
this.desc = desc;
}
public Integer getCode() {
return code;
}
public String getDesc() {
return desc;
}
}
客户端模拟获取处理器。
package com.skystep.设计模式.工厂模式.简单工厂;
public class Client {
public static void main(String[] args) {
Handler jsonHandler = HandlerFactory.getHandler(HandlerType.JSON_HANDLER);
jsonHandler.change();
Handler xmlHandler = HandlerFactory.getHandler(HandlerType.XML_HANDLER);
xmlHandler.change();
}
}
JSON-DATA
XML-DATA
Process finished with exit code 0
3. 扩展解读
3.1. SpringBoot 项目中使用 简单工厂
在实际项目中,我们通常使用 SpringBoot 进行开发,结合 IOC 机制也可以使用简单工厂达到简化产品生产,同时符合开闭原则。
规范处理器的行为,并且要求每类处理器都需要增加一个id,相当于产品的型号。让客户端方便指定生产哪个型号的处理器。
package com.skystep.aspect.sevice;
public interface Handler {
// 协议转换 将接受到的报文转化为另一种格式的报文
String change(String data);
// 获取处理器的id,相当于产品的型号
Integer getId();
}
实现JSON处理器,并且使用 @Component 注解注册为 IOC 容器中的一个实例。
package com.skystep.aspect.sevice;
import org.springframework.stereotype.Component;
@Component
public class JsonHandler implements Handler {
@Override
public String change(String data) {
// 进行json格式解析
System.out.println("JSON-DATA");
return "JSON-DATA:" + data;
}
@Override
public Integer getId() {
return HandlerType.JSON_HANDLER.getCode();
}
}
实现XML处理器,并且使用 @Component 注解注册为 IOC 容器中的一个实例。
package com.skystep.aspect.sevice;
import org.springframework.stereotype.Component;
@Component
public class XmlHandler implements Handler {
@Override
public String change(String data) {
// 进行json格式解析
System.out.println("XML-DATA");
return "XML-DATA:" + data;
}
@Override
public Integer getId() {
return HandlerType.XML_HANDLER.getCode();
}
}
实现默认处理器,并且使用 @Component 注解注册为 IOC 容器中的一个实例。
package com.skystep.aspect.sevice;
import org.springframework.stereotype.Component;
@Component
public class DefaultHandler implements Handler {
@Override
public String change(String data) {
// 进行json格式解析
System.out.println("DEFAULT-DATA");
return "DEFAULT-DATA:" + data;
}
@Override
public Integer getId() {
return HandlerType.DEFAULT_HANDLER.getCode();
}
}
实现一个简单工厂,在工厂中维护一个 map,将型号和处理器对应起来,存储到 map,提供统一的获取方法。
package com.skystep.aspect.sevice;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;
@Component
public class HandlerFactory {
@Autowired
private ApplicationContext ac;
public static Map<Integer, Handler> handers = new HashMap<>();
@PostConstruct
public void init() {
Map<String, Handler> settings = ac.getBeansOfType(Handler.class);
for (Map.Entry<String, Handler> el : settings.entrySet()) {
Handler handler = el.getValue();
Integer id = handler.getId();
handers.put(id, handler);
}
}
public Handler getHandler(Integer id) {
return handers.get(id) == null ?
handers.get(HandlerType.DEFAULT_HANDLER.getCode()) : handers.get(id);
}
}
实现一个控制器,供客户端进行调用,可以输入需要转化的字符串和转化的的格式(JSON/XML等)。
package com.skystep.aspect.controller;
import com.skystep.aspect.sevice.Handler;
import com.skystep.aspect.sevice.HandlerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@Autowired
HandlerFactory handlerFactory;
@RequestMapping("/handler")
public String change(@RequestParam String data, @RequestParam Integer type) {
Handler handler = handlerFactory.getHandler(type);
return handler.change(data);
}
}
在这里,我们巧妙使用简单工厂和IOC思想,使用 getBeanByType 将实现 Handler 接口的实例汇总到工厂中,又提供了获取方法。如此一来,后续的要实现不同的数据转化,我们只需实现另外一个处理类,将类注册到IOC容器中即可,其他的业务逻辑代码都不需要改变,基本上达到开闭原则。