当你有一群相关的具体类时,通常会写出如下代码:
一旦有变化或扩展,就要打开代码进行检查,这样使得系统更难维护更新,也容易犯错。
记得我们的第一个设计原则:找出可能变化的部分,将它们从不变的地方提取出来。
遵循此原则,按照惯例从故事说起。
假如你有一个比萨店,你的比萨订购系统会这样写代码:
但是比萨的种类不止一种,于是只能有一个指定种类的参数传入:
但是由于竞争激烈,你必须不断增加比萨种类,于是你增加了种类,又删掉了不要的种类:
这样肯定不行的。根据第一条原则,可能变化的要封装起来,我们将下面这段抽取出来:
放在另一个类SimplePizzaFactory中,如图:
我们可以叫这个类工厂类,因为它专门生产对象。之前的OrderPizza方法只要得到这样的一个对象就可以得到想要的Pizza了,OrderPizza成为了该对象的一个“客户”。
你一定会问,这样做有什么意思,只不过转移了代码。当然不是,因为SimplePizzaFactory可以有很多客户,而且以后扩展披萨种类就不用修改到订购的代码了,OrderPizza所要知道的是它拥有一个工厂,该工厂可以生产它所要的
Pizaa。它甚至不需要知道做出来的披萨的样子,就可以去prepare,去bake了。
像这样:
这样,一个简单工厂的建好了!
当然,简单工厂并不是一种设计模式,因为只是简单的把创建对象的代码封装起来,它充其量只算是工厂模式的一种热身。接下来才是真正的工厂模式现身~~
现在你要开连锁店了,不同地区的连锁店有不同的风味。你有一个想法,创建不同的工厂,比如纽约工厂和芝加哥工厂,然后像之前的代码一样将它们传入PizzaStore中,就可以生产出具有独特地域风格的Pizza了,类似这样:
但是你发现连锁店对比萨的制作流程也不完全一样(prepare方法),而且如果传错工厂可能出现纽约连锁店制作出芝加哥风味的比萨。。你现在想把连锁店和创建比萨绑在一起的同时又保持一定的弹性。
有一个方法可以让比萨的制作活动局限于PizzaStore类,但又让每个区域的连锁店可以自由制作符合地域风格的比萨。
你可以将PizzaStore做成抽象类,创建比萨的方法做成抽象方法,不同区域具体的比萨店继承PizzaStore去实现该方法,像这样:
比如纽约的店,就可以将该方法这样写:
这样让子类去做决定制作什么风味的比萨。
现在只要new出一个某区域的店,调用该店的orderPizza方法就可以得到想要的比萨了。
我们抽象的比萨类可以这样设计:
纽约和芝加哥的比萨可以这样设计:
这样可以提供不同的作料,也可以任意改变比萨的制作方法(覆盖基类方法)
运行结果:
梳理下工厂方法的条理,工厂方法主要由创建者类PizzaStore和产品类Pizza组成,它们都是抽象类。PizzaStore有两个方法,一个是订购orderPizza,包括createPizza和Pizza出售的准备工作,而createPizza是一个抽象方法,留给子类去实现,子类是具体的披萨店,它去决定生产怎么样的披萨。而Pizza的子类就是各种披萨咯。
就像下面的图:
看下官方的定义:
工厂方法(Factory Method)模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中。
核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅负责具体工厂子类必须实现的接口,这样进一步抽象化的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新的产品。
如下面的类图,抽象的Creator提供了创建对象的借口,也叫做“工厂方法”。只有子类可以通过实现该方法来创建爱你对象。可以说,抽象Creator创建抽象产品,具体Creator创建具体产品。因为Creator和具体的产品没有紧耦合,所以增加和改变产品并不会影响到Creator,所以说工厂方法将产品的“实现”从“使用”中解耦。
由于工厂模式内容较多,主要有两个小模式,今先聊第一个模式:工厂方法,下次聊抽象工厂。