学习GOF23种设计模式,从学习简单工厂开始是个不错的选择,简单工厂虽不属于GOF设计模式中的一种,但它在开发中是非常常用的,也充分体现了面向对象开发的要点和好处,所以理解简单工厂,对于后面学习其它的模式是非常有帮助的。
一、引出模式
我们时常听说面向对象开发很厉害,所以在开发中我们会时不时尝试用面向对象的方法去开发程序,然后,就会遇到这种情况:面对对象开发,我们要有接口,我们要有实现接口的具体类,用的时候我们要遵循面向接口编程。于是乎就出现了这种情况:
IAnimal animal = new Dog(); animal.Call();
哈哈,这不就是面向接口进行编程了吗?当然,这可不是面向接口编程,虽然你使用了接口,但不代表就是面向接口编程。我们一起来围观下吧。这段代码通常是写在客户端的,于是乎,客户端既知道接口,又知道了具体的实现类。有何问题?我们还是先来回顾一下接口最主要的作用吧!没错,就是“封装隔离”,具体的实现类被接口所封装并使其与客户端隔离,也就是说,客户端不应该知道具体实现类的存在。如果不能像上面这样做的话,客户端现在只知道一个接口,那我怎么才能够得到接口的对象呢?
二、简单工厂
1.我们先看看简单工厂的定义吧!
定义:提供一个创建对象实例的功能,而无需关心其具体的实现。
2.上述问题的解决方案:
既然我们在客户端中无法直接得到实现类的对象,那我们可以新增一个类,让这个类来帮我们返回实现类的对象,这样,客户端只要和新增的类有关系就行了,就不需要知道具体的类了,这样不就解除了它们之间的耦合了。
3.简单工厂结构图:
IAnimal:定义客户端所需要的功能接口。
Dog:具体实现IAnimal的实现类
Pig:具体实现IAnimal的实现类
Factory:工厂,选择何时的实现类来创建IAnimal接口对象。
Client:客户端,通过Factory来获取IAnimal接口对象。
提示:看完第一章的朋友,看类图就能知道代码的大致实现了。
4.简单工厂示例代码:
class Program { static void Main(string[] args) { Factory factory = new Factory(); IAnimal animal = factory.Create("pig"); animal.Call(); Console.ReadKey(); } } public class Factory { public IAnimal Create(string Name) { if (Name == "pig") { return new Pig(); } else if (Name == "dog") { return new Dog(); } else { return new Pig(); } } } public interface IAnimal { void Call(); } public class Dog : IAnimal { public void Call() { Console.WriteLine("狗在叫........"); } } public class Pig : IAnimal { public void Call() { Console.WriteLine("猪在叫........"); } }
三、理解简单工厂
1.静态工厂
在客户端中我们创建了工厂类的实例,使用该实例的Create方法,来返回所需要的接口对象。我们其实可以将工厂类当作工具类进行使用,就是将Create方法定义成静态方法,直接通过Factory.Create()获取对象实例,最好也将该类的构造函数设为私有,这样外部就无法实例化该工厂类。静态工厂就是这么来的。
2.如何选择合适的接口对象
工厂类是如何返回合适的接口对象的呢?一般常用的有两种方式,第一种就是从客户端获取参数,通过对参数的判断来选择接口实例,上述的代码示例就是采用此方法,第二种是通过获取配置文件中的值,来选择接口实例。
3.工厂模式的优点
简单工厂模式帮助我们实现了组件的封装,可以让外部实现面向接口编程。
简单工厂模式实现了客户端与具体实现类的解耦。客户端只需知道工厂和接口就可以了。
4.工厂模式的缺点
增加的客户端的复杂度,如果客户端是通过参数获取接口对象的,那么客户端就要理解参数所代表的具体功能。
系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,有可能造成工厂逻辑过于复杂。
5.何时选用工厂模式
如果想要完全封装隔离具体的实现,让外部只能通过接口来操作封装体,可以选用简单工厂。
如果想要把对外创建对象的职责进行集中管理和控制,可以使用简单工厂。
6.总结
简单工厂的本质就是选择实现,再重点就是“选择”,一般在我们系统中接口和具体的实现类都是已经存在了的,我们要思考的就是“如何选择”实现。