设计模式之简单工厂模式

2019-08-05 17:25:37 浏览数 (1)

撸了今年阿里、头条和美团的面试,我有一个重要发现.......>>>

简介

简单工厂模式 Simple Factory Pattern 也叫静态工厂方法模式 Static Factory Method Pattern,隶属于设计模式中的创建型模式。简单工厂通过一个静态方法来给使用方提供类创建的方式,将对象创建的控制权由使用方转换到提供方,可以将使用方和提供方具体的实现解耦,提供方就可以在用户无感知的情况下进行具体实现的替换。

角色

简单工厂模式涉及到3个角色,分别是工厂角色、抽象产品角色、具体产品角色。具体产品是抽象产品的某个实现。

这里的产品也是很抽象的一个概念。按照平时的实践,抽象产品角色可以是策略模式,具体产品角色就是某个策略;抽象产品可以是命令模式,具体产品角色就是某个命令;抽象产品角色也可以是创建型模式中的抽象工厂,具体产品角色就是某个具体的工厂类。只要涉及到继承关系,简单工厂模式就有可能的用武之地,而设计模式是继承使用的重场景,所以简单工厂模式是可以和设计模式的其他模式很好的结合起来的。

模式结构

简单工厂模式通常是以参数来决定我们创建的类的类型的,参数的使用完全取决于简单工厂模式的提供方。我们以策略模式为例,在排序场景使用简单工厂模式来做模式结构说明。

代码语言:javascript复制
// 抽象产品角色,此处为策略模式接口
public interface Sort {
    public void sort(List<Comparable<T>> list);
}

// 具体产品角色,此处为两个排序的具体实现
public class BubbleSort implements Sort {
    public void sort(List<Comparable<T>> list) {
        //使用冒泡算法对 list 进行排序
    }
}

public class QuickSort implements Sort {
    public void sort(List<Comparable<T>> list) {
        // 使用快排算法对 list 进行排序
    }
}

// 工厂角色
// 我们知道在待排数据量很大时使用快排效果最好
// 在数据量小的时候,使用冒泡、插入排序算法效果最好
// 因此我们根据数据量 size 来决定使用哪种排序算法
public class SortFactory {
    public static int THRESHOLD = 16;
    
    public static Sort getSortInstance(int size) {
        if (size <= THRESHOLD) {
            return new BubbleSort();
        }
        
        return QuickSort();
    }
}

使用场景

使用场景决定于模式解决的问题,简单工厂模式要解决的是对象的实例化问题,因此只要有实例化的场景就有可能用到简单工厂模式,像在角色这一节中已经介绍的几个例子,但是也要注意简单工厂模式最好符合以下的条件:

  1. 由于创建过程有简单工厂负责,因此具体产品的数量不宜过多,过多的话就会导致简单工厂模式成为修改瓶颈,也不利于维护
  2. 客户端不需要了解被创建类的细节,而只通过抽象产品角色即可工作
  3. 对类的数量没有限制,在一些客户端上,有时需要限制包大小,简单工厂模式会多创建一个类出来,需要考虑。

最佳实践

  1. 简单工厂模式很多时候都是一个简单的参数比较,如下:
代码语言:javascript复制
public class SimpleFactory {
    public static Product getInstance(int param) {
        if (param == 1) {
            return new ConcreteProduct1();
        }
        
        if (param == 2) {
            return new ConcreteProduct2();
        }
        
        return null;
    }
}

在只有简单的几个实现时,这样写还可以接受,但是当新增实现类时,我们就需要修改简单工厂方法的代码,这违背了设计原则中的开闭原则,对这种情况我们可以使用查表法。

代码语言:javascript复制
public class SimpleFactory {
    public static Map<Integer, Product> productMap = new HashMap<>();
    
    static {
        productMap.put(1, new ConcreteProduct1());
        productMap.put(2, new ConcreteProduct2());
    }
    public static Product getInstance(int param) {
        return productMap.get(param);
    }
}

注意,这个实现和上面的实现并不完全一致,因为在每次查表时返回的都是同一个实例,这适合于 Service 等无状态的类,如果需要每次都需要返回一个新的实例,可以使用 Map<Integer, Class<Product>> 的形式,在每次调用时使用newInstance 来获取新实例。

  1. 简单工厂不仅限于简单的参数相等比较,像我在上面举的排序的例子,简单工厂是可以实现某些策略的。不要自己局限住了。
  2. 使用外部配置文件的方法来配合简单工厂。很多时候简单工厂的参数和实例的关系是直接写在代码中,或者在一个 map 中,但如果需要将工厂的实例对应关系变为可配置的,由使用方决定采用哪个类,我们就可以将配置关系放到配置文件中,简单工厂读取配置文件来加载对应关系。

0 人点赞