一、概述
1.1使用场景。
在任何需要生成复杂对象的地方,都可以使用工厂方法模式, 直接用new可以完成的不需要用工厂模式。个人理解,重点就是这个复杂 (构造函数有很多参数)和是否可以直接用new。
二、工厂模式的五种写法
2.1简单的静态工厂模式。
/**
* 图形抽象类
*/
public abstract class Shap {
/**
* 描述形状(画画)
*/
public abstract void draw();
}
/**
* 圆形类
*/
public class Circle extends Shap {
public void draw() {
System.out.println("这是圆形...");
}
}
/**
* 长方形类
*/
public class Rectangle extends Shap {
public void draw() {
System.out.println("这是长方形...");
}
}
/**
* 正方形类
*/
public class Square extends Shap {
public void draw() {
System.out.println("这是正方形...");
}
}
/**
* 简单静态工厂类
*/
public class SimpleStaticShapFactory {
// 长方形
public static final int TYPE_RECTANGLE = 1;
// 正方形
public static final int TYPE_SQUARE = 2;
// 圆形
public static final int TYPE_CIRCLE = 3;
public static Shap createShap(int type) {
switch (type) {
case TYPE_RECTANGLE:
return new Rectangle();
case TYPE_SQUARE:
return new Square();
case TYPE_CIRCLE:
default:
return new Circle();
}
}
}
/**
* 测试类
*/
public class Test {
public static void main(String[] args) {
// 创建实例
Shap shap = SimpleStaticShapFactory.createShap(SimpleStaticShapFactory.TYPE_CIRCLE);
shap.draw();
// 创建实例
Shap shap2 = SimpleStaticShapFactory.createShap(SimpleStaticShapFactory.TYPE_SQUARE);
shap2.draw();
}
}
// 运行结果
这是圆形...
这是正方形...
特点:
(1)它是一个具体的类,非接口抽象类。有一个重要的createShap()方法,利用if或者 switch创建产品并返回。
(2)createShap()方法通常是静态的,所以也称之为静态工厂。
缺点:
(1)扩展性差(我想增加一种图形,除了新增一个图形类,还需要修改工厂类方法)。
(2)不同的图形类需要不同额外参数的时候不支持。
2.2基于反射的简单工厂模式。
/**
* 工厂类
*/
public class ReflectFactory {
/**
* @param clzzz Class对象
* @return 实例对象
*/
public static <T extends Shap> T createShap(Class<T> clzz) {
T instance = null;
try {
instance = (T) Class.forName(clzz.getName()).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return instance;
}
}
/**
* 测试类
*/
public class Test {
public static void main(String[] args) {
//创建实例
Shap shap = ReflectFactory.createShap(Rectangle.class);
shap.draw();
//创建实例
Shap shap2 = ReflectFactory.createShap(Square.class);
shap2.draw();
}
}
// 运行结果
这是长方形...
这是正方形...
特点:
(1)它也是一个具体的类,非接口抽象类。但它的createShap()方法,是利用反射机制生成对象返回,好处是增加一种产品时,不需要修改createShap()的代码。
缺点:
(1)这种写法粗看牛逼,细想之下,不谈reflection的效率还有以下问题。
(2)个人觉得不好,因为Class.forName(clzz.getName()).newInstance()调用的是无参构造函数生成对象,它和new Object()是一样的性质,而工厂方法应该用于复杂对象的初始化 ,当需要调用有参的构造函数时便无能为力了,这样像为了工厂而工厂。
(3)不同的产品需要不同额外参数的时候 不支持。
2.3多方法静态工厂模式(常用)。
/**
* 工厂类
*/
public class MultipartStaticFactory {
/**
* 创建圆形
* @return Circle
*/
public static Shap createCircle() {
return new Circle();
}
/**
* 创建长方形
* @return Rectangle
*/
public static Shap createRectangle() {
return new Rectangle();
}
/**
* 创建正方形
* @return Square
*/
public static Shap createSquare() {
return new Square();
}
}
/**
* 测试类
*/
public class Test {
public static void main(String[] args) {
// 创建实例
Shap shap = MultipartStaticFactory.createCircle();
shap.draw();
// 创建实例
Shap shap2 = MultipartStaticFactory.createSquare();
shap2.draw();
}
}
//运行结果
这是圆形...
这是正方形...
特点:
(1)使用前面两张方法实现的工厂,都有一个缺点:不同的类需要不同额外参数的时候不支持。
(2) 如果使用时传递的type、Class出错,将不能得到正确的对象,容错率不高。 而多方法的工厂模式为不同产品,提供不同的生产方法,使用时需要哪种产品就调用该种产品的方法,使用方便、容错率高。
(3)这个例子可以感受到工厂方法的魅力了吧:方便创建同种类型的复杂参数对象。
此种设计模式还在Executors源码用到:
Executor是生成各种线程池的工厂,其采用的便是多方法静态工厂模式。
/**
* java.util.concurrent.Executors类部分源码
*/
public class Executors {
/**
* ThreadPoolExecutor方法有5个参数,有部分参数写法固定,而工厂类就只需要传入一个参数
*/
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
/**
* 假如JDK想增加创建ForkJoinPool类的方法,只想配置parallelism参数,便在类里增加一个如下的方法
*/
public static ExecutorService newWorkStealingPool(int parallelism) {
return new ForkJoinPool
(parallelism,
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
public static ExecutorService newWorkStealingPool() {
return new ForkJoinPool
(Runtime.getRuntime().availableProcessors(),
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
2.4普通工厂模式。
/**
* 抽象工厂类,生产图形
*/
public abstract class ShapFactory {
abstract Shap createShap();
}
/**
* 具体工厂子类,圆形工厂
*/
public class CircleFactory extends ShapFactory {
@Override
Shap createShap() {
return new Circle();
}
}
/**
* 具体工厂子类,长方形工厂
*/
public class RectangleFactory extends ShapFactory {
@Override
Shap createShap() {
return new Rectangle();
}
}
/**
* 具体工厂子类,正方形工厂
*/
public class SquareFactory extends ShapFactory {
@Override
Shap createShap() {
return new Square();
}
}
/**
* 测试类
*/
public class Test {
public static void main(String[] args) {
// 创建圆形工厂
ShapFactory shapFactory = new CircleFactory();
// 创建圆形实例
Shap circle = shapFactory.createShap();
circle.draw();
// 创建正方形工厂
ShapFactory shapFactory2 = new SquareFactory();
// 创建正方形实例
Shap Square = shapFactory2.createShap();
Square.draw();
}
}
// 运行结果
这是圆形...
这是正方形...
特点:
(1)普通工厂就是把简单工厂【2.1简单的静态工厂模式】中具体的工厂类,划分成两层:抽象工厂层和具体的工厂子类层。
(2)可以看出,普通工厂模式特点:不仅仅做出来的产品要抽象, 工厂也应该需要抽象。
(3)工厂方法使一个产品类的实例化延迟到其具体工厂子类。
(4)工厂方法的好处就是更拥抱变化。当需求变化,只需要增删相应的类,不需要修改
(5)而简单工厂【2.1简单的静态工厂模式】需要修改工厂类的createShap()方法和具体图形类,多方法静态工厂【2.3多方法静态工厂模式】模式需要增加一个静态方法和具体图形类。
缺点:
(1) 引入抽象工厂层后,每次新增一个具体图形类,也要同时新增一个具体工厂类。
2.4抽象工厂模式。
/**
* 饮料抽象类
*/
public abstract class Drink {
/**
* 描述每种饮料多少钱
*/
public abstract void prices();
}
/**
* 可乐类
*/
public class Cola extends Drink {
@Override
public void prices() {
System.out.println("可口可乐3.0元...");
}
}
/**
* 牛奶类
*/
public class Milk extends Drink {
@Override
public void prices() {
System.out.println("牛奶2.5元...");
}
}
/**
* 抽象工厂类
*/
public abstract class DrinkAndDraw {
/**
* 喝饮料
*
* @return
*/
public abstract Drink doDrink();
/**
* 画画
*
* @return
*/
public abstract Shap doDraw();
}
/**
* 具体工厂类1
*/
public class FactoryOne extends DrinkAndDraw {
/**
* 喝可乐
*/
@Override
public Drink doDrink() {
return new Cola();
}
/**
* 画圆形
*/
@Override
public Shap doDraw() {
return new Circle();
}
}
/**
* 具体工厂类2
*/
public class FactoryTwo extends DrinkAndDraw {
/**
* 喝牛奶
*/
@Override
public Drink doDrink() {
return new Milk();
}
/**
* 画正方形
*/
@Override
public Shap doDraw() {
return new Square();
}
}
/**
* 测试类
*/
public class Test {
public static void main(String[] args) {
// 上幼儿园的小明一边喝可口可乐,一边在画圆形
// 创建工厂1的实例,返回的方法是可乐实例的方法和圆形实例的方法
DrinkAndDraw drinkAndDraw = new FactoryOne();
// 调用可乐实例的喝可乐方法
drinkAndDraw.doDrink().prices();
// 调用圆形实例的画圆形方法
drinkAndDraw.doDraw().draw();
// 上幼儿园的小明一边喝牛奶,一边在画正方形
DrinkAndDraw drinkAndDraw2 = new FactoryTwo();
drinkAndDraw2.doDrink().prices();
drinkAndDraw2.doDraw().draw();
}
}
// 运行结果
可口可乐3.0元...
这是圆形...
牛奶2.5元...
这是正方形...
特点:
(1)将工厂也抽象了,在使用时,工厂和产品都是面向接口编程。
缺点:
(1)如果每次扩展新抽象类,比如读书抽象类,那就需要新加具体实现类如读小说还是读教科书,然后需要修改抽象工厂类,而后所有的具体工厂子类,都被牵连,需要同步被修改。