设计模式-工厂(方法)模式
预告/前言
面向对象设计模式分为三类:创建型、结构型和行为型,工厂设计模式是面向对象设计模式中的创建型设计模式之一。
创建型设计模式主要包括:
- 单例模式
- 工厂模式
- 抽象工厂模式
- 构建者模式
- 原型模式
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 框架编写自己的应用。你希望在应用中使用圆形按钮,但是原框架仅支持矩形按钮。你可以使用
圆形按钮RoundButton子类来继承标准的按钮Button类。但是,你需要告诉UI框架UIFramework类使用新的子类按钮代替默认按钮。为了实现这个功能,你可以根据基础框架类开发子类圆形按钮UIUIWithRoundButtons,并且重写其createButton创建按钮方法。基类中的该方法返回按钮对象, 而你开发的子类返回圆形按钮对象。 现在,你就可以使用圆形按钮UI类代替UI框架类。 - 如果你希望复用现有对象来节省系统资源, 而不是每次都重新创建对象, 可使用工厂方法。
P&Cs 优缺点
优点:
- 避免创建者和具体产品之间的紧密耦合。
- 单一职责原则。 你可以将产品创建代码放在程序的单一位置, 从而使得代码更容易维护。
- 开闭原则。 无需更改现有客户端代码, 你就可以在程序中引入新的产品类型.
缺点: - 应用工厂方法模式需要引入许多新的子类, 代码可能会因此变得更复杂。 最好的情况是将该模式引入创建者类的现有层次结构中。
where JDK中使用工厂模式的例子
很多
- java.util.Calendar类创建实例时,使用getInstance(),对于客户端来讲,不用关注Calendar创建的具体实现;
- NumberFormat类创建实例时,也是使用getInstance()。