对简单工厂模式还不了解的可以查看下我的历史文章 简单工厂模式,简单工厂模式的核心是使用工厂实现选择创建产品实现,这应该很好理解。
简单工厂模式
对工厂方法模式还不了解的可以查看下我的历史文工厂方法模式。工厂方法模式的核心是将选择创建产品实现的过程,延迟到子类去完成,这也是相对比较好理解的模式。
工厂方法模式
第一节的回顾,我们复习了简单工厂与工厂方法模式。由此我们也会使用 UML 图线画出对抽象工厂模式的理解,然后我们在对比三张 UML 图来分析他们之间的设计是如何演进或取舍的。
抽象工厂模式
抽象工厂模式强调了产品簇的概念,那么什么是产品簇呢?在抽象工厂模式中,产品簇是指工厂生产出的产品们之间彼此具备强关联。比如:AK47工厂生产的 AK47步枪、AK47专配的子弹,一旦 AK47装错了子弹是无法正常开枪的(甚至会炸膛)。
刚开始接触抽象工厂模式的时候,我就是不能理解抽象工厂模式与工厂方法模式的区别,也许屏幕前的你也有一样的困惑。但经过一段时间时间的思考与实践,我逐渐的明白它们之间的差异。
移动手机大卖场
工厂方法模式生产产品的形式就像 移动手机大卖场(上图)。卖场里面只卖手机,这些手机里面可能有 三星、华为、OPPO、金立、苹果、HTC、Nokia 等类别,但无论如何它都没有跳出手机的范畴。因此想买手机的客户,直接去大卖场总能挑选到心仪的手机,因为手机(工厂生产的产品实例)太多了。
苹果零售店
而抽象工厂模式生产产品的形式则更像是苹果零售店(上图),除了卖手机之外还提供Mac、路由器、耳机、iPad、iPod 等电子产品。作为一个对品质(逼格)有要求的客户,会直接去苹果零售店里购买手机,一时兴起再买一台 Mac 也是可以的。
无论手机大卖场和苹果零售店都是可以买到手机的,但是针对的客户群体显然是不一样的。同样的,工厂方法模式 与 抽象工厂模式 针对的客户群体也是不一样的,不过它们都拥有 生产产品 这一职责。
让单一产品线上的产品更易被扩展
。让多条 {单一产品线上的产品更易被扩展} 的同时,能够让多条线上的产品保持约束
。工厂方法&抽象工厂
如上图,横向有两条产品线(手机、电脑),纵向有三个产品簇(苹果、三星、小米)。当我们不需要考虑产品簇时,为工厂方法模式。反之,就是抽象工厂模式。
目录
可以发现在目录层级上与工厂方法模式并没有区别,差异在工厂的组织形式上。
public interface IPhone {
void screen();
void micro();
void battery();
void usb();
// XXX just for test
void printLog();
}
苹果手机,及电脑的实现类就省略了。
public class XiaoMiPhoneImpl implements IPhone {
private String TAG = "小米手机";
@Override
public void screen() {
System.out.println(TAG + "屏幕");
}
@Override
public void micro() {
System.out.println(TAG + "麦克风");
}
@Override
public void battery() {
System.out.println(TAG + "电池");
}
@Override
public void usb() {
System.out.println(TAG + "usb插口");
}
@Override
public void printLog() {
screen();
micro();
battery();
usb();
}
}
实际生活中可能无法提取工厂接口,毕竟每个工厂生产的产品不可能都保持一致。在这里定义接口的目的是想强调:这是纵向的产品簇。
public interface IFactory {
void createPhone();
void createComputer();
}
public class XiaoMiFactoryImpl implements IFactory {
@Override
public void createPhone() {
IPhone phone = new XiaoMiPhoneImpl();
phone.printLog();
}
@Override
public void createComputer() {
IComputer computer = new XiaoMiComputerImpl();
computer.printLog();
}
}
public class Client {
public static void main(String[] args) {
IFactory factory = new XiaoMiFactoryImpl();
factory.createPhone();
factory.createComputer();
}
}
小米手机屏幕
小米手机麦克风
小米手机电池
小米手机usb插口
小米电脑屏幕
小米电脑键盘
小米电脑鼠标
小米电脑usb
在理解了思想的前提下,编写这些代码是相当简单的。
IPolicy 是产生窗口屏幕相关对象的抽象接口,在 android 源码中,Policy 是 IPolicy 的唯一一个实现,用于生成对象集合。Policy 创建了一系列的对象(PhoneWindow、PhoneLayoutInflater、PhoneWindowManager、PhoneFallbackEventHandler)。从UML图就可以看出,IPolicy算是一个典型的抽象工厂,只不过在源码中只有一个具体的工厂实现。
另外Policy的实现还有一点特殊的地方,它使用static域将他需要创建的对象都预先load出来,也就是说当虚拟机加载Policy类的时候,就会加载它创建的对象的class。
public interface IPolicy {
public Window makeNewWindow(Context context);
public LayoutInflater makeNewLayoutInflater(Context context);
public WindowManagerPolicy makeNewWindowManager();
public FallbackEventHandler makeNewFallbackEventHandler(Context context);
}
public class Policy implements IPolicy {
private static final String TAG = "PhonePolicy";
private static final String[] preload_classes = {
"com.android.internal.policy.impl.PhoneLayoutInflater",
"com.android.internal.policy.impl.PhoneWindow",
"com.android.internal.policy.impl.PhoneWindow$1",
"com.android.internal.policy.impl.PhoneWindow$DialogMenuCallback",
"com.android.internal.policy.impl.PhoneWindow$DecorView",
"com.android.internal.policy.impl.PhoneWindow$PanelFeatureState",
"com.android.internal.policy.impl.PhoneWindow$PanelFeatureState$SavedState",
};
static {
// For performance reasons, preload some policy specific classes when
// the policy gets loaded.
for (String s : preload_classes) {
try {
Class.forName(s);
} catch (ClassNotFoundException ex) {
Log.e(TAG, "Could not preload class for phone policy: " + s);
}
}
}
public Window makeNewWindow(Context context) {
return new PhoneWindow(context);
}
public LayoutInflater makeNewLayoutInflater(Context context) {
return new PhoneLayoutInflater(context);
}
public WindowManagerPolicy makeNewWindowManager() {
return new PhoneWindowManager();
}
public FallbackEventHandler makeNewFallbackEventHandler(Context context) {
return new PhoneFallbackEventHandler(context);
}
}
Window
)代码太多,咱就不贴了。
PhoneWindow
)代码太多,咱就不贴了。
其实四种不同的产品(Window,WindowManagerPolicy,LayoutInflater,FallbackEventHandler),如果用四个工厂方法也是可以的,但是会缺乏将本来应该有相互关联的产品拆分开了,抽象工厂一定程度上提高了它们之间的内聚。
假如除了Phone外,如果需要加入新的产品簇(比如说TV,智能手表),那么只需要创建一个对应Policy,以及对应的产品系列,然后将PolicyManager的IPolicy指向新产品簇的Policy就好了。