工厂模式的设计思想

1,465 阅读9分钟

这是我参与8月更文挑战的第7天,活动详情查看:8月更文挑战

什么是工厂模式

所谓工厂模式,就是: 定义一个用于创建对象的接口,让子类去决定创建哪一个类,说白了 "就是创建什么类" 由子类来决定

说人话就是: 类由工厂创建,什么工厂?看业务,什么业务就是什么工厂。也就是说:将对类的创建这个动作延迟到具体的子类

工厂模式类图

我们根据图示就能写出顶层代码(类图看不懂的可以看UML类图详解):

// 顶层工厂类
abstract class Creator {
    // 创建产品,参数为类名
    abstract Product createProduct(String name);
}

// 顶层产品类
abstract class Product {
    // 获取产品信息
    abstract String getInfo();
}

接着我们来写下底层实现:

// 工厂的具体实现类
class ConcreteCreator extends Creator {
    @Override
    Product createProduct(String name) {
        Product product = null;
        try {
            product = (Product) Class.forName(name).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return product;
    }
}

// 产品的具体实现类
class ConcreteProduct extends Product {
    @Override
    String getInfo() {
        return toString();
    }
}

好,我们来看下业务层的使用:

public void test() {
    // 创建一个工厂
    Creator creator = new ConcreteCreator();
    // 用工厂创建一个产品
    Product product = creator.createProduct("com.test.ConcreteProduct");
    // 打印产品信息
    System.out.println(product.getInfo());
}

根据上述代码,我们知道,顶层拿着Creator去创建对象,但是Creator毛都没做,而是交给具体的工厂类ConcreteCreator来实现,也就是将创建类的动作延迟到子类,正对应了我们上述的定义。

但是,看这个代码,没发现好处在哪里,好像写了一大堆多余的代码啊,我们这么写不行吗:

public void test() {
    Product product = new ConcreteProduct();
    System.out.println(product.getInfo());
}

完全可以,一点问题都没有,但是,如果有多个地方需要创建这个产品的时候,比如:

class A {
    public void test(String type) {
        Product product = new ConcreteProduct();
        System.out.println(product.getInfo());
    }
}

class B {
    public void test(String type) {
        Product product = new ConcreteProduct();
        System.out.println(product.getInfo());
    }
}

class C {
    public void test(String type) {
        Product product = new ConcreteProduct();
        System.out.println(product.getInfo());
    }
}

// ...其他类

然后,有一天,这个产品的创建方法需要改变,必须传入一个name,那么,所有创建这个产品的地方都需要修改...,这个时候你有多少个引用这个类的地方,就需要改多少...如果我们引入了工厂呢,因为只有工厂创建了这个对象,所以只需要修改工厂就可以了,A、B、C都不需要改变。

道理很简单,说白了就是: 将1对多的关系(1个产品,多个地方创建),变为1对1的关系(1个产品,一个工厂创建),所以只会引起一个改变,

更直白些,A、B、C是使用产品的,职责就是使用,不应该涉及创建的逻辑,所以应该有个工厂给我提供这个产品,我不需要知道创建产品的细节,我只知道它能用就行了。所以,工厂模式的一个特点就是: 屏蔽创建细节。因为屏蔽了产品的创建细节,所以,如果产品有任何改动,只要不是使用方式的改动,高层逻辑(使用它的类)都不受影响)。

工厂模式可以退化,当只有一个工厂的时候,就没必要去创建工厂了,直接使用静态方法就可以了,比如单例就是简单工厂模式

进化为多工厂

通过上述,我们看到,ConcreteCreator这个工厂类是通过反射创建对象的,这有很多缺点,1 效率低 2 使用不便。那么我们能不能使用反射呢,可以,我们修改如下:

class ConcreteCreator extends Creator {
    @Override
    Product createProduct(String name) {
        Product product = null;
        if (name.equals("com.test.ConcreteProduct")) {
            product = new ConcreteProduct();
        } else if (name.equals("com.test.ConcreteProduct2")) {
            product = new ConcreteProduct2(); // 这里我们添加一个新的产品2
        }
        return product;
    }
}

可以看到,我们通过判断类名,来添加if-else分支,从而创建不同的产品。问题又来了,当if-else分支多了的时候,可读性差,而且难以维护。怎么办呢,我们可以使用多工厂模式,我们让一个工厂只创建一个产品:

abstract class Creator {
    // 不需要类名了
    abstract Product createProduct();
}

// 工厂1
class ConcreteCreator extends Creator {
    @Override
    Product createProduct(String name) {
        // 只创建产品1
        return new ConcreteProduct();
    }
}

// 工厂2
class ConcreteCreator2 extends Creator {
    @Override
    Product createProduct(String name) {
        // 只创建产品2
        return new ConcreteProduct2();
    }
}

使用方:

public void test(String type) {
    // 创建工厂1
    Creator creator = new ConcreteCreator();
    // 工厂1自己去创建产品1
    Product product = creator.createProduct();

    // 创建工厂2
    Creator creator2 = new ConcreteCreator2();
    // 工厂2自己去创建产品2
    Product product2 = creator2.createProduct();
}

可以看到,改为多工厂后,我们的逻辑清晰了很多:

  • 1 对顶层来说,原来创建产品 我需要知道 产品的名字,而现在只需要找对应的工厂就行,对应的工厂自己会去创建对应的产品。
  • 2 对底层来说,原来新加产品 需要添加if-else来添加分支,都在一个类里,所以只能一个人改。现在添加产品只需要添加一个工厂即可,可以多个人改,一人负责一个工厂就行。

我们的业务逻辑清晰了很多,那么后面维护扩展就方便了很多。但是!不太对镜啊,我需要个产品,你特么先让我去建个工厂,我干嘛要创建工厂...

能不能这样呢?我只需要知道工厂的联系方式,然后就可以联系工厂给我生产产品呢,这样是最符合实际的了,可以!我们来对工厂模式进行修改。

工厂模式优化

我们为了把创建工厂的动作,从底层逻辑抽离出来,我们可以创建一个 创建工厂的工厂,如下:

// 工厂创建器
class FactoryCreator {
    // 创建工厂
    public static Creator createFactory(Class<? extends Creator> claz) {
        try {
            return (Creator) Class.forName(claz.getName()).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

我们创建了一个工厂创建器,然后顶层逻辑只需要找它就可以了。如下:

public void test(String type) {
    // 根据工厂1的联系方式获取工厂1
    Creator creator = FactoryCreator.createFactory(ConcreteCreator.class)
    // 工厂1自己去创建产品1
    Product product = creator.createProduct();

    // 根据工厂2的联系方式获取工厂2
    Creator creator2 = FactoryCreator.createFactory(ConcreteCreator2.class)
    // 工厂2自己去创建产品2
    Product product2 = creator2.createProduct();
}

可以看到,顶层逻辑不再去创建工厂,而是直接传入工厂的联系方式(.class类),就能获取到对应工厂,然后去获取产品。

当然,通过反射的方式创建对象,本身就有性能开销,我们可以添加一个map,来缓存已经创建过的工厂,下次可以直接从map里取。

class FactoryCreator {
    // 用来缓存工厂
    private Map<String, Creator> map = new HashMap<>();

    public Creator createFactory(Class<Creator> claz) {
        try {

            String name = claz.getName();
            // 如果命中缓存,直接返回
            if (map.containsKey(name)) return map.get(name);

            // 否则就创建工厂
            Creator creator = (Creator) Class.forName(claz.getName()).newInstance();
            // 添加到缓存
            map.put(name, creator);
            // 返回工厂
            return creator;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

更详细的代码可以看这里的最后一个例子

工厂模式的使用

我们来看Android源码中的最常见的例子:

View contentView = LayoutInflater.from(mContext).inflate(R.layout.xxxxxxx, null);

我们需要通过LayoutInflater.from(mContext)来获取LayoutInflater,接着看:

public static LayoutInflater from(Context context) {
    // 这里通过Context.getSystemService()来获取
    LayoutInflater LayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    if (LayoutInflater == null) {
        throw new AssertionError("LayoutInflater not found.");
    }
    return LayoutInflater;
}

我们知道,Context的最终实现是ContextImpl,我们接着跟进去:

// 这个name是上面传入的: Context.LAYOUT_INFLATER_SERVICE
@Override
public Object getSystemService(String name) {
    return SystemServiceRegistry.getSystemService(this, name);
}

然后跟到SystemServiceRegistry里面

public static Object getSystemService(ContextImpl ctx, String name) {
    // 先从一个map中获取取一个ServiceFetcher
    ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
    // 然后再通过fetch的getService获取Service,重点!!!
    return fetcher != null ? fetcher.getService(ctx) : null;
}

// ServiceFetcher是一个接口,这是一个典型的工厂模式
static abstract interface ServiceFetcher<T> {
    T getService(ContextImpl ctx);
}

// 这就是存放ServiceFetcher的map
private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS = new HashMap<String, ServiceFetcher<?>>();

那么既然有get,肯定有put,我们找一下这个put:

// 通过registerService来put
private static <T> void registerService(String serviceName, Class<T> serviceClass, ServiceFetcher<T> serviceFetcher) {
    SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);

    // 这里就put进去了
    SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
}

那么什么时候调用registerService呢?我们继续跟:

static {
    // put进来的value是一个工厂,创建LayoutInflater的工厂,这里的工厂是CachedServiceFetcher,重点!
    registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class, new CachedServiceFetcher<LayoutInflater>() {

        // 然后重写了CachedServiceFetcher的createSerivice()方法,重点!
        @Override
        public LayoutInflater createService(ContextImpl ctx) {
            // 创建出PhoneLayoutInflater,它就是LayoutInflater的子类,这里并没有把PhoneLayoutInflater缓存起来。
            return new PhoneLayoutInflater(ctx.getOuterContext());
        }});
}

哦,原来就是在CachedServiceFetcher的静态代码块里,当然,这里还有其他的Service的初始化,比如WindowManagerService,LocationManagerService等。

但是!我们没有发现把LayoutInflater缓存起来的代码!那么每次获取都会重新创建一个吗?我们回退到获取LayoutInflater的地方:

// 这里,通过fetcher.getService()获取,而且通过上面我们知道了这个fetcher就是CachedServiceFetcher,听名字就是带缓存的。
return fetcher != null ? fetcher.getService(ctx) : null;

我们看下:

static abstract class CachedServiceFetcher<T> implements ServiceFetcher<T> {
    private final int mCacheIndex;

    CachedServiceFetcher() {
        // Note this class must be instantiated only by the static initializer of the
        // outer class (SystemServiceRegistry), which already does the synchronization,
        // so bare access to sServiceCacheSize is okay here.
        mCacheIndex = sServiceCacheSize++;
    }

    @Override
    @SuppressWarnings("unchecked")
    public final T getService(ContextImpl ctx) {
        // 哦,果然有个cache
        final Object[] cache = ctx.mServiceCache;
        final int[] gates = ctx.mServiceInitializationStateArray;

        for (;;) {
            boolean doInitialize = false;
            synchronized (cache) {

                // 这里先从缓存里面找,找到就直接返回
                T service = (T) cache[mCacheIndex];
                if (service != null || gates[mCacheIndex] == ContextImpl.STATE_NOT_FOUND) {
                    // 找到了,直接返回
                    return service;
                }

                if (gates[mCacheIndex] == ContextImpl.STATE_READY) {
                    gates[mCacheIndex] = ContextImpl.STATE_UNINITIALIZED;
                }

                if (gates[mCacheIndex] == ContextImpl.STATE_UNINITIALIZED) {
                    doInitialize = true;
                    gates[mCacheIndex] = ContextImpl.STATE_INITIALIZING;
                }
            }

            if (doInitialize) {
                T service = null;
                @ServiceInitializationState int newState = ContextImpl.STATE_NOT_FOUND;
                try {
                    // 这里调用了createService(),也就是被重写的那个
                    service = createService(ctx);
                    newState = ContextImpl.STATE_READY;

                } catch (ServiceNotFoundException e) {
                    onServiceNotFound(e);

                } finally {
                    // 这里直接进行缓存了
                    synchronized (cache) {
                        // 加入缓存
                        cache[mCacheIndex] = service;
                        gates[mCacheIndex] = newState;
                        cache.notifyAll();
                    }
                }
                return service;
            }
            synchronized (cache) {
                while (gates[mCacheIndex] < ContextImpl.STATE_READY) {
                    try {
                        cache.wait();
                    } catch (InterruptedException e) {
                        Log.w(TAG, "getService() interrupted");
                        Thread.currentThread().interrupt();
                        return null;
                    }
                }
            }
        }
    }
    
    // 这里提供抽象方法,直接抛出异常,没事,反正已经被重写了
    public abstract T createService(ContextImpl ctx) throws ServiceNotFoundException;
}

我们直接看注释就行,逻辑很简单,通过一个数组来进行缓存,只不过加了同步锁。

好,现在我们整理下逻辑:

  • 1 我们通过LayoutInflater.from(context)来获取LayoutInflater的实例。
  • 2 内部是通过context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)来实现的。
  • 3 于是我们找到了Context的 真正实现类ContextImpl 里面去
  • 4 发现它是通过SystemServiceRegistry.getSystemService()来实现的,于是我们找到SystemServiceRegistry里面去。
  • 5 发现它是从一个map中获取到一个工厂,然后通过工厂的getService()去获取的,于是我们找向这个map中put工厂的地方。
  • 6 最终发现,是在它的静态代码块中put的。
  • 7 而且我们发现这个工厂的真正实现类是CachedServiceFetcher,它里面自带了缓存,当getService()的时候,先去缓存中找,找到就返回,找不到就调用createService()创建,然后放入缓存。

总结

我们看到了工厂模式的魅力,顶层无需关心对象的细节,只需要委托给工厂去创建即可,极大的降低了耦合性,而且,多工厂模式还提高了灵活性和拓展性。通过源码,我们看到了工厂模式的强大,创建的动作和缓存的动作可以分开,极大的增强了创建对象时机的灵活性。