设计模式之工厂模式【factory】(二)
一、基础概念解析
定义
工厂模式是另一个有关创建对象概念的模式。它和其他的设计模式的区别在于它没有显示地要求我们使用构造函数,相反,它为创建对象提供一个通用的接口,用这个接口我们可以创建我们希望创建的指定类型的工厂对象。
应用实例
1、您需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现。 2、Hibernate 换数据库只需换方言和驱动就可以。
使用场景
1、日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。 2、数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。 3、设计一个连接服务器的框架,需要三个协议,“POP3”、“IMAP”、“HTTP”,可以把这三个作为产品类,共同实现一个接口。
优点: 1、一个调用者想创建一个对象,只要知道其名称就可以了。 2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。 3、屏蔽产品的具体实现,调用者只关心产品的接口。
缺点:1.调用的方便,不需要关心其中的产生过程与条件也就意味着对其对象的产生是不清晰的,势必会影响对代码可读性的影响以及增大理解难度。2.对象实例的过多创建,占用堆内存,与正常的对象创建操作相比,在获取目标对象前,工厂模式的使用势必要多创建一个工厂类对象,过多的工厂类对象创建若没有被及时回收则会使本就较小的堆内存更拥挤。
注意:借由在缺点当中已经提及的原因,以及程序复杂度、时间复杂度…等等原因,在对于简单对象的创建选择上一般不采用工厂模式进行对象的创建操作。
二、图解实例说明
阅读菜鸟教程中得工厂模式的图解:
三、代码示范
步骤 1
创建一个接口:
Shape.java
public interface Shape {
void draw();
}
步骤 2
创建实现接口的实体类。
Rectangle 的实现类
Rectangle.java
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
Square 的实现类
Square.java
public class Square implements Shape {
@Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}
Circle.java的实现类
Circle.java
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
步骤 3
创建一个工厂,生成基于给定信息的实体类的对象
ShapeFactory.java
public class ShapeFactory {
//使用 getShape 方法获取形状类型的对象
public Shape getShape(String shapeType){
if(shapeType == null){
return null;
}
if(shapeType.equalsIgnoreCase("CIRCLE")){
return new Circle();
} else if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new Rectangle();
} else if(shapeType.equalsIgnoreCase("SQUARE")){
return new Square();
}
return null;
}
}
步骤 4
使用该工厂,通过传递类型信息来获取实体类的对象。
FactoryPatternDemo.java
public class FactoryPatternDemo {
public static void main(String[] args) {
ShapeFactory shapeFactory = new ShapeFactory();
//获取 Circle 的对象,并调用它的 draw 方法
Shape shape1 = shapeFactory.getShape("CIRCLE");
//调用 Circle 的 draw 方法
shape1.draw();
//获取 Rectangle 的对象,并调用它的 draw 方法
Shape shape2 = shapeFactory.getShape("RECTANGLE");
//调用 Rectangle 的 draw 方法
shape2.draw();
//获取 Square 的对象,并调用它的 draw 方法
Shape shape3 = shapeFactory.getShape("SQUARE");
//调用 Square 的 draw 方法
shape3.draw();
}
}
步骤 5
执行程序,输出结果:
四、实际开发中的代码应用
在短信对接平台的代码编写过程当中,对于工厂模式类似的策略选择进行了应用
1.设定的接口
public interface IChannelOperator {
/**
* 分组标识
*
* @return
*/
GroupDTO group();
/**
* 单发
*
* @param smsSingleBO
* @return
*/
SmsResult execute(SmsSingleBO smsSingleBO);
/**
* 群发
*
* @param smsMultiBO
* @return
*/
SmsResult execute(SmsMultiBO smsMultiBO);
}
2.进行接口实现
【1】第一步的实现
【2】渠道的对接过程
3.创建工厂类,区别不同的短信分发(验证/营销/…等不同的功能区分)
具体的区分根据接口中
/**
* 分组标识
*
* @return
*/
GroupDTO group(); 中所返回的值作为工程中的标识
编写一个改良版的工厂类:
@Autowired
private List<IChannelOperator> channelOperatorList;
@Autowired
private IMessageHandle messageHandle;
private Map<Integer,List<IChannelOperator>> channelGroupMap=new HashMap<>();
@PostConstruct
public void init() {
//TODO:初始化通道供应商
//TODO:填充channelGroupMap
//根据flag字段进行分组
for(IChannelOperator channelOperator:channelOperatorList){
if(ChannelTypeEnum.VERIFY_CHANNEL.getCode()==channelOperator.group().getFlag()){
List<IChannelOperator> verifyChannelList = channelGroupMap.get(ChannelTypeEnum.VERIFY_CHANNEL.getCode());
if(verifyChannelList==null){
verifyChannelList=new ArrayList<>();
channelGroupMap.put(ChannelTypeEnum.VERIFY_CHANNEL.getCode(),verifyChannelList);
}
verifyChannelList.add(channelOperator);
}else if(ChannelTypeEnum.SALE_CHANNEL.getCode()==channelOperator.group().getFlag()){
List<IChannelOperator> saleChannelList = channelGroupMap.get(ChannelTypeEnum.SALE_CHANNEL.getCode());
if(saleChannelList==null){
saleChannelList=new ArrayList<>();
channelGroupMap.put(ChannelTypeEnum.SALE_CHANNEL.getCode(),saleChannelList);
}
saleChannelList.add(channelOperator);
}else{
List<IChannelOperator> collectionChannelList = channelGroupMap.get(ChannelTypeEnum.COLLECTION_CHANNEL.getCode());
if(collectionChannelList==null){
collectionChannelList=new ArrayList<>();
channelGroupMap.put(ChannelTypeEnum.COLLECTION_CHANNEL.getCode(),collectionChannelList);
}
collectionChannelList.add(channelOperator);
}
}
//分组完成后,进行排序
GroupSortUtil.sortByOrder(channelGroupMap);
}
初步编写,可以进行进一步的优化,中间还有很多的if、else,显得很是冗余。
调用展示,对改进工厂的使用过程,根据前端传递的发送类型以及业务类型等,从工厂中取出相对应的派发实现类
public SmsResult dispatch(ChannelBO channelBO) {
try {
List<IChannelOperator> channelOperatorList = channelGroupMap.get(channelBO.getChannelType());
if(channelOperatorList!=null) {
SmsResult smsResult = messageHandle.handle(channelBO, channelGroupMap.get(channelBO.getChannelType()));
return smsResult;
}else{
throw new MessageException(MessageFactory.getMsg("G19880111"));
}
}catch (Exception e){
LOGGER.error(e.getMessage(),e);
}
return null;
}
总结:工厂模式其实就是一个,多方案选择的一种策略。