Bootstrap

【Java设计模式】- 工厂方法模式

写在前面

Hello,我是奔跑的蜗牛呀,很高兴和大家分享自己的学习内容😳,若文章内容有误,请大家多多指正!!

前言

工厂方法模式是简单工厂模式的延伸,在继承了简单工厂模式优点的同时,通过引入抽象工厂类弥补了简单工厂模式中增加新的具体产品对象需要对已有系统进行改变的缺陷。

工厂方法模式中,利用面向对象的多态性和里氏代换原则,在程序运行时子类对象将覆盖父类对象,从而使得系统更容易扩展。

工厂方法模式

一. 概述

定义一个用于创建对象的接口,但是让其子类决定将哪一个类实例化,让一个类的实例化延迟到其子类。

工厂方法模式中定义了一个抽象工厂类,并且定义了创建产品对象的公共接口,返回抽象产品对象,抽象工厂子类实现了抽象接口,返回具体产品对象。

二. 结构

工厂方法模式包含四个部分:抽象工厂类具体工厂类抽象产品类具体产品类

工厂方法模式结构图

抽象工厂类

抽象工厂类中声明了公共工厂方法,返回抽象产品对象。

/**
 * 抽象工厂类
 */
public interface IFactory {

    IProduct createPro(String params);
    
}

具体工厂类

是抽象工厂类的子类,实现了公共工厂方法,并返回一个具体产品类的实例对象。通常情况下,与具体产品类一一对应。

/**
 * 具体工厂类
 */
public class FacImplA implements IFactory {

    // 创建产品A
    @Override
    public IProduct createPro(String params) {
        ProImplA pro = new ProImplA();
        // 执行相关业务逻辑
        return pro;
    }
    
}

抽象产品类

工厂方法模式所创建对象的公共父类,定义了一组具有子类共性的公共方法,和子类独的抽象方法。

/**
 * 抽象产品类
 */
public interface IProduct {

    // 公共方法
    default void methodShared(){
        // 业务逻辑
    }

    // 不同方法
    void methodDiff(String params);
    
}

具体产品类

是抽象产品类的子类,实现了抽象产品对象中定义的抽象方法,通常情况下,与具体工厂类一一对应。

/**
 * 具体产品类
 */
public class ProImplA implements IProduct {

    // 不同执行逻辑的方法
    @Override
    public void methodDiff(String params) {
        // 不同处理逻辑
    }
    
}

三. 案例

案例描述
设计一个程序用于读取各种不同类型的图片格式,针对每一种图片格式都设计一个图片读取器。例如:GIF图片读取器用于读取GIF格式的图片,JPG图片读取器用于读取JPG格式的图片。

分析可得

  1. 图片格式可以提取抽象提取为抽象产品类,包含一个图片读取的公共抽象方法。
  2. GIF、JPG格式的图片可以做为具体产品类,实现父类的图片读取方法。
  3. 采用工厂模式实现,当出现第三种格式图片时,可以在不修改原代码逻辑的情况下快速扩展,需要定义一个抽象工厂类,定义一个具体工厂类。

图片读取器结构图

代码实现

图片读取器工厂类

/**
 * 图片读取器工厂类
 */
public interface IReaderFactory {

    /**
     * 获取图片读取器
     *
     * @param args 参数集
     * @return IImageReader
     */
    IImageReader getIReader(String args);
}

GIF图片读取器工厂类

/**
 * GIF图片读取器工厂类
 */
public class GifRFactory implements IReaderFactory {

    Logger logger = LoggerFactory.getLogger(GifRFactory.class);

    /**
     * 获取GIF图片读取器
     *
     * @param args 参数集
     * @return GifIReader
     */
    @Override
    public IImageReader getIReader(String args) {

        GifIReader gifIReader = new GifIReader();

        logger.info("创建GIF图片读取器成功!");

        return gifIReader;
    }
}

JPG图片读取器工厂类

/**
 * JPG图片读取器工厂类
 */
public class JpgRFactory implements IReaderFactory {

    Logger logger = LoggerFactory.getLogger(JpgRFactory.class);

    /**
     * 获取JPG图片读取器
     *
     * @param args 参数集
     * @return JpgIReader
     */
    @Override
    public IImageReader getIReader(String args) {

        JpgIReader jpgIReader = new JpgIReader();

        logger.info("创建JPG图片读取器成功!");

        return jpgIReader;
    }
}

图片读取器接口类

/**
 * 图片读取器接口
 */
public interface IImageReader {

    /**
     * 带参数读取图片
     *
     * @param args 参数集
     */
    void reader(String args);
}

GIF图片读取器

/**
 * GIF图片读取器
 */
public class GifIReader implements IImageReader {

    Logger logger = LoggerFactory.getLogger(GifIReader.class);

    /**
     * 带参数读取GIF格式图片
     *
     * @param args 参数集
     */
    @Override
    public void reader(String args) {
        logger.info("读取GIF格式图片!");
    }
}

JPG图片读取器

/**
 * JPG图片读取器
 */
public class JpgIReader implements IImageReader {

    Logger logger = LoggerFactory.getLogger(JpgIReader.class);

    /**
     * 带参数读取JPG格式图片
     *
     * @param args 参数集
     */
    @Override
    public void reader(String args) {
        logger.info("读取JPG格式图片!");
    }
}

客户端调用

public class ApiTest {

    Logger logger = LoggerFactory.getLogger(ApiTest.class);

    @Test
    public void factoryTest(){

        IReaderFactory iReaderFactory;
        IImageReader imageReader;
        // 可以配置XML文件,通过读取配置文件,获取需要的图片读取器,使得程序更加符合开闭原则。
        logger.info("读取配置文件获取需要的图片格式读取器工厂类");
        iReaderFactory = new GifRFactory();

        logger.info("获取GIF图片读取器");
        imageReader = iReaderFactory.getIReader("args");

        logger.info("调用GIF图片读取图片");
        imageReader.reader("args");
        
    }
}

运行结果:
-读取配置文件获取需要的图片格式读取器工厂类
-获取GIF图片读取器
-创建GIF图片读取器成功!
-调用GIF图片读取图片
-读取GIF格式图片!

总结

优点

  1. 能够让工厂自主的确定创建何种产品对象,而具体的创建细节进行隐藏封装。
  2. 在加入新的产品时无须修改抽象工厂和抽象产品提供的接口,只需要增加一个具体工厂和一个具体产品即可,极大的提高了程序的扩展性。

缺点

每个具体产品都需要一个与之对应的具体工厂类,使得系统中类的个数成对增加,增加了系统的复杂度,有更多的类需要编辑和运行,带来了一些额外的开销。

适用环境

客户端不需要知道需要的具体对象的类名,只需要知道对应工厂类即可,可以将具体工厂类的类名存储在配置文件或数据库中。

;