给人看的Kotlin设计模式——工厂方法
概念
工厂方法模式相对正式的定义:
In class-based programming, the factory method pattern is a creational pattern that uses factory methods to deal with the problem of creating objects without having to specify the exact class of the object that will be created. This is done by creating objects by calling a factory method—either specified in an interface and implemented by child classes, or implemented in a base class and optionally overridden by derived classes—rather than by calling a constructor. (Wikipedia)
说人话就是,一个接口定义了这么一个工厂方法,然后被若干个子类实现了,这样我们就可以通过多态的方式去调用这个工厂方法来获取对象,避免了我们直接去new一个对象。并且由于是多态调用,我们最后获取对象的实际类型会随各个子类的不同而不同,对象构造的差异化、复杂性被分发给了各个子类,每个子类都变得很简单。
核心思想:对象创建与对象使用分离。
多态实际上是一种逻辑分发,在寻求最大共性的同时,把差异性分发给各个组件,从而简化逻辑。
工厂方法仍然是一种“工厂”,简单工厂的优势对于工厂方法而言也适用:
- 具名化
- 不必在每次调用它们的时候都创建一个新对象
- 可以返回原返回类型的任何子类型的对象
- 所返回的对象的类可以随着每次调用而发生变化,这取决于工厂方法的参数值
- 方法返回的对象所属的类,在编写包含该静态工厂方法的类时可以不存在(例如使用动态代理技术)
示例
来看一个Android中的实际例子:
public interface ViewModelStoreOwner {
//工厂方法
@NonNull
ViewModelStore getViewModelStore();
}
//Activity实现了ViewModelStoreOwner
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
ViewModelStoreOwner {
@NonNull
@Override
public ViewModelStore getViewModelStore() {
if (getApplication() == null) {
throw new IllegalStateException("Your activity is not yet attached to the "
+ "Application instance. You can't request ViewModel before onCreate call.");
}
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
//NonConfigurationInstances存在就从中恢复ViewModelStore
if (nc != null) {
mViewModelStore = nc.viewModelStore;
}
//不然就新建一个ViewModelStore
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
}
//Fragment实现了ViewModelStoreOwner
public class Fragment implements ViewModelStoreOwner {
@NonNull
@Override
public ViewModelStore getViewModelStore() {
if (mFragmentManager == null) {
throw new IllegalStateException("Can't access ViewModels from detached fragment");
}
//从FragmentManager中获取ViewModelStore
return mFragmentManager.getViewModelStore(this);
}
}
正是由于Activity和Fragment都实现了ViewModelStoreOwner接口,我们才能在其中使用ViewModel,像是这样:
class MyFragment : Fragment() {
private val viewModel by lazy {
//这里的this其实就是ViewModelStoreOwner
ViewModelProvider(this).get(MyViewModel::class.java)
}
//如果你使用fragment-ktx,还可以这样,最终还是调用的ViewModelProvider
val viewmodel2: MyViewModel by viewmodels()
}
以上是存储ViewModel时用到的工厂方法模式,我们在创建ViewModel时也会用到工厂方法模式:
public class ViewModelProvider {
//为ViewModel定义的工厂方法
public interface Factory {
@NonNull
<T extends ViewModel> T create(@NonNull Class<T> modelClass);
}
}
有时候为了依赖注入我们会创建自己的ViewModelFactory:
class MyViewModel(
private val repository: MyRepository
) : ViewModel()
class MyViewModelFactory(
private val repository: MyRepository
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return MyViewModel(repository) as T
}
}
使用时:
class MyFragment : Fragment() {
//假设repository已被初始化,仅仅是示例
val repository = app.repository
private val viewModel by lazy {
//将从我们提供的MyViewModelFactory中获取ViewModel
ViewModelProvider(this).get(MyViewModel::class.java, MyViewModelFactory(repository))
}
//如果你使用fragment-ktx,还可以这样
val viewmodel2: MyViewModel by viewmodels {
MyViewModelFactory(repository)
}
}
所以ViewModelProvider(this).get(MyViewModel::class.java, MyViewModelFactory(repository))
其实是用到了两个不同的工厂方法,ViewModelStoreOwner
和ViewModelProvider.Factory
。
结构
工厂方法模式的经典结构:
工厂方法模式主要有四个角色:
- Product 产品,即上例中的ViewModelStore,ViewModel。
- Concrete Products 具体的产品,实现了Product接口的具体的产品。对于第一个例子而言其实是没有这个角色的,Activity和Fragment作为Concrete Creators最终实例化的其实都是ViewModelStore,只是获取的途径不同;对于第二个例子而言Concrete Products就是MyViewModel等等这些我们定义的ViewModel的子类。
- Creator 构建者,即上例中的ViewModelStoreOwner,ViewModelProvider.Factory。
- Concrete Creators 具体的构建者,就是工厂方法的实现类,即上例中的Activity,Fragment,MyViewModelFactory。
工厂方法模式实际上是一种特殊的模板方法模式,只不过工厂方法模式强调的是把实例化对象的过程延迟到各个子类中,这里的“实例化过程”不一定非得是具体产品对象的实例化,工厂方法的优势中也提到了,不必在每次调用工厂方法的时候都创建一个新对象,就像第一个例子那样,使用工厂方法是为了屏蔽对象存储的差异。具体产品的差异化或者产品存储的差异化等是选择工厂方法模式的原因,有差异化也会有共性存在,产品构造、存储的共性逻辑放在Creator中是个很好的选择。
工厂方法模式的关键是工厂角色和产品角色的多态设计。