工厂方法模式
分类
工厂方法模式分为四种:
- 简单工厂模式
- 多个工厂方法模式
- 静态工厂方法模式
- 工厂方法模式
其中前三种都属于简单工厂模式,又叫做静态工厂方法模式,但不属于23种GOF设计模式之一。
一、简单工厂模式
简单工厂模式就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。
简单工厂模式存在三个组成部分:
工厂角色
在简单工厂模式中,工厂类是一个具体的实现类,在系统设计中工厂类负责实际对象的创建工作。
工厂类(Factory)的特点是:它知道系统中都存在哪些能够创建对象的具体类(ConcreteProduct),也知道该如何将创建的对象,以某种能够屏蔽具体类实现细节的方式(AbstractProduct)提供给所需要的其他角色来使用该对象提供的数据和服务。
抽象产品角色
抽象产品角色是具体的产品的抽象。抽象就是将产品的共性抽取出来,可以直接暴露给客户端(需要使用具体产品的角色)。
抽象产品角色,在实际系统中可以定义为接口或者抽象类。
具体产品角色
具体产品实现类一定是抽象产品类的实现或扩展。
举例:一个发送邮件和短信的例子
关系图:
二者的共同接口:
1 | public interface Sender { |
实现类:
1 | public class MailSender implements Sender { |
1 | public class SmsSender implements Sender { |
工厂类:
1 | public class SendFactory { |
测试类:
1 | public class FactoryTest { |
二、多个工厂方法模式
多个工厂方法模式是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象
关系图:
工厂类:
1 | public class SendFactory { |
测试类:
1 | public class FactoryTest { |
三、静态工厂方法模式
静态工厂方法模式将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可
工厂类:
1 | public class SendFactory { |
测试类:
1 | public class FactoryTest { |
模式应用:以上三种称为简单工厂模式
1 | //在JDK类库中广泛使用了简单工厂模式,如工具类java.text.DateFormat,它用于格式化一个本地日期或者时间 |
模式优缺点:
- 优点
- 将对象的创建和对象本身业务处理分离可以降低系统的耦合度,使得两者修改起来都相对容易
- 在调用工厂类的工厂方法时,由于工厂方法是静态方法,可通过类名直接调用,而且只需要传入一个简单的参数即可
- 在实际开发中,还可以在调用时将所传入的参数保存在XML等格式的配置文件中,修改参数时无须修改任何Java源代码
- 简单工厂模式的要点在于:当你需要什么,只需要传入一个正确的参数,就可以获取你所需要的对象,而无须知道其创建细节
- 缺点
- 简单工厂模式最大的问题在于工厂类的职责相对过重,增加新的产品需要修改工厂类的判断逻辑,这一点与开闭原则是相违背的
- 在简单工厂模式中,只提供了一个工厂类,该工厂类处于对产品类进行实例化的中心位置,它知道每一个产品对象的创建细节,并决定何时实例化哪一个产品类。简单工厂模式最大的缺点是当有新产品要加入到系统中时,必须修改工厂类,加入必要的处理逻辑,这违背了开闭原则
- 在简单工厂模式中,所有的产品都是由同一个工厂创建,工厂类职责较重,业务逻辑较为复杂,具体产品与工厂类之间的耦合度高,严重影响了系统的灵活性和扩展性。而多态工厂类模式可以很好地解决这一问题
四、工厂方法模式
工厂方法模式又称多态工厂(Polymorphic Factory)模式,它属于类创建型模式。在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。
工厂方法模式的结构:
抽象工厂(Creator)角色
是工厂方法模式的核心,与应用程序无关。任何在模式中创建的对象的工厂类必须实现这个接口。
具体工厂(Concrete Creator)角色
这是实现抽象工厂接口的具体工厂类,包含与应用程序密切相关的逻辑,并且受到应用程序调用以创建产品对象。
抽象产品(Product)角色
工厂方法模式所创建的对象的超类型,也就是产品对象的共同父类或共同拥有的接口。
具体产品(Concrete Product)角色
这个角色实现了抽象产品角色所定义的接口。某具体产品有专门的具体工厂创建,它们之间往往一一对应。
抽象工厂类:
1 | public abstract class SendFactory{ |
具体工厂子类:
1 | //邮件工厂 |
实现类:
1 | //邮件实现类 |
测试类:
1 | public class FactoryTest { |
很明显可以看到,SendFactory工厂类变成了SendFactory抽象类,而继承此抽象类的分别是MailSendFactory和SmsSendFactory等等具体的工厂类。
这样做有什么好处呢?很明显,这样做就完全OCP了。如果需要再加入(或扩展)产品类(比如加多个“发微信”)的话就不再需要修改工厂类了,而只需相应的再添加一个继承了工厂抽象类的具体工厂类即可。
模式应用:
1 | //JDBC中的工厂方法(没有多个具体工厂,特例) |
模式优缺点:
- 优点:
- 在工厂方法模式中,工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节,用户只需要关心所需产品对应的工厂,无须关心创建细节,甚至无须知道具体产品类的类名
- 基于工厂角色和产品角色的多态性设计是工厂方法模式的关键。它能够使工厂可以自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部。工厂方法模式之所以又被称为多态工厂模式,是因为所有的具体工厂类都具有同一抽象父类
- 使用工厂方法模式的另一个优点是在系统中加入新产品时,无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其他的具体工厂和具体产品,而只要添加一个具体工厂和具体产品就可以了。这样,系统的可扩展性也就变得非常好,完全符合开闭原则
- 缺点:
- 在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销
- 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度
总结
总体来说,工厂方法模式适合:凡是出现了大量的产品需要创建,并且具有共同的接口时,可以通过工厂方法模式进行创建。
在简单工厂模式的三种模式中,第一种如果传入的字符串有误,不能正确创建对象,第三种相对于第二种,不需要实例化工厂类,所以,大多数情况下,我们会选用第三种——静态工厂方法模式