工厂模式的一些思考

面试中经常被问到设计模式,既然昨天写了被详细问到的单例模式,不妨每天深入学习一个设计模式。总体感觉设计模式学起来很抽象,需要去思考一些问题,尤其是在日常撸代码和工作中可能会突然感悟到。

我查看了很多博客发现大家都把工厂模式具象化讲的很好,例如:工厂模式和单例模式都隶属于创造模式(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)。

六、小结

总结来说,有三种方法实现工厂模式:简单工厂,工厂方法,抽象工厂。他们并不好区分,因为有很多重叠。核心概念:工厂模式是将适当的对象的责任委托给合适的工厂类。如果我们的工厂很复杂,服务于多种类型,比如之前提到的自行车和机动车,我们还是可以有根据的改动我们的代码不至于很混乱。

上一篇:二十三天搞懂设计模式之抽象工厂模式


下一篇:路面不平度的数值模拟方法研究毕业设计论文