LOADING

加载过慢请开启缓存 浏览器默认开启

设计模式-Factory工厂模式

2023/7/17 java DesignPattern
  1. 设计模式-工厂(方法)模式
    1. 预告/前言
    2. what/why
    3. structure
    4. how
      1. 实现方式
    5. when 适合的应用场景
    6. P&Cs 优缺点
    7. where JDK中使用工厂模式的例子

设计模式-工厂(方法)模式

预告/前言

面向对象设计模式分为三类:创建型、结构型和行为型,工厂设计模式是面向对象设计模式中的创建型设计模式之一。
创建型设计模式主要包括:

  • 单例模式
  • 工厂模式
  • 抽象工厂模式
  • 构建者模式
  • 原型模式

what/why

通过一个工厂类来封装对象的创建过程,实现对象的创建与使用的分离。
提供灵活的对象创建方式,降低代码的耦合度,同时也方便了代码的扩展和维护。

structure

在工厂模式中,主要包含以下元素:

  • 超类(可以是一个接口,抽象类或一个具体的类)
  • 一个或多个子类
  • 工厂类

工厂模式

how

具体例子:

  • 抽象概念: Computer
  • 具体实现: PC机、笔记本Laptop。

Computer抽象类(也可以是一个接口)

public abstract class Computer {
    public abstract String getRAM();    /* 内存 */
    public abstract String getHDD();    /* 硬盘 */
    public abstract String getCPU();    /* CPU */
    @Override
    public String toString() {
        return "RAM= " + this.getRAM() + ", HDD=" + this.getHDD() + ", CPU=" + this.getCPU();
    }
}

定义PC和Laptop

public class PC extends Computer {
    private String ram;
    private String hdd;
    private String cpu;

    public PC(String ram, String hdd, String cpu) {
        this.ram = ram;
        this.hdd = hdd;
        this.cpu = cpu;
    }

    @Override
    public String getRAM() {  return ram;}
    @Override
    public String getHDD() {  return hdd;}
    @Override
    public String getCPU() {  return cpu;}
}
public class Laptop extends Computer{
    private String ram;
    private String hdd;
    private String cpu;
    
    public Laptop(String ram, String hdd, String cpu) {
        this.ram = ram;
        this.hdd = hdd;
        this.cpu = cpu;
    }
    @Override
    public String getRAM() {  return ram;}
    @Override
    public String getHDD() {  return hdd;}
    @Override
    public String getCPU() {  return cpu;}
}

定义工厂类:

public class ComputerFactory {

    public static Computer getComputer(String type, String ram, String hdd, String cpu) {
        // 不区分大小写比较
        if ("PC".equalsIgnoreCase(type)) {
            return new PC(ram, hdd, cpu);
        }
        if ("Laptop".equalsIgnoreCase(type)) {
            return new Laptop(ram, hdd, cpu);
        }
        return null;
    }
}

对于客户端只需要根据需要的类型,调用工厂类的getComputer()方法获取想要的实例了。
简单的测试,模拟工厂类的使用。

public class FactoryTest {

    public static void main(String[] args) {
        Computer pc = ComputerFactory.getComputer("PC", "16GB", "500GB", "2.4GHz");
        Computer laptop = ComputerFactory.getComputer("Laptop", "16GB", "500GB", "2.4GHz");

        System.out.println("PC:"+pc);
        System.out.println("laptop:"+laptop);
    }
}

实现方式

  • 让所有产品都遵循同一接口。 该接口必须声明对所有产品都有意义的方法。
  • 在创建类中添加一个空的工厂方法。 该方法的返回类型必须遵循通用的产品接口。
  • 在创建者代码中找到对于产品构造函数的所有引用。 将它们依次替换为对于工厂方法的调用,同时将创建产品的代码移入工厂方法。可能需要在工厂方法中添加临时参数来控制返回的产品类型。
    工厂方法的代码看上去可能非常糟糕。 其中可能会有复杂的 switch分支运算符, 用于选择各种需要实例化的产品类。(友情链接-策略模式解决)
  • 现在, 为工厂方法中的每种产品编写一个创建者子类, 然后在子类中重写工厂方法, 并将基本方法中的相关创建代码移动到工厂方法中。
  • 如果应用中的产品类型太多, 那么为每个产品创建子类并无太大必要, 这时你也可以在子类中复用基类中的控制参数。
  • 如果代码经过上述移动后, 基础工厂方法中已经没有任何代码, 你可以将其转变为抽象类。 如果基础工厂方法中还有其他语句, 你可以将其设置为该方法的默认行为。

when 适合的应用场景

  • 当你在编写代码的过程中, 如果无法预知对象确切类别及其依赖关系时, 可使用工厂方法。
    工厂方法将创建产品的代码与实际使用产品的代码分离, 从而能在不影响其他代码的情况下扩展产品创建部分代码。
    例如, 如果需要向应用中添加一种新产品, 你只需要开发新的创建者子类, 然后重写其工厂方法即可。
  • 如果你希望用户能扩展你软件库或框架的内部组件, 可使用工厂方法。
    继承可能是扩展软件库或框架默认行为的最简单方法。 但是当你使用子类替代标准组件时, 框架如何辨识出该子类?解决方案是将各框架中构造组件的代码集中到单个工厂方法中, 并在继承该组件之外允许任何人对该方法进行重写。

    假设你使用开源 UI 框架编写自己的应用。你希望在应用中使用圆形按钮,但是原框架仅支持矩形按钮。你可以使用圆形按钮Round­Button子类来继承标准的按钮Button类。但是,你需要告诉UI框架UIFramework类使用新的子类按钮代替默认按钮。为了实现这个功能,你可以根据基础框架类开发子类圆形按钮UIUIWith­Round­Buttons,并且重写其create­Button创建按钮方法。基类中的该方法返回按钮对象, 而你开发的子类返回圆形按钮对象。 现在,你就可以使用圆形按钮UI类代替UI框架类。

  • 如果你希望复用现有对象来节省系统资源, 而不是每次都重新创建对象, 可使用工厂方法。

P&Cs 优缺点

优点:

  • 避免创建者和具体产品之间的紧密耦合。
  • 单一职责原则。 你可以将产品创建代码放在程序的单一位置, 从而使得代码更容易维护。
  • 开闭原则。 无需更改现有客户端代码, 你就可以在程序中引入新的产品类型.
    缺点:
  • 应用工厂方法模式需要引入许多新的子类, 代码可能会因此变得更复杂。 最好的情况是将该模式引入创建者类的现有层次结构中。

where JDK中使用工厂模式的例子

很多

  • java.util.Calendar类创建实例时,使用getInstance(),对于客户端来讲,不用关注Calendar创建的具体实现;
  • NumberFormat类创建实例时,也是使用getInstance()。