Java代码审计-设计模式-工厂模式

Java设计模式-工厂模式(Singleton Pattern)

目录

  • 什么是工厂模式
  • 三种工厂模式的实现
  • JavaSE中工厂模式的使用
  • Struts2工厂模式的应用

工厂模式完成了对象创建过程的封装,对外屏蔽实现过程

一、什么是工厂模式

1.1 什么是工厂模式?

工厂模式的出现是为了解决创建对象实例的问题,通常我们使用new关键字创建某个对象,但在特定的情况下,一个对象的创建需要一些列的步骤,依赖其它的模块对象,所以这类对象的创建**更像是一个过程**,而非单个动作。这与工厂的工作模式极为相似,工厂中制造一个汽车,比如要从全世界各地购买零部件,然后组装为一台汽车,而这个组装的过程,就是工厂模式的创建汽车的过程。

工厂模式利用了面向对象的封装特点,对外屏蔽实现过程。

二、3种工厂模式的实现

3.1 简单工厂模式(Simple Factory)

简单工厂模式完成了最原始、最简单的对象创建与业务逻辑的隔离,降低了耦合性,来看一个例子

package org.factory.simpleFactory;

/**
 * 车的抽象类
 */
abstract class Car{
    public Car(){
        this.build();
    }
    public abstract void build();
}
class BMW extends Car {
    public void build(){
        System.out.println("生产一台BMW");
    }
}

class Benz extends Car{
    public void build(){
        System.out.println("生产一台Benz");
    }
}

/**
 * 工厂类
 */
class CarFactory{
    /**
     * 创建汽车
     * @param type 汽车类型
     * @return 汽车实例
     */
    public Car createCar(String type){
        Car car = null;
        if (type == "bmw"){
            car = new BMW();
        } else if (type == "benz") {
            car = new Benz();
        }
        return car;
    }
}

public class Client {
    public static void main(String[] args) {
        CarFactory carFactory = new CarFactory();
        BMW bmw = (BMW) carFactory.createCar("bmw");
        Benz benz = (Benz) carFactory.createCar("benz");
    }
}

以上就是简单工厂模式的实现,有这么几个特点:

  1. 所有的对象都在一个工厂中创建
  2. 完成了对象创建功能和对象使用的隔离,封装了创建的过程
  3. 根据传递的参数即可创建不同对象,较为灵活

但很显然这种方式有很多问题

  1. 违反开闭原则,想要增加新对象,就要修改工厂类
  2. 扩展性差,if else太多,不够优雅,而且如果创建几百个类型的对象,工程类的维护会令人发指

但简单工厂模式也有自己的应用场景,例如在相对简单业务场景下,JDK中的Calendar类就使用了简单工厂模式。

3.2 工厂方法模式(Factory Method)

简单工厂的最大缺点在于工厂类不够灵活,工厂方法模式便将工厂进行抽象化,定义创建对象的接口,具体如何实现对象的创建,交给具体的工厂类。那么此时想要新增产品,只需要扩展实现抽象工厂类即可,符合开闭原则。

当使用者想要创建某个对象时,只需要找到对应的工厂,即可创建。工厂方法模式需要以下角色

  • 抽象工厂 AbstractFactory: 定义创建对象的接口,具体由具体工厂实现
  • 具体工厂 Factory:实现抽象工厂的接口,实现创建对象的过程
  • 抽象产品 AbstractProduct:所有具体产品的父类,可以满足里氏替换的设计原则
  • 具体产品 Product:工厂所建实例

下面为实例代码

package org.factory.factoryMethod;

/**
 * 车的抽象类
 */
abstract class Car{
    public Car(){
        this.build();
    }
    public abstract void build();
}
class BMW extends Car {
    public void build(){
        System.out.println("生产一台BMW");
    }
}

class Benz extends Car {
    public void build(){
        System.out.println("生产一台Benz");
    }
}
/**
 * 工厂的抽象类
 */
abstract class Factory{
    public abstract Car create();
}

class BMWFactory extends Factory{

    public BMW create() {
        return new BMW();
    }
}

class BenzFactory extends Factory{

    public Benz create() {
        return new Benz();
    }
}

public class Client {
    public static void main(String[] args) {
        BenzFactory benzFactory = new BenzFactory();
        benzFactory.create();

        BMWFactory bmwFactory = new BMWFactory();
        bmwFactory.create();

    }
}

总结下上面的代码UML图

image-20230419160316233

方法工厂有很多优点

  1. 扩展性强:方法工厂模式限定了某个工厂只能创建某个对象,只要知道工厂名字就可以知道他创建的对象是什么,而且扩展性极强,如果增加五菱汽车,只需要增加一个五菱工厂类和五菱汽车对象即可。
  2. 屏蔽产品:产品的变化与调用者无关,这有点像“中间层理念”,例如Java的JDBC连接,我们只需要更改驱动名称即可,其它的不需要关心,十分强大!
  3. 解耦:这个特性是所有的设计模式都有的优点,符合依赖倒置原则,值依赖产品类的抽象;也符合里氏替换原则,可以使用子类替换父类,符合迪米特法则,我只需要操作Factory,不操作触产品类方法。

方法工厂一般在抽象类的实现类会不断变化时会使用,例如信息通知功能,可以短信、可以是邮件、可以是抖音,在未来可能是其它的方式

3.3 抽象工厂模式(Abstract Factory)

抽象模式是最难理解的模式,首先,观察下方法工厂模式,有没有发现方法工厂模式中只用到了一种“产品”?需要Create,直接就创建了一个对象,但我们回想下工厂制造汽车的场景,更多的是在“组装”。例如创建一个汽车需要:轮胎、天窗、底盘等,而这三个都是单独的对象。

这就很明确了:我生成一个宝马需要天窗、底盘等产品,接下来通过代码实现上面的需求(也可以先看代码后面的UML图,再回头看代码,会更高效)

package org.factory.abstractFactory;

/**
 * 轮胎的抽象类
 */
abstract class Tyre{
    public Tyre(){
        this.getName();
        this.getColor();
    }
    public abstract void getColor();
    public abstract void getName();
}

class TyreA extends Tyre {
    public void getColor() {
        System.out.println("轮胎A的颜色是黑色");
    }
    public void getName() {
        System.out.println("轮胎A的名字是轮胎A");
    }
}
class TyreB extends Tyre {
    public void getColor() {
        System.out.println("轮胎B的颜色是红色");
    }
    public void getName() {
        System.out.println("轮胎B的名字是轮胎B");
    }
}
/**
 * 天窗的抽象类
 */
abstract class Window{
    public Window(){
        this.open();
        this.close();
    }
    public abstract void open();
    public abstract void close();
}

class WindowA extends Window {

    public void open() {
        System.out.println("WindowA会开窗");
    }

    public void close() {
        System.out.println("WindowA会关窗");
    }
}

class WindowB extends Window {

    public void open() {
        System.out.println("WindowB会开窗");
    }

    public void close() {
        System.out.println("WindowB会关窗");
    }
}
/**
 * 汽车的抽象类
 * 每个汽车都需要安装Window和Tyre
 */
abstract class Car {
    public abstract void setWindow(Window window);
    public abstract void setTyre(Tyre tyre);
}


class BMW extends Car {

    private Window window;
    private Tyre tyre;

    public void setWindow(Window window) {
        this.window = window;
    }

    public void setTyre(Tyre tyre) {
        this.tyre = tyre;
    }
}

class Benz extends Car {
    private Window window;
    private Tyre tyre;

    public void setWindow(Window window) {
        this.window = window;
    }

    public void setTyre(Tyre tyre) {
        this.tyre = tyre;
    }
}
/**
 * 工厂的抽象类
 */
interface Factory{
    public Window createWindow();
    public Tyre createTyre();
    public Car create();
}

class BMWFactory implements Factory {
    public BMW create() {
        System.out.println("开始生产BMW");
        BMW bmw = new BMW();
        bmw.setTyre(createTyre());
        bmw.setWindow(createWindow());
        return bmw;
    }

    public Window createWindow() {
        return new WindowB();
    }

    public Tyre createTyre() {
        return new TyreA();
    }
}

class BenzFactory implements Factory {

    public Benz create() {
        System.out.println("开始生产Benz");
        Benz benz = new Benz();
        benz.setTyre(createTyre());
        benz.setWindow(createWindow());
        return benz;
    }

    public Window createWindow() {
        return new WindowB();
    }

    public Tyre createTyre() {
        return new TyreB();
    }
}

public class Client {
    public static void main(String[] args) {
        BMWFactory bmwFactory = new BMWFactory();
        bmwFactory.create();
        BenzFactory benzFactory = new BenzFactory();
        benzFactory.create();
    }
}
// 运行结果如下
开始生产BMW
轮胎A的名字是轮胎A
轮胎A的颜色是黑色
WindowB会开窗
WindowB会关窗
开始生产Benz
轮胎B的名字是轮胎B
轮胎B的颜色是红色
WindowB会开窗
WindowB会关窗

image-20230218105133179

代码很多,表达出了抽象工厂的使用方法,抽象工厂有很多优点

  1. 封装性:Factory屏蔽了技术细节,使得高层模块只关心抽象和接口即可,工厂负责创建实例
  2. 产品族内的约束为非公开状态:高层模块只知道工厂会返回我一个宝马,但宝马内部用的什么材料我不清楚,但你给我一个宝马实例即可,具体的产品族内的约束是在工厂内实现的

缺点也很明显,产品(宝马)的扩展很困难,如果在宝马中增加后备箱,想一下需要改哪些地方?

  1. 首先Factory接口、BMWFactory肯定要修改,因为要增加createHouBX方法
  2. Car接口以及实现类都要增加setHouBX的方法
  3. 增加HouBX类和实现类(这是没什么问题的)

所以主要问题是要修改Factory/Car接口,这是违反开闭原则的。所有实现Factory/Car的类都要修改、测试。

总结下:

  1. 在产品中增加零部件是困难的(例如宝马增加后备箱)
  2. 增加产品的种类是容易得(增加五菱汽车)
  3. 抽象模式比作数据库连接切换的话,可以理解为虽然便于两数据库之间的切换,但是不便于增加需求功能。

对比下三种工厂模式

  • 简单工厂:只有唯一工厂(简单工厂),一个产品接口/抽象类,根据简单工厂中的静态方法来创建具体产品对象。适用于产品较少,几乎不扩展的情景
  • 工厂方法:有多个工厂(抽象工厂+多个具体工厂),一个产品接口/抽象类,根据继承抽象工厂中的方法来多态创建具体产品对象。适用于一个类型的多个产品
  • 抽象方法:有多个工厂(抽象工厂+多个具体工厂),多个产品接口/抽象类,对产品子类进行分组,根据继承抽象工厂中的方法多态创建同组的不同具体产品对象。适用于多个类型的多个产品,例如BenzFactory在创建汽车时,可以使用WindowA也可以使用WindowB。

三、JavaSE中工厂模式的使用

首先列举下使用工厂模式的类(看到名为newInstance的方法,大概率就是工厂模式)

  1. java.util.Calendar 类 getInstance 方法使用了简单工厂
  2. java.lang.Class 类的 newInstance 方法
  3. java.lang.Class 类的 forName 方法
  4. java.lang.reflect.Array 类的 newInstance 方法
  5. java.lang.reflect.Constructor 类的 newInstance 方法
  6. java.lang.reflect.Proxy 类的 newProxyInstance 方法
  7. JDK 中8 种基本类型的包装类 Integer、Long、Short、Character、Byte、Float、Double、Boolean 的 valueOf 方法使用了简单工厂,可以根据参数创建不同的对象
java.util.Calendar 类 getInstance 方法使用了简单工厂

类中有非常多的getInstance方法的重载,用于创建实例

getInstance方法中会调用createCaledar方法去创建不同的日期计算的类

image-20230219211024895

java.lang.Class 类的 newInstance 方法

Class类调用构造器创建对象实例

image-20230219211357756

java.lang.Class 类的 forName 方法

forName方法会调用forName0方法,该方法是一个被native修饰的方法。

native是与C++联合开发的时候用的!使用native关键字说明这个方法是原生函数,也就是这个方法是用C/C++语言实现的,并且被编译成了DLL,由java去调用。

native 是用做java 和其他语言(如c++)进行协作时使用的,也就是native 后的函数的实现不是用java写的。

既然都不是java,那就别管它的源代码了,我们只需要知道这个方法已经被实现即可。

native的意思就是通知操作系统, 这个函数你必须给我实现,因为我要使用。 所以native关键字的函数都是操作系统实现的, java只能调用。

image-20230219211635718

java.lang.reflect.Array 类的 newInstance 方法

java.lang.reflect包下除了提供Method(方法)、Constructor(构造器)、Filed(成员变量)这三个类,这三个类使我们在学习反射时常用的类,除此之外还提供了Array类,Array对象可以代表所有的数组,可通过Array类来动态创建数组,newInstance用于创建指定元素类型、指定维度的数组。这里的dimensions是可变个数的。

该方法是一个native方法

image-20230219213133827

8种基本类型的valueOf方法

通过valueOf方法,new出实例,其它的基本类型不在一一讲解

image-20230219213256086

java.lang.reflect.Constructor 类的 newInstance 方法

通过构造器创建对象实例

image-20230219213509722

java.lang.reflect.Proxy 类的 newProxyInstance 方法

通过构造器创建对象实例

image-20230219213720758

四、Struts2工厂模式的应用

4.1 InternalFactory类

在Struts2的依赖注入中使用了工厂模式,关于Struts2的容器加载机制,可以浏览《Mark链接-Struts2依赖注入实现原理》

Struts2在依赖注入时定义了InternalFactory接口

// 内部工厂接口
interface InternalFactory<T> extends Serializable {

  /**
   * Creates an object to be injected.
   *
   * @param context of this injection
   * @return instance to be injected
   */
  T create(InternalContext context);
}

在ContainerBuilder类将InternalFactory加入factories列表时,生成内部类实现create方法,create方法中定义了一个对象创建的过程,一个比较简单的工厂模式。

image-20230302111429776

4.2 ObjectFactory类

该类无父类接口,就是一个实现类,完成对Struts2的Action、Bean、Result、Interceptor对象的创建,也是一个比较简单的工厂模式

image-20230302112143999

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MarginSelf

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值