假设我养了两种宠物,分别为狗和兔子。狗吃肉,兔子吃草,于是我创建Dog类和Rabbit类,并给出对应的eatMeat()函数和eatGrass()函数。
正常情况下,我在喂宠物时会通过一个主函数中的switch语句来决定喂哪只。
然而,当我以后想要养一只仓鼠时,就需要再在主函数的switch中加入仓鼠的case,这就更改了原来的代码,违背了开闭原则(改动已有代码的结构哒咩)。
再或者说,我突然不想喂狗了,我想喂兔子,我还得更改原来所有的Dog类实例对象为Rabbit类实例对象,太麻烦,而且也违反了开闭原则。
还有一种可能,就是我养宠物只是因为家里吃的太多,想让宠物帮我吃,只想要草食动物或者肉食动物,不想具体分清哪些宠物吃草、哪些宠物吃肉(客户端对实现并不关心)。
于是我灵机一动,采用了工厂方法模式。
首先我们把Dog类和Rabbit类抽象出一个父类Pet类,Pet类里面有一个eat()的虚函数,这样我们只需要在Dog类中和Rabbit类中覆写eat(),在喂养时我们只需要说“给我eat!”,宠物就自己吃自己该吃的了。
然后我们使用一个统一的PetFactory类中的getPet()用来生成Pet,就完成了一个简单工厂模式(Simple Factory Pattern)。
但是我们的目标还没有完全解决。在加入新动物后,虽然主人(客户端)那边不需要更改指令,但是PetFactory类依旧要进行更改,依旧不太符合开闭原则。而且主人还是要考虑哪些是肉食动物、那些是草食动物来指示工厂生成对应的Pet。
设计模式的核心思想是:抽象,问题没有解决,那就接着抽象。
我们把之前用的PetFactory类抽象为抽象类,让我们想要的食肉动物工厂MeatPetFactory类和食草动物工厂GrassPetFactory类作为它的子类,并覆写PetFactory中的getPet()函数,就完成了工厂方法模式(Factory Method Pattern)。
现在我们看看工厂设计模式如何解决了之前的问题:
首先,Dog与Rabbit的构造已经被封装为MeatPetFactory和GrassPetFactory,主人(客户端)无需知道到底是具体哪种动物吃了他们家的肉和草,只知道他们创造出来的这种小动物能达成吃肉吃草的目的即可。
另外,如果我原来喂的是Dog,现在想要更改我喂的宠物为Rabbit,我只需要在创建宠物处将创建用工厂从MeatPetFactory改为GrassPetFactory即可,无需更改源代码的逻辑,客户端调用的依旧是PetFactory.getPet()和Pet.eat()。
当然,如果我现在肉和草的数量较为平均,想找一种杂食动物。使用工厂方式后,我们完全可以在不违反开闭原则的前提下做到这件事:
如上图,我们在不更改原来代码结构的前提下轻松地实现了对源代码的扩展。
这就是工厂方法模式!