这是我参与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()创建,然后放入缓存。
总结
我们看到了工厂模式的魅力,顶层无需关心对象的细节,只需要委托给工厂去创建即可,极大的降低了耦合性,而且,多工厂模式还提高了灵活性和拓展性。通过源码,我们看到了工厂模式的强大,创建的动作和缓存的动作可以分开,极大的增强了创建对象时机的灵活性。
今天的文章工厂模式的设计思想分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/21507.html