设计模式(三)工厂方法

94 阅读4分钟

工厂方法是指使用一个接口方法代替 new 操作实现对象实例化,隐藏对象创建的细节和逻辑,这样可以根据不同的场景返回不同的实例对象。

工厂方法根据抽象程度不同分为两类:

  • 简单工厂,又称静态工厂
  • 工厂方法,又称多态工厂、虚拟构造器

一、简单工厂

定义一个专门的工厂类负责创建其它类的实例,被创建的实例拥有共同的父类。特点:

  • 有一个抽象产品类,定义了一个以上的抽象方法需要在具体产品子类中实现
  • 继承自该抽象产品类的具体产品子类需实现这些抽象方法
  • 有一个工厂类,专门负责实例化这些子类对象
// 抽象基类:颜色
public abstract class Color {
    // 抽象方法:返回色值
    public abstract String getValue();
}

// 子类:黑色
public class Black extends Color {
    public Black() {
        System.out.println("Black Instantiation");
    }
    
    @Override
    public String getValue() {
        return "000000";
    }
}

// 子类:红色
public class Red extends Color {
    public Red() {
        System.out.println("Red Instantiation");
    }
    
    @Override
    public String getValue() {
        return "ff0000";
    }
}

// 子类:白色
public class White extends Color {
    public White() {
        System.out.println("White Instantiation");
    }
    
    @Override
    public String getValue() {
        return "ffffff";
    }
}

// 实例化颜色的工厂类
public class ColorFactory {
    public static Color getColor(String colorName) {
        switch (colorName.toLowerCase()) {
            case "black":
                return new Black();
            case "red":
                return new Red();
            case "white":
                return new White();
            default:
                return null;
        }
    }
}

测试简单工厂

import org.junit.Test;

import java.util.Random;

public class ColorFactoryTests {
    @Test
    public void test() {
        String[] colorNames = {"black", "red", "white"};
        Random random = new Random();
        for (int i = 0; i < 10; i++) {
            ColorFactory.getColor(colorNames[random.nextInt(3)]);
        }
    }
}

测试结果

Black Instantiation
Black Instantiation
White Instantiation
Red Instantiation
White Instantiation
Red Instantiation
White Instantiation
Black Instantiation
White Instantiation
White Instantiation

优点:

  • 使用者通过工厂获取所需对象实例,无需直接创建对象实例,这样使用者就无需关心创建过程,只需要聚焦于使用,实现了解耦;
  • 工厂负责相关对象的实例化工作,如果创建对象的逻辑发生变化,则只需在工厂中集中修改,便于维护。

缺点:

  • 工厂集中了对象的实例化工作,则一定要保证其始终正常运行,否则系统中所有相关对象的实例化都会发生异常;
  • 如果对象类型很多,则工厂逻辑会比较复杂,且一旦新增一个对象类型,就需要修改工厂的逻辑,违反了“开-闭原则”;
  • 使用了静态工厂方法,静态方法不能被集成和重写,工厂角色无法形成继承结构。

二、工厂方法

工厂方法定义一个用于创建对象的工厂接口,让工厂子类(具体工厂)决定实例化哪一个类。工厂方法使一个类的实例化延迟到工厂子类。特点:

  • 定义一个抽象产品类,被所有具体产品类继承并实现公共行为方法
  • 定义一个工厂接口,此接口包含创建抽象产品类的方法,具体工厂类实现此接口方法
  • 具体工厂类决定创建具体产品实例的逻辑
// 工厂接口
public interface IColorFactory {
    Color getColor();
}

// 具体工厂类:实例化黑色
public class BlackFactory implements IColorFactory {
    @Override
    public Color getColor() {
        return new Black();
    }
}

// 具体工厂类:实例化红色
public class RedFactory implements IColorFactory {
    @Override
    public Color getColor() {
        return new Red();
    }
}

// 具体工厂类:实例化白色
public class WhiteFactory implements IColorFactory {
    @Override
    public Color getColor() {
        return new White();
    }
}

// Black、Red、White 三个类的代码在此省略,和简单工厂中示例代码完全一致

工厂方法的使用与测试

@Test
public void test() {
    IColorFactory factory;
    factory = new BlackFactory();
    Color black = factory.getColor();
    factory = new RedFactory();
    Color red = factory.getColor();
    factory = new WhiteFactory();
    Color white = factory.getColor();
}

优点:

  • 工厂方法中不再使用核心工厂类负责所有相关对象的创建,而是使用接口让其变成了一个抽象角色,定义了所有工厂子类必须实现的接口方法,这样便可以在不修改现有工厂的情况下引入新产品,遵守了“开-闭原则”并提高了可扩展性;
  • 在工厂方法中,通常一个核心工厂接口对应一个抽象产品基类,每个具体工厂类对应一个具体产品类,这个具体工厂就负责这个具体产品的实例化。

缺点: 由于具体工厂类和具体产品类一一对应,所以产品类数量的增加会导致工厂类数据同比例增加,可能导致类规模迅速扩大,增加系统编译和运行开销,也不利于系统维护。