设计模式系列之 工厂方法模式
定义
定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类。工厂方法让类的实例化推迟到子类中进行。
该定义是对生产者一方的描述,涉及四种角色,包括接口、接口实现类、被实例化的类、抽象产品(隐含)。其中
接口是指工厂接口(广义接口,在java中可以是接口或者抽象类),该接口定义一个生产产品的方法;
接口实现类指工厂接口的实现子类;
实例化的类指生产的产品;
抽象产品指产品的父类。工厂接口中要先给出抽象产品,其子类才能实例化具体的产品;
定义中第二句话实际上给出了工厂方法的功能,即让子类决定要实例化的类是哪一个。此处的“决定”并不是指运行时的决定,而是在编码过程中程序员的决定。工厂子类和产品实例的对应关系在编码时已经确定,执行时选择哪个工厂实例就可以生产对应的产品。举个例子,工厂子类和产品实例的关系好比是火车和目的地的关系,选择了开往北京的火车,那么最终的目的地就是北京,不会是其他地方。
注意,该模式的核心是工厂方法,实际上这个方法是一个抽象方法,用于定义创建产品的规范。
模式结构
从定义可知,除客户端外,该模式包含了四种角色。从两个平行的类层级出发,可对这四种角色分类。
- 相关簇
- 工厂接口和工厂接口实现类属于工厂相关簇,两者之间存在“高低层”的关系
- 抽象产品和产品实例都属于产品相关簇,两者之间存在“高低层”的关系
- 等级
- 工厂接口和抽象产品属于一个等级,属于底层等级,用于定义规范或框架。两者可以相互调用
- 工厂接口实现类和产品实例属于一个等级,属于高层等级,用于实现框架或规范。两者可以相互调用
四种角色对应关系的表格如下,列表示产品簇,行表示同等级
表1 两种平行的类层级
相关簇 | 相关簇 | |
---|---|---|
同等级 | 抽象产品 | 抽象工厂 |
同等级 | 实例产品 | 工厂实现类 |
四种角色对应UML图如下,列表示产品簇,行表示同等级
图一 工厂方法模式UML图
设计模式套路:抽象之间建立依赖关系,形成框架或规范;实例与抽象之间、实例与实例之间中进行实际的交互,实现OO的多态。
代码实践
步骤1 创建抽象产品。产品作为被操作对象,要先于同等级的工厂创建。
public abstract class AbstractProduct {/*** 产品抽象方法*/abstract void productAction();/*** 产品其他公有行为*/public void powerOn(){System.out.println("打开电源");}/*** 产品其他公有行为*/public void lightOn(){System.out.println("打开灯光");}/*** 产品其他公有行为*/public void dance(){System.out.println("开始跳舞");}
}
步骤2 创建工厂接口
public abstract class AbstractFactoryMethod {/*** 工厂方法,也是模式命名的来由* @return*/abstract AbstractProduct createProduct();/*** 方法中用到了工厂方法* 具体工厂方法生产什么产品根据当前类的子类决定*/public void productShow(){/* 依赖倒置。从依赖具体product改为依赖抽象AbstractProduct父类中用到了工厂方法,而工厂方法的实现是在子类中。*/AbstractProduct product= createProduct();product.powerOn();product.lightOn();product.dance();}
}
注意,工厂接口中的工厂方法可以在任一地方使用,包括工厂接口内部。
步骤3 创建产品实例
/**
*小马
*/
public class Horse extends AbstractProduct {@Overridevoid productAction() {System.out.println("小马在奔跑");}
}/**
*小牛
*/
public class cow extends AbstractProduct {@Overridevoid productAction() {System.out.println("小牛在吃草");}
}
步骤4 创建工厂实现类
/**
*小马工厂
*/
public class HorseFactory extends AbstractFactoryMethod {@OverrideAbstractProduct createProduct() {return new Horse();}
}/**
*小牛工厂
*/
public class CowFacory extends AbstractFactoryMethod {@OverrideAbstractProduct createProduct() {return new Cow();}
}
以上代码的类图如下
图二 不包含依赖关系 图三 包含依赖关系
特点
优点
- 避免了简单工厂的三个缺点,即符合开闭原则、可以使用继承结构、避免因创建功能集中的工厂出错使系统瘫痪。
- 通过工厂名即可获取对应产品,不必关系产品的创建过程。
缺点
- 抽象方法只能生产一种产品
- 类爆炸问题
- 提高了系统的抽象性和理解难度
满足的设计原则
满足开闭原则
在简单工厂模式中,一旦需要生产新产品,都需要修改正在运行的历史代码,这不符合开闭原则。
在工厂方法模式中,添加新产品时,不需要修改正在运行的历史代码。只需要新产品实现抽象产品,同时添加一个对应的实现抽象工厂的产品工厂
满足依赖倒置原则
在不使用工厂方法模式的代码结构中,产品消费者依赖产品生产者,即高层依赖低层。
使用工厂方法模式后,产品消费者依赖抽象产品(业务关注产品提供的服务),具体的产品生产者也依赖抽象产品(抽象产品规定了产品该怎么生产,是规范),此时发生了依赖倒置(产品生产者(低层)依赖抽象产品(高层))
适用场景
同简单工厂模式类似,首先是需要创建产品的场景。特别地,当需要生成的产品不多且不会增加,一个具体工厂类就可以完成任务时,可删除抽象工厂类。这时工厂方法模式将退化到简单工厂模式。
- 客户只知道产品的工厂名,不知道具体的产品名。譬如TCL电视工厂、小米手机工厂。
- 客户不关系产品创建过程,需要时再动态指定。可将具体工厂类的类名存储在配置文件或数据库中。
参考资料
- 工厂方法模式(详解版)
- Head First 设计模式(中文版)
- 工厂方法模式(Factory Method)-最易懂的设计模式解析
- 工厂方法
设计模式系列之 工厂方法模式相关推荐
- Java 设计模式之静态工厂方法模式
设计模式系列 创建型设计模式 Java 设计模式之单例模式 Java 设计模式之静态工厂方法模式 Java 设计模式之工厂方法模式 Java 设计模式之抽象工厂模式 Java 设计模式之Builder ...
- 设计模式总结篇系列:工厂方法模式(Factory Method)
工厂方法模式适合于对实现了同一接口或继承了同一父类的一些类进行实例的创建.一般是通过定义一个工厂类,并在其方法中实现对具有上述特点的类对象的创建. 根据具体产生类对象的方法定义形式,又可以将其分为普通 ...
- Java设计模式菜鸟系列(四)工厂方法模式建模与实现
转载请注明出处:http://blog.csdn.net/lhy_ycu/article/details/39760895 工厂方法模式(Factory Method) 工厂方法:顾名思义,就是调用工 ...
- 手撕设计模式之「工厂方法模式」(Java描述)
前言 工厂方法模式是对简单工厂模式的改进,它通过对工厂类进行抽象形成一个抽象工厂接口,再让具体的工厂负责对应产品的创建,使得在增加产品的场景中也满足"开闭原则".希望通过本文的学习 ...
- 设计模式学习-工厂方法模式
在上文(设计模式学习-简单工厂模式)的模拟场景中,我们用简单工厂模式实现了VISA和MASTERARD卡的刷卡处理,系统成功上线并运行良好,突然有一天老大跑来说,我们的系统需要升级,提供对一般银联卡的 ...
- 设计模式----创建型设计模式(单例模式、工厂方法模式、构建者模式)
创建型设计模式 单例模式(Singleton Pattern) 单例模式介绍 代码演示 饿汉式(静态常量) 饿汉式(静态代码块) 懒汉式(线程不安全) 懒汉式(线程安全,同步方法) 懒汉式(线程安全, ...
- 【设计模式学习】工厂方法模式
cpp学习设计模式:工厂方法模式 在学习工厂方法模式之前,先回忆前面学的简单工厂模式: 简单工厂模式就是将对象的创建和逻辑的判断都交给了一个工厂类去做,这样做的优点是客户端不需要知道具体产品类的类名和 ...
- 设计模式C++实现——工厂方法模式
模式定义: 工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个. 工厂方法让类把实例化推迟到子类. 模式结构: Creator是一个类,它实现了全部操纵产品的方法,但不实现工厂方法 ...
- 设计模式读书笔记-----工厂方法模式
一.问题 在前一章<设计模式读书笔记-----简单工厂模式>中通过披萨的实例介绍了简单工厂模式.在披萨实例中,如果我想根据地域的不同生产出不同口味的披萨,如纽约口味披萨,芝加哥口味披萨.如 ...
最新文章
- Fertility of Soils:根系C/P计量比影响水稻残根周际酶活的时空动态分布特征
- Ural(Timus) 1146. Maximum Sum
- 棉花糖主机送mysql_mysql操作
- Idea配置spark环境及编写scala代码
- [转]DML DDL DCL 语言的区别
- 动态规划算法——最长上升子序列
- mpvue 小程序下拉刷新 三个点那种
- WP7游戏开发:TweeJump(cocos2d-xna)
- 站长吧asp工具设置_网站更换域名需要怎么办?网站更换域名如何设置?
- mui mui.plusReady() 事件中的变量问题;
- 把音频中的某个人声去掉_如何把音乐文件里的人声去掉只保留伴奏
- python 图像倾斜校正_图像矫正原理说明
- 新老系统迁移及整合方案
- 培训linux系统下载,非常好的Linux培训教程集合下载
- java程序笑脸怎么打_Java程序运行后出现一张笑脸,鼠标点击一次则变成哭脸,再点击一次又变成笑脸,依次轮换。...
- CSR8675项目实战:BlueBrowsing蓝牙播放器
- 《转》vue-cli的webpack模板项目配置文件注释
- [Java] Comparator接口/compare方法的介绍与使用
- Zotero使用TIPS
- 2022 年超过 27 个最流行的计算机视觉应用程序和用例