引言
工厂模式,简单的理解,就是封装通过new方式创建对象的代码。工厂模式可分为三类:
简单工厂(Simple Factory)
工厂方法(Factory Method)
抽象工厂(Abstract Factory)
本文的目的,就是通过举例理解区分三种工厂模式。
没有工厂模式
场景
如果用户要购买Iphone手机,在没有工厂模式的情况下,用户只能自己根据手机型号来创建手机,客户代码如下:
public class Customer {
public Iphone getIphone(String type) {
switch (type) {
case "iphone5":
return new Iphone5();
case "iphone6":
return new Iphone6();
}
return null;
}
}
问题
当现在需要把ipone5下架,推出iphone6时,Customer代码如下:
public class Customer {
public Iphone getIphone(String type) {
switch (type) {
case "iphone6":
return new Iphone6();
case "iphone7":
return new Iphone7();
}
return null;
}
}
简单的修改Customer端的代码,就能满足新的需求,但是,这违背了一个原则:
设计应该”对扩展开发,对修改关闭”
每次有新的型号,都需要改变Customer的代码,这明显是不合理,于是该普通工厂模式出现了。
普通工厂
把创建手机变化的部分封装到一个新的类SimpleIphoneFactory,Customer代码如下:
public class Customer {
public Iphone getIphone(String type) {
SimpleIphoneFactory simpleIphoneFactory = new SimpleIphoneFactory();
return simpleIphoneFactory.creatIphone(type);
}
}
SimpleIphoneFactory的代码如下:
public class SimpleIphoneFactory {
public Iphone creatIphone(String type) {
switch (type){
case "iphone6":
return new Iphone6();
case "iphone7":
return new Iphone7();
}
return null;
}
}
改变后,感觉代码并没有太大的变化。当iphone6下架,iphone8上架,还是得改变SimpleIphoneFactory的代码。
但是,此时Customer的代码无须改动,简单工厂方法的目的,就是把具体实例化的代码,从客户端删除。
问题
当Iphone的型号越来越多时,SimpleIphoneFactory的代码依然需要改变,Customer类符合开闭原则,SimpleIphoneFactory不符合开闭原则。下面,我们采用工厂方法,把获取手机的方法getIphone()移回Customer,解决SimpleIponeFactory依赖过多Iphone实体对象的问题。
工厂方法
把用户变成抽象类,他的子类决定实例化什么类型的手机:
public abstract class Customer {
public abstract Iphone getIphone();
}
Iphone5消费者:
public class Iphone5Customer extends Customer{
@Override
public Iphone getIphone() {
return new Iphone5();
}
}
Iphone6消费者:
public class Iphone6Customer extends Customer{
@Override
public Iphone getIphone() {
return new Iphone6();
}e
}
下面来看不同用户获取手机的代码:
//购买5的用户
Customer iphone5Customer = new Iphone5Customer();
iphone5Customer.getIphone();
//购买6的用户
Customer iphone6Customer = new Iphone6Customer();
iphone5Customer.getIphone();
问题
用户获取手机,是为了使用,我们给手机添加一个startUp()方法启动手机:
public abstract class Iphone {
/**
* 电池毫安数
*/
protected int power;
/**
* 电池
*/
protected Battery battery;
/**
* 设置电池
* @param battery
*/
public void setBattery(Battery battery){
this.battery = battery;
};
/**
* 开机
*/
public abstract void startUp();
}
Iphone抽象类提供一个开机的抽象方法,由子类实现。我们开看Iphone5的实体类:
public class Iphone5 extends Iphone{
private static final String TAG = "Iphone5";
@Override
public void startUp() {
if(battery.power() == 5000){
Log.d(TAG,"startUp success");
}else{
Log.d(TAG,"Boom!!!!");
}
}
}
可以看到Iphone5实体类,当调用startUp方法时,需要判断电池的毫安数,当等于5000时,成功启动;否则会爆炸。Iphone依赖Battery,下面来看Battery抽象类:
public abstract class Battery {
public abstract int power();
}
抽象类定义了一个抽象power()方法,调用此方法返回电池的毫安数。来看Iphone5Battery和Iphone6Battery的类:
public class Iphone5Battery extends Battery{
@Override
public int power() {
return 5000;
}
}
public class Iphone6Battery extends Battery{
@Override
public int power() {
return 10000;
}
}
假设,用户把iphone5的手机配上iphone6的电池(假设电池外形一样,只是毫安数不一样),代码如下:
Customer iphone5Customer = new Iphone5Customer();
Iphone iphone5 = iphone5Customer.getIphone();
iphone5.setBattery(new Iphone6Battery());
iphone5.startUp();
毫无疑问,这会发生爆炸。Log.d(TAG,"Boom!!!!")
。为了防止爆炸,生产手机时,必须要配套生产同类型的电池。当需要约束产品类之间的关系时,抽象工厂出场了。
抽象工厂
Iphone稳定的产能,需要各个代工厂的生产,苹果公司制定了一套生产手机的框架来保证手机的质量,例如Iphone6的手机只能使用Iphone6的电池。苹果公司可不想像三星手机那样因电池原因发生爆炸事件。
我们修改Customer类如下:
public class Customer {
public void startUp(IphoneFactory iphoneFactory){
iphoneFactory.startUp();
}
}
Customer类提供了一个启动手机的方法,传入一个IphoneFactory对象,由IphoneFactory创建手机和对应的电池,防止因电池型号不对导致的爆炸意外。
IphoneFactory类如下:
public abstract class IphoneFactory {
public abstract Iphone creatIphone();
public abstract Battery creatBattery();
public void startUp(){
Iphone iphone = createIphone();
Battery battery = createBattery();
iphone.setBattery(battery);
iphone.startUp();
}
}
IphoneFactory是一个抽象类,startUp方法确定了Iphone和Battery的关系,子类实现创建Iphone和Battery的方法。看Iphone5Factory的类:
public class Iphone5Factory extends IphoneFactory {
@Override
public Iphone creatIphone() {
return new Iphone5();
}
@Override
public Battery creatBattery() {
return new Iphone4Battery();
}
}
最后我们来看启动Iphone5手机的代码:
IphoneFactory iphone5Factory = new Iphone5Factory();
Customer customer = new Customer();
customer.startUp(iphone5Factory);
可以看到,iphone5手机成功启动的Log。因为IphoneFactory封装了startUp的方法,明确了Iphone和Battery的关系,用户不能自主组装Iphone和Battery,防止了因装错电池导致事故的发生。
类图
简单工厂
工厂方法
抽象工厂
一句话小结
简单工厂:将创建代码从客户端移至工厂类。
工厂方法:用继承的方式实现,一种产品对应一个工厂类。
抽象工厂:系统存在产品族,且产品之间存在关系。