阅读本文建议先看下目录,把握整体脉络。最好是使用过工厂模式,即使没有用过该设计模式,文章中的示例建议一定要自己动手敲一敲,便于更加全面深入地了解工厂模式,有异议可以评论或私聊哦

1、简单(静态)工厂模式

1.1 含义

  • 简单工厂模式又称静态方式模式(因为工厂类定义了一个静态方法)
  • 现实生活中工厂是负责生产产品的;同样在设计模式中,简单工厂模式我们可以理解为负责生产对象的一个类,称为”工厂类“

1.2 解决的问题

将”类实例化的操作“与”使用对象的操作“分开,让使用者不需要知道具体参数就可以实例化出所需要的产品类,从而避免了在客户端代码中显式指定,实现了解耦,即使用者可以直接消费产品而不需要知道其生产的细节

1.3 模式原理

1.3.1 模式组成

模式表格

1.3.2 UML类图

在这里插入图片描述

1.3.3 使用步骤

  • 创建抽象产品类,定义具体产品的公共接口
  • 创建具体产品类(继承抽象产品类),定义生产的具体产品
  • 创建工厂类,通过创建静态方法根据传入不同参数从而创建不同产品类的实例
  • 外界调用工厂类的静态方法传入不同参数具体产品类

1.4 实例讲解

1.4.1 需求概况

  • 背景:小成有一个塑料生产厂,用来做塑料加工生意
  • 目的:最近推出了3个产品,小成希望使用简单工厂模式

1.4.2 使用步骤

创建抽象产品类,定义具体产品类的公共接口

package com.zizhou.factory.simple;

/**
 * 创建抽象产品类,定义具体产品类的公共接口
 */
public abstract class Product {
    public abstract void show();
}

创建具体产品类,继承抽象产品类,定义生产的具体产品

// 创建具体产品类,继承抽象产品类,定义生产的具体产品
package com.zizhou.factory.simple;

public class ProductA extends Product{
    @Override
    public void show() {
        System.out.println("ProductA created");
    }
}

package com.zizhou.factory.simple;

public class ProductB extends Product{
    @Override
    public void show() {
        System.out.println("ProductB created");
    }
}

package com.zizhou.factory.simple;

public class ProductC extends Product{
    @Override
    public void show() {
        System.out.println("ProductC created");
    }
}

创建工厂类,通过创建静态方法根据传入的不同参数生成不同具体产品类的实例

package com.zizhou.factory.simple;

/**
 * 创建工厂类,通过创建静态方法根据传入的不同参数生成不同具体产品类的实例
 */
public class Factory {

    public static Product getProduct(String type) {
        switch (type) {
            case "A":
                return new ProductA();
            case "B":
                return new ProductB();
            case "C":
                return new ProductC();
            default:
                return null;
        }
    }
}

外界调用工厂类的静态方法,传入不同参数生成不同具体产品类的实例

package com.zizhou.factory.simple;

/**
 * 外界调用工厂类的静态方法,传入不同参数生成不同具体产品类的实例
 */
public class SimpleFactoryTest {
    public static void main(String[] args) {
        try {
            Factory.getProduct("A").show();
        } catch (Exception e) {
            System.out.println("没有这类产品");
        }
        try {
            Factory.getProduct("B").show();
        } catch (Exception e) {
            System.out.println("没有这类产品");
        }
        try {
            Factory.getProduct("C").show();
        } catch (Exception e) {
            System.out.println("没有这类产品");
        }
        try {
            Factory.getProduct("D").show();
        } catch (Exception e) {
            System.out.println("没有这类产品");
        }
    }
}

1.5 优点

  • 将实例的创建与使用工作分开,使用者不需要关心产品的创建细节,实现了解耦
  • 把初始化实例时的工作放到工厂里进行,使代码更容易维护,更符合面向对象原则&面向接口编程,而不是面向实现编程

1.6 缺点

  • 工厂类中集中了所有实例的创建逻辑,一旦这个工厂不能工作,整个系统都会受到影响
  • 违背了开放-关闭原则,一旦添加新产品就不得不修改工厂类的逻辑,这样会造成工厂逻辑过于复杂
  • 简单工厂模式使用了静态工厂方法,静态方法不能被继承和重写,会造成工厂角色无法形成基于继承的等级结构

1.7 使用步骤

  • 调用方如果只知道传入工厂类的参数,对于如何创建对象的逻辑并不关心时
  • 当工厂类负责创建的对象比较少时

2、工厂方法模式

2.1 介绍

2.1.1 含义

工厂方法模式,又称工厂模式、多态工厂模式或虚拟构造器模式,通过定义工厂父类负责定义创建对象的公共接口,而子类负责生成具体的对象

2.1.2 主要作用

将类实例化(具体产品的创建)的工作延迟给工厂类的子类(具体工厂)中完成,由子类来决定应该实例化(创建)哪一个类

2.1.3 解决的问题

即简单工程模式中的缺点,把具体产品的创建延迟到子类中,此时工厂类不再负责所有产品的创建,而只是给出具体工厂需要实现的接口,这样工厂方式在添加新的产品时不需要修改工厂类的逻辑,而是添加新的工厂子类,符合开放关闭原则,克服了简单工厂模式中的缺点

2.2 模式原理

2.2.1 UML类图

在这里插入图片描述

2.2.2 模式组成

在这里插入图片描述

2.2.3 使用步骤

  1. 创建抽象工厂类,定义具体工厂的公共接口
  2. 创建抽象产品类,定义具体产品的公共接口
  3. 创建具体产品类,继承抽象产品类,定义生产的具体产品
  4. 创建具体工厂类,继承抽象工厂类,定义创建具体产品实例的方法
  5. 外界通过调用具体工厂类的方法,从而创建不同具体产品类的实例

2.3 实例讲解

2.3.1 实例概况

  • 背景:小成有一间塑料加工厂(仅生产A类产品);随着客户需求的变化,客户需要生产B类产品;
  • 冲突:改变原有塑料加工厂的配置和变化非常困难,假设下一次客户需要再发生变化,再次改变将增大非常大的成本;
  • 解决方案:小成决定置办塑料分厂B来生产B类产品;

2.3.2 使用步骤

创建抽象工厂类,定义具体工厂的公共接口

package com.zizhou.factory.pattern;

/**
 * 创建抽象工厂类,定义具体工厂的公共接口
 */
public abstract class Factory {
    public abstract Product getProduct();
}

创建抽象产品类,定义具体产品的公共接口

package com.zizhou.factory.pattern;

/**
 * 创建抽象产品类,定义具体产品的公共接口
 */
public abstract class Product {
    public abstract void show();
}

创建具体产品类,继承抽象产品类,描述生产的具体产品

package com.zizhou.factory.pattern;

/**
 * 创建具体产品类,继承抽象产品类,描述生产的具体产品
 */
public class ProductA extends Product{
    @Override
    public void show() {
        System.out.println("ProductA created");
    }
}

package com.zizhou.factory.pattern;

public class ProductB extends Product{
    @Override
    public void show() {
        System.out.println("ProductB created");
    }
}

创建具体工厂类, 继承抽象工厂类, 定义创建具体产品实例的方法

package com.zizhou.factory.pattern;

/**
 * 创建具体工厂类, 继承抽象工厂类, 定义创建具体产品实例的方法
 */
public class FactoryA extends Factory{
    @Override
    public Product getProduct() {
        return new ProductA();
    }
}

package com.zizhou.factory.pattern;

public class FactoryB extends Factory{
    @Override
    public Product getProduct() {
        return new ProductB();
    }
}

外界通过调用具体工厂类的方法,从而创建不同具体产品类的实例

package com.zizhou.factory.pattern;

/**
 * 外界通过调用具体工厂类的方法,从而创建不同具体产品类的实例
 */
public class FactoryPatterTest {
    public static void main(String[] args) {
        // 产品A
        FactoryA factoryA = new FactoryA();
        factoryA.getProduct().show();
        // 产品B
        FactoryB factoryB = new FactoryB();
        factoryB.getProduct().show();
    }
}

2.4 优点

  • 更符合开闭原则
    新增一件产品时,只需要增加相应的产品子类和工厂子类即可,简单工厂模式需要修改工厂类的判断逻辑

  • 符合单一职责原则
    每个具体工厂类只负责创建对应的产品,简单工厂中的工厂类存在复杂的switch逻辑判断

  • 不使用静态工厂方法, 可以形成基于继承的等级结构。简单工厂模式的工厂类使用静态工厂方法

总结: 工厂模式可以说是简单工厂模式的进一步抽象和扩展,在保留了简单工厂的封装优点同时,让扩展变得简单,让继承变得可行,增加了多态性的体现

2.5 缺点

  • 添加新产品时,除了增加新产品类外,还要提供与之对应的具体工厂类,系统类的个数将成对增加,在一定程度上增加了系统的复杂度;同时,有更多的类需要编译和运行,给系统带来一些额外的开销
  • 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM,反射等技术,增加了系统的实现难度
  • 虽然保证了工厂方法的对修改关闭原则,但对于使用工厂方法的类,如果要更换另外一种产品,仍然需要修改实例化的具体工厂类
  • 一个具体工厂只能创建一个具体产品

2.6 应用场景

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

3、抽象工厂模式

3.1 定义

抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口

3.2 产品等级和产品族

  • 产品等级:即是相同的产品,例如美的冰箱、海尔冰箱、格力冰箱,他们都是冰箱,他们的产品等级都相同,虽然不属于同一个厂家
  • 产品族:即是相同的厂家,美的冰箱、美的空调、美的电扇,他们虽然不是同一个产品等级,但属于同一个厂家,所以他们的产品族相同

3.3 抽象工厂和抽象方法的区别

抽象工厂是创建型设计模式,他强调了一系列产品对象(即属于同一个产品族)的创建过程,和工厂方法侧重点不同,工厂方法更侧重于同一产品等级,而抽象工厂侧重的是同一产品族

3.4 使用步骤

  1. 创建产品接口,定义产品信息方法
  2. 创建具体产品, 实现产品接口,描述具体产品的信息
  3. 创建抽象工厂接口,提供创建具体产品实例的方法
  4. 创建具体的工厂, 实现抽象工厂接口
  5. 外界调用工厂中提供的方法, 创建具体产品

3.5 实例概况

创建产品接口,描述产品信息

package com.zizhou.factory.abstractly;

/**
 * 创建产品接口,描述产品信息
 */
public interface AirConditioner {
    void airConditionInfo();
}

package com.zizhou.factory.abstractly;

public interface Fan {
    void fanInfo();
}

package com.zizhou.factory.abstractly;

public interface Fridge {
    void fridgeInfo();
}

创建具体产品, 实现产品接口,描述具体产品的信息

package com.zizhou.factory.abstractly;

/**
 * 创建具体产品, 实现产品接口,描述具体产品的信息
 */
public class MediaAirConditioner implements AirConditioner{
    @Override
    public void airConditionInfo() {
        System.out.println("美的空调");
    }
}

package com.zizhou.factory.abstractly;

public class MediaFan implements Fan{
    @Override
    public void fanInfo() {
        System.out.println("美的电风扇");
    }
}

package com.zizhou.factory.abstractly;

public class MediaFridge implements Fridge{
    @Override
    public void fridgeInfo() {
        System.out.println("美的冰箱");
    }
}

创建抽象工厂接口,提供创建具体产品实例的方法

package com.zizhou.factory.abstractly;

/**
 * 创建抽象工厂接口,提供创建具体产品实例的方法
 */
public interface Factory {
    AirConditioner createAirConditioner();
    Fridge createFridge();
    Fan createFan();
}

package com.zizhou.factory.abstractly;

/**
 * 创建具体的工厂, 实现抽象工厂接口
 */
public class MediaFactory implements Factory{
    @Override
    public AirConditioner createAirConditioner() {
        return new MediaAirConditioner();
    }

    @Override
    public Fridge createFridge() {
        return new MediaFridge();
    }

    @Override
    public Fan createFan() {
        return new MediaFan();
    }
}

外界调用工厂中提供的方法, 创建具体产品

/**
 * 外界调用工厂中提供的方法, 创建具体产品
 */
public class AbstractlyTest {
    public static void main(String[] args) {
        Factory factory = new MediaFactory();

        factory.createAirConditioner().airConditionInfo();
        factory.createFridge().fridgeInfo();
        factory.createFan().fanInfo();
    }
}        

3.6 优点

  • 可以确信你从工厂得到的产品彼此是兼容的
  • 可以避免具体产品和客户端代码之间的紧密耦合
  • 符合单一职责原则
  • 符合开闭原则

3.7应用场景

程序需要处理不同系列的相关产品,但是您不需要依赖于这些产品的具体类时,可以使用抽象工厂模式

4、参考文章

  1. 博文地址: https://www.jianshu.com/p/e55fbddc071c
  2. 博文地址:https://blog.csdn.net/varyall/article/details/82355964
  3. 博文地址:https://blog.csdn.net/start_mao/article/details/85946950
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐