定义
定义一个创建对象的接口,但让这个接口的实现类来决定实例化哪个类。工厂方法让类的实例化推迟到子类中进行。也就是说,工厂方法模式会定义一个单独的方法去创建或者管理对象。
考虑到创建对象的功能粒度,比较好的方式是未每一个具体目标对象设置一个单独的工厂实现类以及对象创建方法。
工厂方法模式的类图
只去看定义,可能学术化太强,不足以形成一种比较形象的思维。让我们来做一个这样的思考:
首先,接口更多的是强调行为,我们是赋予这个接口创建对象的职责。这个时候,大家暂时不用去考虑具体实现的事情,把关注点放在创建对象这一抽象概念上,具体开发上就是关注当前的接口。 同时,我们采用面向对象的设计原则,尽量做到接口设计的职责单一。如果在具体的设计上涉及多个场景,可以考虑抽象工厂,这个会在以后的篇幅里回顾。
综上所述,工厂方法模式,有着很多优势:
最大的优势莫过于,它体现着面向对象的开闭原则我们为什么要对每个对象都设计一个工厂接口,就是因为我们不希望因为未来不缺性的需求而影响现有功能,所以我们需要开放出一个口子,既能保持现有设计,又能不断的扩展新功能,也是就是动态扩展。 其次,采用较细的粒度设计,也稳定了当前的设计,减少了未来的影响,这是单一职责的好处。
当然,工厂模式也有着很明显的缺陷:
首先类的个数成倍数级增加 。 同时引入的抽象层或者说是抽象概念,也在一定程度上增加了系统理解的难度,根据实现方式的需要也有可能会增加系统额外的开发,因为有些功能的实现方式还会考虑反射等 。
应用场景
当一个类不需要依赖其他类而只需要依赖其接口的时候,可以考虑工厂模式 。 为了使系统更好的适应未来不缺性的需要,可以考虑功能扩展,这一点可以看看.NET Core关于Configuration功能的源码 。
Demo
最后我们看看demo吧,这是一个非常简单的demo,正常来说,工厂模式涵盖的内容很多,思维不要太过局限于demo,更多还应该考虑每个工厂可能还有自己特殊的地方,毕竟已经设计了单独的工厂类了。
以下demo的场景是想找到一个厨师去做饭,厨师角色暂时按照男女厨师来区分。
代码语言:javascript复制1 public interface IFactory
2 {
3 ICook GetCooker();
4 }
代码语言:javascript复制1 public class ManFactory : IFactory
2 {
3 public ICook GetCooker()
4 {
5 return new IManCook();
6 }
7 }
代码语言:javascript复制1 public class WomanFactory : IFactory
2 {
3 public ICook GetCooker()
4 {
5 return new IWomanCook();
6 }
7 }
代码语言:javascript复制1 public interface ICook
2 {
3 void DoMeal();
4 }
代码语言:javascript复制1 public class IManCook : ICook
2 {
3 public void DoMeal()
4 {
5 Console.WriteLine("男厨师做饭");
6 }
7 }
代码语言:javascript复制1 public class IWomanCook : ICook
2 {
3 public void DoMeal()
4 {
5 Console.WriteLine("女厨师做饭");
6 }
7 }
代码语言:javascript复制 1 public class Program
2 {
3 static void Main(string[] args)
4 {
5 //找男厨师做饭
6 IFactory manFactory = new ManFactory();
7 ICook manCook = manFactory.GetCooker();
8 manCook.DoMeal();
9
10 //找女厨师做饭
11 IFactory womanFactory = new ManFactory();
12 ICook womanCook = womanFactory.GetCooker();
13 womanCook.DoMeal();
14 }
15 }