什么是工厂方法模式?
工厂方法模式的定义:
Define an interface for creating an object,but let subclasses decide which class to instantiate.Factory Method lets a class defer instantiation to subclasses.(定义一个用于创建对象的 接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。) |
工厂方法模式的通用类图如图8-1所示:
图8-1:工厂方法模式通用类图
在工厂方法模式中,抽象产品类Product负责定义产品的共性,实现对事物最抽象的定 义;Creator为抽象创建类,也就是抽象工厂,具体如何创建产品类是由具体的实现工厂 ConcreteCreator完成的。
工厂方法模式的扩展比较多,以下是比较通用的工厂方法模式实例:
- 抽象产品类:
<span style="color:#141418"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#a626a4">public</span> <span style="color:#a626a4">abstract</span> <span style="color:#a626a4">class</span> <span style="color:#4078f2">Product</span> {
<span style="color:#a0a1a7"><em>// 产品类的公共方法</em></span>
<span style="color:#a626a4">public</span> <span style="color:#a626a4">void</span> <span style="color:#4078f2">method1</span>() {
<span style="color:#a0a1a7"><em>// 业务逻辑处理</em></span>
}
<span style="color:#a0a1a7"><em>// 抽象方法</em></span>
<span style="color:#a626a4">public</span> <span style="color:#a626a4">abstract</span> <span style="color:#a626a4">void</span> <span style="color:#4078f2">method2</span>();
}
</code></span></span>
- 具体产品类:
具体的产品类可以有多个,都继承于抽象产品类。
<span style="color:#141418"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#a626a4">public</span> <span style="color:#a626a4">class</span> <span style="color:#4078f2">ConcreteProduct1</span> <span style="color:#a626a4">extends</span> <span style="color:#4078f2">Product</span>{
<span style="color:#4078f2">@Override</span>
<span style="color:#a626a4">public</span> <span style="color:#a626a4">void</span> <span style="color:#4078f2">method2</span>() {
<span style="color:#a0a1a7"><em>// TODO Auto-generated method stub</em></span>
}
}
<span style="color:#a626a4">public</span> <span style="color:#a626a4">class</span> <span style="color:#4078f2">ConcreteProduct2</span> <span style="color:#a626a4">extends</span> <span style="color:#4078f2">Product</span>{
<span style="color:#4078f2">@Override</span>
<span style="color:#a626a4">public</span> <span style="color:#a626a4">void</span> <span style="color:#4078f2">method2</span>() {
<span style="color:#a0a1a7"><em>// TODO Auto-generated method stub</em></span>
}
}
</code></span></span>
- 抽象工厂类
抽象工厂类负责定义产品对象的产生。
<span style="color:#141418"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#a626a4">public</span> <span style="color:#a626a4">abstract</span> <span style="color:#a626a4">class</span> <span style="color:#4078f2">Creator</span> {
<span style="color:#a0a1a7"><em>/** 创建一个产品对象,其输入参数类型可以自行设置 * 通常为String、Enum、Class等,当然也可以为空 */</em></span>
<span style="color:#a626a4">public</span> <span style="color:#a626a4">abstract</span> <T <span style="color:#a626a4">extends</span> <span style="color:#4078f2">Product</span>> T <span style="color:#4078f2">createProduct</span>(Class<T> c);
}
</code></span></span>
- 具体工厂类
具体产生一个产品的对象,是由具体的工厂类实现的。
<span style="color:#141418"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#a626a4">public</span> <span style="color:#a626a4">class</span> <span style="color:#4078f2">ConcreteCreator</span> {
<span style="color:#a626a4">public</span> <T <span style="color:#a626a4">extends</span> <span style="color:#4078f2">Product</span>> T <span style="color:#4078f2">createProduct</span>(Class<T> c) {
<span style="color:#986801">Product</span> <span style="color:#986801">product</span> <span style="color:#ab5656">=</span> <span style="color:#0184bb">null</span>;
<span style="color:#a626a4">try</span> {
product = (Product) Class.forName(c.getName()).newInstance();
} <span style="color:#a626a4">catch</span> (Exception e) {
<span style="color:#a0a1a7"><em>// 异常处理</em></span>
}
<span style="color:#a626a4">return</span> (T) product;
}
}
</code></span></span>
- 场景类:
<span style="color:#141418"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#a626a4">public</span> <span style="color:#a626a4">class</span> <span style="color:#4078f2">Client</span> {
<span style="color:#a626a4">public</span> <span style="color:#a626a4">static</span> <span style="color:#a626a4">void</span> <span style="color:#4078f2">main</span>(String[] args) {
<span style="color:#986801">Creator</span> <span style="color:#986801">creator</span> <span style="color:#ab5656">=</span> <span style="color:#a626a4">new</span> <span style="color:#4078f2">ConcreteCreator</span>();
<span style="color:#986801">Product</span> <span style="color:#986801">product</span> <span style="color:#ab5656">=</span> creator.createProduct(ConcreteProduct1.class);
<span style="color:#a0a1a7"><em>/** 继续业务处理 */</em></span> }
}
</code></span></span>
Why工厂方法模式?
使用工厂方法模式有以下优点:
- 良好的封装性,代码结构清晰。一个对象创建是有条件约束的,如一个调用者需 要一个具体的产品对象,只要知道这个产品的类名(或约束字符串)就可以了,不用知道创 建对象的艰辛过程,降低模块间的耦合。
- 优秀的扩展性:在增加产品类的情况下,只要适当地修改具体 的工厂类或扩展一个工厂类,就可以完成“拥抱变化”。
- 屏蔽产品类的具体细节:产品类的实现如何变化,调用者都不需要关 心,它只需要关心产品的接口,只要接口保持不变,系统中的上层模块就不要发生变化。因 为产品类的实例化工作是由工厂类负责的,一个产品对象具体由哪一个产品生成是由工厂类 决定的。
- 解耦:高层模块值需要知道产品的抽象类,其他的实现类都不用关心,符合迪米特法则;也符合依赖倒置原则,只依赖产品类的抽象;同样符合里氏替换原则,使用产品子类替换产品父类。
简单工厂模式
简单工厂模式并不是一个标准的设计模式,但是在实际开发中是非常有用的。
简单工厂模式(Simple Factory Pattern):定义一个工厂类,它可以根据参数的不同返回不同类的 实例,被创建的实例通常都具有共同的父类。
以女娲造人为例:女娲在不同的场景下分别造出了白种人、黑种人、黄种人。
类图如下:
图8-2:简单工厂模式类图
- 简单工厂模式中的工厂类
<span style="color:#141418"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#a626a4">public</span> <span style="color:#a626a4">class</span> <span style="color:#4078f2">HumanFactory</span> {
<span style="color:#a626a4">public</span> <span style="color:#a626a4">static</span> <T <span style="color:#a626a4">extends</span> <span style="color:#4078f2">Human</span>> T <span style="color:#4078f2">createHuman</span>(Class<T> c) {
<span style="color:#a0a1a7"><em>// 定义一个生产出的人种</em></span>
<span style="color:#986801">Human</span> <span style="color:#986801">human</span> <span style="color:#ab5656">=</span> <span style="color:#0184bb">null</span>;
<span style="color:#a626a4">try</span> {
<span style="color:#a0a1a7"><em>// 产生一个人种</em></span>
human = (Human) Class.forName(c.getName()).newInstance();
} <span style="color:#a626a4">catch</span> (Exception e) {
System.out.println(<span style="color:#50a14f">"人种生成错误!"</span>);
}
<span style="color:#a626a4">return</span> (T) human;
}
}
</code></span></span>
- 人类:
<span style="color:#141418"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#a626a4">public</span> <span style="color:#a626a4">interface</span> <span style="color:#4078f2">Human</span> {
<span style="color:#a0a1a7"><em>//每个人种的皮肤都有相应的颜色 </em></span>
<span style="color:#a626a4">public</span> <span style="color:#a626a4">void</span> <span style="color:#4078f2">getColor</span>();
<span style="color:#a0a1a7"><em>//人类会说话 </em></span>
<span style="color:#a626a4">public</span> <span style="color:#a626a4">void</span> <span style="color:#4078f2">talk</span>();
}
</code></span></span>
- 黄色人种类:
<span style="color:#141418"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#a626a4">public</span> <span style="color:#a626a4">class</span> <span style="color:#4078f2">BlackHuman</span> <span style="color:#a626a4">implements</span> <span style="color:#4078f2">Human</span>{
<span style="color:#4078f2">@Override</span>
<span style="color:#a626a4">public</span> <span style="color:#a626a4">void</span> <span style="color:#4078f2">getColor</span>() {
System.out.println(<span style="color:#50a14f">"黑色人种的皮肤颜色是黑色的!"</span>);
}
<span style="color:#4078f2">@Override</span>
<span style="color:#a626a4">public</span> <span style="color:#a626a4">void</span> <span style="color:#4078f2">talk</span>() {
System.out.println(<span style="color:#50a14f">"Hey!Man."</span>);
}
}
</code></span></span>
- 白色人种类:
<span style="color:#141418"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#a626a4">public</span> <span style="color:#a626a4">class</span> <span style="color:#4078f2">WhiteHuman</span> <span style="color:#a626a4">implements</span> <span style="color:#4078f2">Human</span>{
<span style="color:#4078f2">@Override</span>
<span style="color:#a626a4">public</span> <span style="color:#a626a4">void</span> <span style="color:#4078f2">getColor</span>() {
System.out.println(<span style="color:#50a14f">"白肤色"</span>);
}
<span style="color:#4078f2">@Override</span>
<span style="color:#a626a4">public</span> <span style="color:#a626a4">void</span> <span style="color:#4078f2">talk</span>() {
System.out.println(<span style="color:#50a14f">"Calm down!Guy!"</span>);
}
}
</code></span></span>
- 黄色人种类:
<span style="color:#141418"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#a626a4">public</span> <span style="color:#a626a4">class</span> <span style="color:#4078f2">YellowHuman</span> <span style="color:#a626a4">implements</span> <span style="color:#4078f2">Human</span>{
<span style="color:#4078f2">@Override</span>
<span style="color:#a626a4">public</span> <span style="color:#a626a4">void</span> <span style="color:#4078f2">getColor</span>() {
System.out.println(<span style="color:#50a14f">"黄土地的颜色!"</span>);
}
<span style="color:#4078f2">@Override</span>
<span style="color:#a626a4">public</span> <span style="color:#a626a4">void</span> <span style="color:#4078f2">talk</span>() {
System.out.println(<span style="color:#50a14f">"五湖四海皆兄弟!"</span>);
}
}
</code></span></span>
- 女娲造人场景类:
<span style="color:#141418"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#a626a4">public</span> <span style="color:#a626a4">class</span> <span style="color:#4078f2">NvWa</span> {
<span style="color:#a626a4">public</span> <span style="color:#a626a4">static</span> <span style="color:#a626a4">void</span> <span style="color:#4078f2">main</span>(String[] args) {
<span style="color:#a0a1a7"><em>// 女娲第一次造人,火候不足,于是白色人种产生了</em></span>
System.out.println(<span style="color:#50a14f">"--造出的第一批人是白色人种--"</span>);
<span style="color:#986801">Human</span> <span style="color:#986801">whiteHuman</span> <span style="color:#ab5656">=</span> HumanFactory.createHuman(WhiteHuman.class);
whiteHuman.getColor();
whiteHuman.talk();
<span style="color:#a0a1a7"><em>// 女娲第二次造人,火候过足,于是黑色人种产生了</em></span>
System.out.println(<span style="color:#50a14f">"\n--造出的第二批人是黑色人种--"</span>);
<span style="color:#986801">Human</span> <span style="color:#986801">blackHuman</span> <span style="color:#ab5656">=</span> HumanFactory.createHuman(BlackHuman.class);
blackHuman.getColor();
blackHuman.talk(); <span style="color:#a0a1a7"><em>// 第三次造人,火候刚刚好,于是黄色人种产生了</em></span>
System.out.println(<span style="color:#50a14f">"\n--造出的第三批人是黄色人种--"</span>);
<span style="color:#986801">Human</span> <span style="color:#986801">yellowHuman</span> <span style="color:#ab5656">=</span> HumanFactory.createHuman(YellowHuman.class);
yellowHuman.getColor();
yellowHuman.talk();
}
}
</code></span></span>
工厂方法模式与IOC/DI
Spring框架对工厂方法模式的应用是非常经典的。
IOC和DI详解
首先看一下什么是IOC和DI:
-
IOC:(InversionofControl)控制反转:
-
DI:(DependencyInjection)依赖注入
带着问题将这两个概念拆解开来:
-
参与者都有谁:
一般有三方参与者,一个是某个对象;一个是IOC/DI的容器;另一个是某个对象的外部资源。
IOC/DI的容器简单点说就是指用来实现IOC/DI功能的一个框架程序;对象的外部资源指的就是对象需要的,但是是从对象外部获取的,都统称资源, -
谁依赖于谁:
某个对象依赖于IOC/DI的容器 -
为什么需要依赖:
对象需要IOC/DI的容器来提供对象需要的外部资源 -
谁注入于谁:
IOC/DI的容器 注入 某个对象 -
到底注入什么:
注入某个对象所需要的外部资源 -
谁控制谁:
IOC/DI的容器控制对象 -
控制什么:
主要是控制对象实例的创建 -
为何叫反转:
反转是相对于正向而言的,所谓正向——常规情况下的应用程序,如果要在A里面使用C,就直接去创建C的对象,也就是说,是在A类中主动去获取所需要的外部资源C,这种情况被称为正向的。所谓反向——就是A类不再主动去获取C,而是被动等待,等待IOC/DI的容器获取一个C的实例,然后反向的注入到A类中。 -
依赖注入和控制反转是同一概念吗?
依赖注入和控制反转是对同一件事情的不同描述,从某个方面讲,就是它们描述的角度不同。依赖注入是从应用程序的角度在描述,可以把依赖注入描述完整点:应用程序依赖容器创建并注入它所需要的外部资源;而控制反转是从容器的角度在描述,描述完整点:容器控制应用程序,由容器反向的向应用程序注入应用程序所需要的外部资源。
IOC/DI对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IOC/DI思想中,应用程序就变成被动的了,被动的等待IOC/DI容器来创建并注入它所需要的资源了。
这么小小的一个改变其实是编程思想的一个大进步,这样就有效的分离了对象和它所需要的外部资源,使得它们松散耦合,有利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。
工厂方法模式和IOC/DI的联系
SpringBean 的创建是典型的工厂模式,IOC容器实现,就是利用一系列的 Bean 工厂,在 Spring 中有许多的 IOC 容器的实现供用户选择和使用,其相互关系如下:
图8-3:Spring IOC容器相互关系
其中 BeanFactory 作为最顶层的一个接口类,它定义了 IOC 容器的基本功能规范,BeanFactory 有三个子类: ListableBeanFactory、HierarchicalBeanFactory 和 AutowireCapableBeanFactory。最终的 默认实现类是 DefaultListableBeanFactory,实现了所有的接口。定义这么多层次的接口是为了适应不同的场景。例如 ListableBeanFactory 接口表示这些 Bean 是可列表的,而 HierarchicalBeanFactory 表示的是这些Bean是有继承关系的 , 也就是每个 Bean 有 可 能 有 父 Bean 。 AutowireCapableBeanFactory 接口定义 Bean 的自动装配规则。这四个接口共同定义了 Bean 的集合、Bean 之间的 关系、以及 Bean 行为。
- 最顶层的BeanFactory接口:
<span style="color:#141418"><span style="background-color:#ffffff"><code class="language-java">ackage org.springframework.beans.factory;
<span style="color:#a626a4">import</span> org.springframework.beans.BeansException;
<span style="color:#a626a4">import</span> org.springframework.core.ResolvableType;
<span style="color:#a626a4">import</span> org.springframework.lang.Nullable;
<span style="color:#a626a4">public</span> <span style="color:#a626a4">interface</span> <span style="color:#4078f2">BeanFactory</span> {
<span style="color:#986801">String</span> <span style="color:#986801">FACTORY_BEAN_PREFIX</span> <span style="color:#ab5656">=</span> <span style="color:#50a14f">"&"</span>;
Object <span style="color:#4078f2">getBean</span>(String var1) <span style="color:#a626a4">throws</span> BeansException;
<T> T <span style="color:#4078f2">getBean</span>(String var1, Class<T> var2) <span style="color:#a626a4">throws</span> BeansException;
Object <span style="color:#4078f2">getBean</span>(String var1, Object... var2) <span style="color:#a626a4">throws</span> BeansException;
<T> T <span style="color:#4078f2">getBean</span>(Class<T> var1) <span style="color:#a626a4">throws</span> BeansException;
<T> T <span style="color:#4078f2">getBean</span>(Class<T> var1, Object... var2) <span style="color:#a626a4">throws</span> BeansException;
<T> ObjectProvider<T> <span style="color:#4078f2">getBeanProvider</span>(Class<T> var1);
<T> ObjectProvider<T> <span style="color:#4078f2">getBeanProvider</span>(ResolvableType var1);
<span style="color:#986801">boolean</span> <span style="color:#4078f2">containsBean</span>(String var1);
<span style="color:#986801">boolean</span> <span style="color:#4078f2">isSingleton</span>(String var1) <span style="color:#a626a4">throws</span> NoSuchBeanDefinitionException;
<span style="color:#986801">boolean</span> <span style="color:#4078f2">isPrototype</span>(String var1) <span style="color:#a626a4">throws</span> NoSuchBeanDefinitionException;
<span style="color:#986801">boolean</span> <span style="color:#4078f2">isTypeMatch</span>(String var1, ResolvableType var2) <span style="color:#a626a4">throws</span> NoSuchBeanDefinitionException;
<span style="color:#986801">boolean</span> <span style="color:#4078f2">isTypeMatch</span>(String var1, Class<?> var2) <span style="color:#a626a4">throws</span> NoSuchBeanDefinitionException;
<span style="color:#4078f2">@Nullable</span>
Class<?> getType(String var1) <span style="color:#a626a4">throws</span> NoSuchBeanDefinitionException;
String[] getAliases(String var1);
}
</code></span></span>