面试中经常被问到设计模式,既然昨天写了被详细问到的单例模式,不妨每天深入学习一个设计模式。总体感觉设计模式学起来很抽象,需要去思考一些问题,尤其是在日常撸代码和工作中可能会突然感悟到。
我查看了很多博客发现大家都把工厂模式具象化讲的很好,例如:工厂模式和单例模式都隶属于创造模式(Creational Pattern),然后举不同的例子便于大家理解。这里就不班门弄斧了。这篇文章有点像翻译,可能更“学术”一点去分析工厂模式。这篇博客基于《Design Patterns and best practice in Java》。
一、工厂模式的由来
在面向对象的语言中,会有“多态”这一特征,或者子类型多态(Subtyping polymorphism),这就存在了"is/a"这样的关系。例如车(Car)和卡车(Truck)都可以被当做交通工具(Vehicle)。这样的特征让我们的代码看起来更简洁,而且扩展性更好。例如我们还可以很轻松的把自行车和货车轻松实现,并且不修改原来的代码。利用多态我们可以使用Vehicle去new Car或者Truck和其他的。
Vehicle car = new Car();
Vehicle truck = new Truck();
但是多态的实现会带来两个问题,第一个是代码耦合(tightly coupled), 父类和子类的耦合。另外一个是很难在不修改代码的情况下扩展,比如第二行我要一个Truck,我需要new Truck。我们需要遵守开闭原则(对扩展开放对修改关闭),每一次对主类(Vehicle)和增加主类的职责违背了此原则。做为主类的Vehicle需要去实例化子类,违反了单一职责原则(每个类只有一个原因去改变)。
因为上述的原因,我们引出了一个新的设计 - 我们增加一个新的类去实例化Vehicle。由此引出简单工厂模式。
二、简单工厂模式
其实这个翻译可以换成简洁工厂模式。 上述缘由说明:工厂模式主要是概括了一个 通过一个公用接口实例化对象的逻辑,以求增加新的类时最小化改动代码。简单工厂模式如图:
SimpleFactory实现了createProduct去实现ConcreteProduct1和ConcreteProduct2,当client需要一个对象的时候可以调用creatProduct这个方法,通过该方法的参数指定需要的object。SimpleFactory实例化并返回该对象给Product,由Product去分发。代码如下:
这个极简的简单静态工厂满足了 单一职责和依赖倒置的原则。但是新增加“产品”的时候需要改动主类VehicleFactory,所以还是违反了开闭原则。我们可以使用两种方式去改善:1,注册Product类并实例它(使用反射)2,注册产品对象并在每一个对象增加一个新实例,以返回原本的实例。这样解释有些抽象甚至拗口,上代码:
使用map记录不同的对象
private Map<String, class> registeredProducts = new HashMap<String, class>();
然后注册一个vehicle
public void registerVehicle(String vehicleId, Class vehicleClass){
registeredProducts.put(vehicleId, vehicleClass);
}
更改create方法
public Vehicle createVehicle(String type) throws InstantiationException, IllegalAccessException{
Class productClass = registeredProducts(type);
return (Vehicle)productClass.newInstance();
}
反射在一些情况下可能不太好:1,反射要求获取runtime的允许,有些时候不行。2,反射会牺牲性能。所以替代方案:在每一个我们要注册的对象中添加实例。
增加一个抽象方法在VehicleFactory中
abstract public Vehicle newInstance();
这样每一个产品都要实现这个方法
@override
public Car newInstance(){
return new Car();
}
注册产品map
private Map<String, Vehicle> registeredProducts = new HashMap<String, Vehicle>();
通过传递一个实例注册一个新的Vehicle
public void registerVehicle(String vehicleId, Vehicle vehicle){
registeredProducts.put(vehicleId, vehicle);
}
然后我们改变createVehicle方法
public AbstractProduct createVehicle(String vehicleId){
return registeredProducts.get(vehicleId).newInstance();
}
三、工厂方法模式
静态方法的提高还是比较冗长的,甚至包括了反射。由此,使用工厂方法模式去改进,工厂方法模式同时使用抽象类和接口实现。上图:
factory class抽象,然后实例化交给下面继承的子类们。这样做就不需要更改就可以扩展factory了。假设我们有汽车工厂,生产小跑车和家庭用的车,在我们的软件中,客户可以选择任何一个比如跑车或者SUV。我们可以从分类开始,分成跑车和家庭车。我们首先写出抽象类:交通工具工厂(vehicle factory):
public abstract class vehicleFactory{
//abstract method that subclasses can implement
protect abstract Vehicle createVehicle(String item);
public Vehicle orderVehicle(String size, String colour){
Vehicle vehicle = createVehicle(size);
vehicle.testVehicle();
vehicle.setColour(colour);
return vehicle;
}
}
为了建造一个车的实例,我们可以首先建立起来我的车工厂(car factory)
public class CarFactory extends VehicleFactory{
@override
protected Vehicle createVehicle(String size){
if(size == "small"){
return new SportsCar();
}else if(size == "large"){
return new SedanCar();
}
return null;
}
}
作为客户端我们可以这样去实现:
VehicleFactory carFactory = new CarFactory();
carFactory.orderVehicle("large","blue");
此时我们通过市场调研,发现卡车更受欢迎,我们就可以建造我们的卡车工厂,然后作为客户端并不需要知道卡车工厂是个什么,怎么制作卡车的只需要关心orderVehicle的两个参数就够了。
四、匿名实体工厂
如果此时我们调研觉得自行车更好,我们可以建造我们的自行车工厂,我们甚至可以不需要另外建立额外的类,直接用匿名内部类搞定:
VehicleFactory bikeFactory = new VehicleFactory(){
@override
protected Vehicle createVehicle(String size){
if(size == "small"){
return new MountainBike();
}else if(size == "large"){
return new CityBike();
}
return null;
}
};
bikeFactory.orderVehicle("large","blue");
五、抽象工厂
抽象工厂是工厂方法的一种延伸。不像工厂方法只创建一个对象,抽象工厂方法创建一系列相关对象的家族对象。如果工厂方法有一个抽象产品(AbstractProduct),则抽象工厂创建一些列的抽象产品。其实工厂方法像是抽象工厂中特别的一个。抽象工厂图:
AbstractFactory :抽象工厂类,声明了所有创造产品类型,如图createProductA和createProductB。
ConcreteFactory: 继承抽象工厂类,实现了抽象工厂类的create方法,这个类负责各个实体类的工厂。
AbstractProduct:抽象产品类,基础接口或者类对最终产品来说,图示中ProductA1和ProductB1属于一个家族(ConcreteFactory1), ProductA2和ProductB2属于一个家族(ConcreteFactory2)。
六、小结
总结来说,有三种方法实现工厂模式:简单工厂,工厂方法,抽象工厂。他们并不好区分,因为有很多重叠。核心概念:工厂模式是将适当的对象的责任委托给合适的工厂类。如果我们的工厂很复杂,服务于多种类型,比如之前提到的自行车和机动车,我们还是可以有根据的改动我们的代码不至于很混乱。