Spring Boot中使用注解实现简单工厂模式

前言

  从设计模式的类型上来说,简单工厂模式是属于创建型模式,又叫静态工厂模式(Simple Factory Pattern),但不属于23种GOF设计模式之一。简单工厂模式是由一个工厂对象决定创建出接口哪一种实现类的实例。简单工厂模式是工厂模式家族中最简单实用的模式,可以理解为是不同工厂模式的一个特殊实现。 

  在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。

  简单工厂模式只需要向工厂类传入一个正确的参数,就可以获取所需要的对象,而无需知道其实现过程。

  简单工厂模式是在什么场景下使用呢?这里简单的枚举几个使用场景:

1、日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方;

2、保险产品配置:保险产品往往有很多赔付等级,每个等级对应不同的保额和赔付金额;

3、设计一个连接服务器的框架,需要三个协议,"POP3"、"IMAP"、"HTTP",可以把这三个作为产品类,共同实现一个接口。

 定义中最重要的一句话就是,由一个工厂对象决定创建出哪一种实现类的实例。在文章《Spring注解之@Autowired:注入Arrays, Collections, and Maps》中,小编介绍了Spring Boot如何把某些接口实现类的Bean注入到Map和List等集合中,方便应用的时候直接读取需要的bean。结合@Autowired可以自动注入指定接口实现类到Map中,介绍简单工厂模式的一个实现策略。

简单工厂模式实践

  我们将创建一个 Shape 接口和实现 Shape 接口的实体类。下一步是定义工厂类 ShapeFactory。在controller FactoryPatternDemo 中,我们演示使用 ShapeFactory 来获取 Shape 对象。它将向 ShapeFactory 传递信息(circle/rectangle/square),以便获取它所需对象的类型。

   新增接口类Shape:

/**
 * 定义bean接口
 */
public interface Shape {
   void draw();
}

   创建Shape的三个实现类,并且都添加注解@Service,将其注册为Spring Bean。

@Service
public class Rectangle implements Shape {
 
   @Override
   public void draw() {
      System.out.println("Inside Rectangle::draw() method.");
   }
}
=========== 我是分割线 =============
@Service
public class Square implements Shape {
 
   @Override
   public void draw() {
      System.out.println("Inside Square::draw() method.");
   }
}
=========== 我是分割线 =============
@Service
public class Circle implements Shape {
 
   @Override
   public void draw() {
      System.out.println("Inside Circle::draw() method.");
   }
}

 实现方法一:基于新建对象实现

  兹创建一个工厂类,根据调用者的不同,创建不同的实现类并返回。而如果碰到不合法的请求参数,则会返回null。 

/**
 * 简单工厂类,通过新建对象实现
 * 不添加Spring注解,调用的时候直接new
 */
public class ShapeFactory {
    /**
     * 使用 getShape 方法获取几何图形类型的对象
     */
    public static Shape getShape(String shapeType) {
        if ("circle".equalsIgnoreCase(shapeType)) {
            return new Circle();
        } else if ("rectangle".equalsIgnoreCase(shapeType)) {
            return new Rectangle();
        } else if ("square".equalsIgnoreCase(shapeType)) {
            return new Square();
        }
        return null;
    }
}

 实现方法二:基于spring注解实现

  下面新增一个类ShapeBeanFactory,通过@Autowired注解把bean注入map后,由变量map替换工厂类,实现简单工厂模式。

/**
 * 简单工厂类,通过 Spring注解 @Autowired 和 @Service 实现
*/
@Component // 添加Spring注解
public class ShapeBeanFactory {

    @Autowired
    private Map<String, Shape> shapeMap;

    public Shape getShape(String shapeType) {
        Shape bean1 = shapeMap.get(shapeType);
        System.out.println(bean1);
        return bean1;
    }

}

   通过对比两种实现方案可以发现,使用Spring注解的方式更加简洁,避开了 if else 分支。 

客户端调用工厂类

 创建一个controller,命名为FactoryPatternDemo,并把两种实现方案封装为不同的私有方法。客户端调用工厂类,传入几何图形类型参数获取几何图形对象并调用该对象的draw方法:

 @GetMapping("/drawMyShape")
    public String drawMyShape(){
        shapeFactoryDraw();
        shapeBeanFactoryDraw();
        return "成功";
    }
    private void shapeFactoryDraw() {
        System.out.println("======= shapeFactory =======");

        //获取 Circle 的对象,并调用它的 draw 方法
        Shape shapeInterface1 = ShapeFactory.getShape("circle");
        //调用 Circle 的 draw 方法
        shapeInterface1.draw();

        Shape shapeInterface2 = ShapeFactory.getShape("rectangle");
        shapeInterface2.draw();
    }

    @Autowired
private ShapeBeanFactory factory; // 使用注解注入

    private void shapeBeanFactoryDraw() {
        System.out.println("======= 实现二 shapeBeanFactory =======");
        Shape shapeInterface1 = factory.getShape("circle");
        shapeInterface1.draw();

        Shape shapeInterface2 = factory.getShape("square");
        shapeInterface2.draw();
    }

     客户端请求draw函数,控制台输出结果如下,说明对工厂类重构成功。

======= shapeFactory =======
Inside Circle::draw() method.
Inside Rectangle::draw() method.
======= 实现二 shapeBeanFactory =======
com.eg.wiener.service.impl.shape.Circle@3a0aa6f6
Inside Circle::draw() method.
com.eg.wiener.service.impl.shape.Square@6d48c8f9
Inside Square::draw() method.

优缺点和适用场景

优点

   工厂类是整个模式的关键。包含了必要的逻辑判断,根据外界给定的信息,决定究竟应该创建哪个具体类的对象。通过使用工厂类,外界可以从直接创建具体产品对象的尴尬局面摆脱出来,仅仅需要负责“消费”对象就可以了,而不必管这些对象究竟如何创建及如何组织的,只需要知道具体产品类所对应的参数即可,对于一些复杂的类名,通过简单工厂模式可以减少使用者的记忆量。明确了各自的职责和权利,有利于整个软件体系结构的优化。

   通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。

缺点

   由于工厂类集中了所有实例的创建逻辑,违反了高内聚责任分配原则,将全部创建逻辑集中到了一个工厂类中;它所能创建的类只能是事先考虑到的,如果需要添加新的类,则就需要改变工厂类了。

   当系统中的具体产品类不断增多时候,可能会出现要求工厂类根据不同条件创建不同实例的需求。这种对条件和具体产品类型的判断交错在一起,很难避免模块功能的蔓延,对系统的维护和扩展非常不利;

   违背开闭原则。对于上面两种简单工厂模式的实现方法,如果我们要添加新的 几何图形,势必要改动 ShapeFactory的代码,那这是不是违反开闭原则呢?实际上,如果不需要频繁地添加新的几何图形实现类,只是偶尔修改一下代码,稍微不符合开闭原则,但权衡扩展性和可读性,这样的代码实现在大多数情况下,也是可以接受的。这是第一种实现方案的瑕疵,但是,第二种实现方案就没有违背开闭原则,完全由Spring IOC容器管理Bean,彰显了Spring IOC容器的强大之处。

适用场景

   在以下场景适合使用简单工厂模式:

   工厂类负责创建的对象比较少:由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。

客户端只知道传入创建工厂类所需的参数,对于如何创建对象不关心:客户端既不需要关心创建细节,甚至连类名都不需要记住,只需要知道类型所对应的参数。 

Reference

https://www.runoob.com/design-pattern/factory-pattern.html

http://www.cnblogs.com/java-my-life/archive/2012/03/22/2412308.html

https://www.jianshu.com/p/5cb52d84bd6d

  

原文地址:https://www.cnblogs.com/east7/p/13412365.html