工厂方法模式(Factory Method Pattern)

工厂方法模式概述

工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。
“工厂方法模式”是对简单工厂模式的进一步抽象化,其好处是可以使系统在不修改原来代码的情况下引进新的产品,即满足开闭原则

介绍

目的: 定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
主要解决: 主要解决接口选择的问题。
何时使用: 我们明确地计划不同条件下创建不同实例时。
如何解决: 让其子类实现工厂接口,返回的也是一个抽象的产品。
关键代码: 创建过程在其子类执行。
优点:

  1. 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程
  2. 无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其他的具体工厂和具体产品,而只要添加一个具体工厂和具体产品就可以了。这样,系统的可扩展性也就变得非常好,完全符合“开闭原则”。
  3. 典型的解耦框架。高层模块只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则。

缺点:

  1. 在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销。
  2. 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度。

使用场景:

  1. 一个类不知道它所需要的对象的类:在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建;客户端需要知道创建具体产品的工厂类
  2. 一个类通过其子类来指定创建哪个对象:在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。
  3. 将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。

应用实例:

  1. 我们需要买一辆汽车,只需要向厂家说需要那种型号的车,然后直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现。
  2. 日志记录器,可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。
  3. 数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。
  4. 设计一个连接服务器的框架,需要三个协议,“POP3”、“IMAP”、“HTTP”,可以把这三个作为产品类,共同实现一个接口。
  5. JDK类库中广泛使用了简单工厂模式,如工具类java.text.DateFormat,它用于格式化一个本地日期或者时间。
public final static DateFormat getDateInstance();
public final static DateFormat getDateInstance(int style);
public final static DateFormat getDateInstance(int style,Locale locale);

模式扩展:

  1. 使用多个工厂方法:在抽象工厂角色中可以定义多个工厂方法,从而使具体工厂角色实现这些不同的工厂方法,这些方法可以包含不同的业务逻辑,以满足对不同的产品对象的需求。
  2. 产品对象的重复使用:工厂对象将已经创建过的产品保存到一个集合(如数组、List等)中,然后根据客户对产品的请求,对集合进行查询。如果有满足要求的产品对象,就直接将该产品返回客户端;如果集合中没有这样的产品对象,那么就创建一个新的满足要求的产品对象,然后将这个对象在增加到集合中,再返回给客户端。
  3. 多态性的丧失和模式的退化:如果工厂仅仅返回一个具体产品对象,便违背了工厂方法的用意,发生退化,此时就不再是工厂方法模式了。一般来说,工厂对象应当有一个抽象的父类型,如果工厂等级结构中只有一个具体工厂类的话,抽象工厂就可以省略,也将发生了退化。当只有一个具体工厂,在具体工厂中可以创建所有的产品对象,并且工厂方法设计为静态方法时,工厂方法模式就退化成简单工厂模式。

注意事项: 作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。

结构

四种角色

抽象工厂(Abstract Factory)
提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法 newProduct() 来创建产品。
具体工厂(ConcreteFactory)
主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
抽象产品(Product)
定义了产品的规范,描述了产品的主要特性和功能。
具体产品(ConcreteProduct)
实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。

结构图


案例实现

案例描述

设计一个可以创建不同几何图形(Shape),如Circle,Rectangle,Triangle等绘图工具类,每个几何图形均具有绘制Draw()和擦除Erase()两个方法。

案例类图

代码实现

抽象产品角色

Shape 图形接口 抽象产品类

public interface Shape {

    /**
     * 几何图形绘制
     */
    void draw();

    /**
     * 几何图形擦除
     */
    void erase();
}

具体产品角色

圆形产品类(Circle)

//圆形产品类(Circle)
public class Circle implements Shape {

    /**
     * 生成图形计数
     */
    private static int count;

    @Override
    public void draw() {
        //半径
        int radius = new Random().nextInt(100) + 1;
        System.out.println("Circle.draw");
        System.out.println("生成了第" + ++count + "个圆形,这个圆形的半径为" + radius);
        System.out.println(">>>>>>>>>>>>>>>>>>>>Circle.draw<<<<<<<<<<<<<<<<<<<<");
    }

    @Override
    public void erase() {
        System.out.println("Circle.erase");
        System.out.println("擦除第" + count-- + "个生成的圆形");
        System.out.println(">>>>>>>>>>>>>>>>>>>>Circle.erase<<<<<<<<<<<<<<<<<<<<");
    }
}

矩形产品类(Rectangle)

//矩形类
public class Rectangle implements Shape {

    /**
     * 生成图形计数
     */
    private static int count;

    @Override
    public void draw() {
        //长度
        int length = new Random().nextInt(100) + 1;
        //宽度
        int width = new Random().nextInt(100) + 1;
        System.out.println("Rectangle.draw");
        System.out.println("生成了第" + ++count + "个矩形,这个矩形的长为" + length + ",宽度为" + width);
        System.out.println(">>>>>>>>>>>>>>>>>>>>Rectangle.draw<<<<<<<<<<<<<<<<<<<<");
    }

    @Override
    public void erase() {
        System.out.println("Rectangle.erase");
        System.out.println("擦除了第" + count-- + "个矩形");
        System.out.println(">>>>>>>>>>>>>>>>>>>>Rectangle.erase<<<<<<<<<<<<<<<<<<<<");
    }
}

三角形产品类(Triangle)

//三角形产品类(Triangle)
public class Triangle implements Shape {

    /**
     * 生成图形计数
     */
    private static int count;

    @Override
    public void draw() {
        //三角形三边长度
        int lengthA = new Random().nextInt(100) + 1;
        int lengthB = new Random().nextInt(100) + 1;
        int lengthC = new Random().nextInt(100) + 1;
        System.out.println("Triangle.draw");
        System.out.println("生成了第" + ++count + "个三角形,这个三角形的三边长分别为为" + lengthA + "," + lengthB + "," + lengthC);
        System.out.println(">>>>>>>>>>>>>>>>>>>>Triangle.draw<<<<<<<<<<<<<<<<<<<<");
    }

    @Override
    public void erase() {
        System.out.println("Triangle.erase");
        System.out.println("擦除了第" + count-- + "个三角形");
        System.out.println(">>>>>>>>>>>>>>>>>>>>Triangle.erase<<<<<<<<<<<<<<<<<<<<");
    }
}

抽象工厂角色

AbstractFactory 图形抽象工厂

public interface AbstractFactory {
    /**
     * 需要创建的产品
     * @return shape
     */
    public Shape product();
}

具体工厂角色

圆形类具体工厂(CircleFactory)

public class CircleFactory implements AbstractFactory {
    /**
     * 需要创建的产品
     *
     * @return shape
     */
    @Override
    public Shape product() {
        return new Circle();
    }
}

矩形类具体工厂(RectangleFactory)

public class RectangleFactory  implements AbstractFactory {
    /**
     * 需要创建的产品
     *
     * @return shape
     */
    @Override
    public Shape product() {
        return new Rectangle();
    }
}

三角形类具体工厂(TriangleFactory)

public class TriangleFactory implements AbstractFactory {
    /**
     * 需要创建的产品
     *
     * @return shape
     */
    @Override
    public Shape product() {
        return new Triangle();
    }
}

demo调用

public class FactoryMethodPattern {

    public static void main(String[] args) {
        Shape product;
        product = new CircleFactory().product();
        product.draw();
        product.draw();
        product.erase();
        product = new RectangleFactory().product();
        product.draw();
        product.erase();
        product = new TriangleFactory().product();
        product.draw();
        product.erase();
    }
}

关于springboot

工厂类根据传入的参数类型,动态决定创建哪一个产品(实现类都继承同一个父类或接口)。在Spring中,我们只要将要用到的支付方式注入到容器中(@Service、@Component等注解),我们便可以根据bean的名字去容器中获取,因为是根据bean的名字从容器中获取对象,所以此类用法更像是简单工厂模式。

创建接口和实现类

抽象产品接口

public interface Shape {

    /**
     * 几何图形绘制
     */
    void draw();

    /**
     * 几何图形擦除
     */
    void erase();
}

具体产品实现类

//圆形类
@Service
public class Circle implements Shape {

    /**
     * 生成图形计数
     */
    private static int count;

    @Override
    public void draw() {
        //半径
        int radius = new Random().nextInt(100) + 1;
        System.out.println("Circle.draw");
        System.out.println("生成了第" + ++count + "个圆形,这个圆形的半径为" + radius);
        System.out.println(">>>>>>>>>>>>>>>>>>>>Circle.draw<<<<<<<<<<<<<<<<<<<<");
    }

    @Override
    public void erase() {
        System.out.println("Circle.erase");
        System.out.println("擦除第" + count-- + "个生成的圆形");
        System.out.println(">>>>>>>>>>>>>>>>>>>>Circle.erase<<<<<<<<<<<<<<<<<<<<");
    }
}

//矩形类
@Service
public class Rectangle implements Shape {

    /**
     * 生成图形计数
     */
    private static int count;

    @Override
    public void draw() {
        //长度
        int length = new Random().nextInt(100) + 1;
        //宽度
        int width = new Random().nextInt(100) + 1;
        System.out.println("Rectangle.draw");
        System.out.println("生成了第" + ++count + "个矩形,这个矩形的长为" + length + ",宽度为" + width);
        System.out.println(">>>>>>>>>>>>>>>>>>>>Rectangle.draw<<<<<<<<<<<<<<<<<<<<");
    }

    @Override
    public void erase() {
        System.out.println("Rectangle.erase");
        System.out.println("擦除了第" + count-- + "个矩形");
        System.out.println(">>>>>>>>>>>>>>>>>>>>Rectangle.erase<<<<<<<<<<<<<<<<<<<<");
    }
}

//三角形类
@Service
public class Triangle implements Shape {

    /**
     * 生成图形计数
     */
    private static int count;

    @Override
    public void draw() {
        //三角形三边长度
        int lengthA = new Random().nextInt(100) + 1;
        int lengthB = new Random().nextInt(100) + 1;
        int lengthC = new Random().nextInt(100) + 1;
        System.out.println("Triangle.draw");
        System.out.println("生成了第" + ++count + "个三角形,这个三角形的三边长分别为为" + lengthA + "," + lengthB + "," + lengthC);
        System.out.println(">>>>>>>>>>>>>>>>>>>>Triangle.draw<<<<<<<<<<<<<<<<<<<<");
    }

    @Override
    public void erase() {
        System.out.println("Triangle.erase");
        System.out.println("擦除了第" + count-- + "个三角形");
        System.out.println(">>>>>>>>>>>>>>>>>>>>Triangle.erase<<<<<<<<<<<<<<<<<<<<");
    }
}

bean获取对象

启动类获取演示

@SpringBootApplication
public class FactoryMethodPatternApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(FactoryMethodPatternApplication.class, args);
        new FactoryMethodPatternApplication().getBean(context);
    }

    /**
     * 第一种,直接在容器中,根据bean名字获取需要的对象
     *
     * <p>使用@Service、@Component等注解时,如果没有指定value</p>
     * <p>则注入的bean默认为首字母小写</p>
     * <p>一般情况下,不常采用context获取bean</p>
     *
     * @param context bean容器
     */
    private void getBean(ConfigurableApplicationContext context) {
        Shape circle = (Circle)context.getBean("circle");
        Shape rectangle = (Rectangle)context.getBean("rectangle");
        Shape triangle = (Triangle)context.getBean("triangle");
        circle.draw();
        rectangle.draw();
        triangle.draw();
        circle.draw();
        circle.erase();
        rectangle.erase();
        triangle.erase();
    }
}

实际开发最常使用的依然是创建工厂类

工厂类实现演示

创建工厂类:

@Component
public class ShapeFactory {

    @Resource
    private Map<String,Shape> shapeMap;
    /**
     * 通过@Autowired或@Resource注解将bean注入map,由变量map替换传统工厂类,实现简单工厂模式
     *
     * @param type String 需要新建的产品名
     * @return Shape 创建成功的产品
     */
    public Shape getShape(String type) {
        return shapeMap.get(type);
    }
}

springboot测试类:

@SpringBootTest
class FactoryMethodPatternApplicationTests {

    @Resource
    private ShapeFactory shapeFactory;
    
    @Test
    void contextLoads() {
        Shape circle = shapeFactory.getShape("circle");
        Shape rectangle = shapeFactory.getShape("rectangle");
        Shape triangle = shapeFactory.getShape("triangle");
        circle.draw();
        rectangle.draw();
        triangle.draw();
        circle.draw();
        circle.erase();
        rectangle.erase();
        triangle.erase();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值