自大学课程初识设计模式以来,就越发觉得有必要系统学习一下设计模式。
刚好在实习前准备期间课比较少,抽出一点时间整理一下记一些笔记,复制粘贴比较多。
笔记比较适合学习过设计模式的同学。
Factory Method Pattern(工厂方法模式)
学习链接:极客学院Wiki_Java设计模式之创造型模式
另外感谢刘伟博士,学习设计模式可以看刘伟博士的博客,很详尽。
刘伟技术博客
工厂方法模式的适用范围
(1) 产品增多,工厂类变得庞大责任变重。系统扩展不灵活,如果增加新类型的产品,必须修改静态工厂方法的业务逻辑,违反了“开闭原则”;
其实就是简单工厂的缺陷所在,在简单工厂模式的前提下进行的改进。如果产品很多,那么业务逻辑变得复杂,会使得工厂类变得过于庞大,此时需要修改成工厂方法模式,将责任分散,并且也不再使用静态工厂方法。
(2) 产品基本无关,不存在“产品族”(产品族的有关概念见工厂三兄弟(3))
当出现相关产品产生时,需要一个工厂生产几种相关产品,这时候工厂方法模式就不在适用,需要适用抽象工厂模式。
(3) 客户端不知道它所需要的对象的类。
在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建,可将具体工厂类的类名存储在配置文件或数据库中。
应用实例
在javaAPI中的应用实例有大家都不陌生的iterator迭代器:
List l = new ArrayList();
l.add("aa");
l.add("bb");
l.add("cc");
for (Iterator iter = l.iterator(); iter.hasNext();) {
String str = (String) iter.next();
System.out.println(str);
}
/* 迭代器用于while循环 */
Iterator iter = l.iterator();
while (iter.hasNext()) {
String str = (String) iter.next();
System.out.println(str);
}
在上述例子中,并没有出现new Iterator,而是由List实例调用了iterator()方法创建了Iterator实例。这里List就相当于工厂(具体工厂),iterator就是产品(当然是抽象产品)。客户端需要list的iterator但是它不需要知道如何创建这样的iterator。
工厂方法模式如何实现
角色
Product(抽象产品)
它是定义产品的接口,是工厂方法模式所创建对象的超类型,也就是产品对象的公共父类。
ConcreteProduct(具体产品)
它实现了抽象产品接口,某种类型的具体产品由专门的具体工厂创建,具体工厂和具体产品之间一一对应。
Factory(抽象工厂)
在抽象工厂类中,声明了工厂方法(Factory Method),用于返回一个产品。抽象工厂是工厂方法模式的核心,所有创建对象的工厂类都必须实现该接口。
ConcreteFactory(具体工厂)
它是抽象工厂类的子类,实现了抽象工厂中定义的工厂方法,并可由客户端调用,返回一个具体产品类的实例。
类图
与简单工厂模式有什么区别?发现已经不是一个工厂生产所有的产品而是一个具体工厂生产一种产品。所有的工厂都继承自一个抽象的父类,这样有什么好处,子类中的工厂方法体可以确定了,它要生产的东西已经是固定的。因此在客户端中只需要关心工厂类即可:需要哪类产品,实例化该产品的工厂,实现了客户端和产品的分离。当然,如果调用了工厂中的工厂方法获得的实例还是在客户端中,如果需要客户端和产品的完全分离,一般会出现使用工厂方法的隐藏。
在工厂类中直接调用产品的业务方法,所以客户端就不需要获取产品实例就能调用产品逻辑了(前提是产品方法比较少)。
工厂方法模式的优缺点
主要优点
(1) 在工厂方法模式中,工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节,用户只需要关心所需产品对应的工厂,无须关心创建细节,甚至无须知道具体产品类的类名。
(2) 基于工厂角色和产品角色的多态性设计是工厂方法模式的关键。它能够让工厂可以自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部。工厂方法模式之所以又被称为多态工厂模式,就正是因为所有的具体工厂类都具有同一抽象父类。
(3) 使用工厂方法模式的另一个优点是在系统中加入新产品时,无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其他的具体工厂和具体产品,而只要添加一个具体工厂和具体产品就可以了,这样,系统的可扩展性也就变得非常好,完全符合“开闭原则”。
主要缺点
(1) 在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销。
(2) 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到 DOM、反射等技术,增加了系统的实现难度。
练习
题目
在简单工厂模式的问题的基础上进行修改。
实现代码
抽象产品和具体产品都不修改。
将工厂类改为抽象父类
工厂类:Factory.java
package com.joy;
public abstract class Factory {
public abstract Shape getShape();
}
同时增加具体工厂。
具体工厂:CircleFactory.java
package com.joy;
public class CircleFactory extends Factory{
@Override
public Shape getShape() {
return new Circle();
}
}
具体工厂:RectangleFactory.java
package com.joy;
public class RectangleFactory extends Factory{
@Override
public Shape getShape() {
return new Rectangle();
}
}
具体工厂:TriangleFactory.java
package com.joy;
public class CircleFactory extends Factory{
@Override
public Shape getShape() {
return new Triangle();
}
}
客户端:FactoryMethodDemo.java
package com.joy;
public class FactoryMethodDemo {
public static void main(String[] args) {
Factory f;
System.out.println("*获取指定形状");
f = new CircleFactory();
Shape shape = f.getShape();
System.out.println("*控制绘制");
shape.draw();
System.out.println("*控制擦除");
shape.erase();
}
}
运行结果
总结
工厂方法相比简单工厂,将产品和客户端分离出来,客户端直接面向的是各个工厂。工厂方法模式应用十分广泛,也很实用,当然也不仅仅是那么简单的例子,以后碰到例子会归纳放到文章后面。