设计模式系列一创建型之(抽象工厂模式)
1、抽象工厂简介
在软件系统中,经常面临着“一系列相互依赖的对象”的创建工作;同时由于需求的变化,往往存在着更多系列对象的创建工作。
- 如何应对这种变化?
- 如何绕过常规的对象的创建方法(new)?
- 如何来避免客户程序和这种“多系列具体对象创建工作”的紧耦合?
然而抽象工厂便可以很好地解决这个问题!
2、意图
提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类。
3、适用性
- 一个系统要独立与它的产品创建、组合和表示时
- 一个系统要由多个产品系列中的一个来配置时
- 当你要强调一系列相关的产品对象的设计以便进行联合使用时
- 当你提供一个产品类库,而只想显示它们的接口而不是实现时
4、结构图
5、应用实例
注:此案例,借鉴网络资料;比较易于学习,所以自己练习了下,在这里也给大家分享了!
中国企业需要一项简单的财务计算:每月月底,财务人员要计算员工的工资。
员工工资 = 基本工资 + 奖金 - 个人所得税 ; 为了简化系统,这里定义员工基本工资:4000
中国企业奖金和个人所得税的计算规则是:
奖金 = 基本工资 * 10%
个人所得税 =(基本工资 + 奖金) * 40%
为此我们要构造一个系统,满足中国企业的需要:(名称Softo)
/// <summary>/// 公用变量基本工资/// </summary>public class Constant{public static double base_salary = 4000;}/// <summary>/// 计算中国个人奖金/// </summary>public class ChinaBonus{public double Calculate(){return Constant.base_salary * 0.1;}}/// <summary>/// 计算个人所得税/// </summary>public class ChinaTax{public double Calculate(){return (Constant.base_salary + Constant.base_salary * 0.1) * 0.4;}}
View Code
客户端调用:
static void Main(string[] args){double bonusValue_China = new ChinaBonus().Calculate();double taxValue_China = new ChinaTax().Calculate();double salary_China = 4000 + bonusValue_China - taxValue_China;Console.WriteLine("China Salary is:" + salary_China);}
View Code
运行结果:
以上满足了中国企业需求,但为了拓展国际市场,需求发生变更;我们要把该系统移植给美国公司使用,员工工资计算规则不变;但是奖金和个人所得税不同于中国,其规则如下:
奖金 = 基本工资 * 15%
个人所得税 = 基本工资 * 5% + 奖金 * 25%
根据现有系统,我们只需要做如下更改:
/// <summary>/// 公用变量基本工资/// </summary>public class Constant{public static double base_salary = 4000;}/// <summary>/// 计算美国个人奖金/// </summary>public class AmericanBonus{public double Calculate(){return Constant.base_salary * 0.1;}}/// <summary>/// 计算美国个人所得税/// </summary>public class AmericanTax{public double Calculate(){return (Constant.base_salary + Constant.base_salary * 0.1) * 0.4;}}
View Code
客户端调用:
static void Main(string[] args){ double bonusValue_American = new AmericanBonus().Calculate();double taxValue_American = new AmericanTax().Calculate();double salary_American = 4000 + bonusValue_American - taxValue_American;Console.WriteLine("American Salary is:" + salary_American);Console.ReadLine();}
View Code
运行结果:
为了以后业务拓展,我们打算把Softo整合为通用系统:
比较以上两个系统,业务规则类发生了变化,客户端调用发生了变化,如果要做通用的就必须保留所有的业务规则模型,在中国与美国之间切换时,只需要修改客户端调用即可。
但是,一个维护性良好的系统应该遵循“开闭原则”。即:封闭对原来代码的修改,开放对原来代码的扩展 (如类的继承,接口的实现)。我们发现不论是中国企业还是美国企业,他们的业务运规则都采用同样的计算接口,于是修改如下:
class Program{static void Main(string[] args){IBonus bonus = new ChinaBonus();double bonusValue_China = bonus.Calculate();ITax tax = new ChinaTax();double taxValue_China = tax.Calculate();double salary_China = 4000 + bonusValue_China - taxValue_China;Console.WriteLine("China Salary is:" + salary_China);Console.ReadLine();}}/// <summary>/// 公用变量基本工资/// </summary>public class Constant{public static double base_salary = 4000;}public interface IBonus{double Calculate();}public interface ITax{double Calculate();}/// <summary>/// 计算中国个人奖金/// </summary>public class ChinaBonus : IBonus{public double Calculate(){return Constant.base_salary * 0.1;}}/// <summary>/// 计算个人所得税/// </summary>public class ChinaTax : ITax{public double Calculate(){return (Constant.base_salary + Constant.base_salary * 0.1) * 0.4;}}/// <summary>/// 计算美国个人奖金/// </summary>public class AmericanBonus : IBonus{public double Calculate(){return Constant.base_salary * 0.1;}}/// <summary>/// 计算美国个人所得税/// </summary>public class AmericanTax : ITax{public double Calculate(){return (Constant.base_salary + Constant.base_salary * 0.1) * 0.4;}}
View Code
运行结果:
然而,上面增加的接口几乎没有解决任何问题,因为当系统的客户在美国和中国企业间切换时,客户端仍然需要修改;
为此增加一个工具类Factory:
public class Factory{public IBonus CreateBonus(){return new ChinaBonus();}public ITax CreateTax(){return new ChinaTax();}}
View Code
客户端调用:
static void Main(string[] args){IBonus bonus = new Factory().CreateBonus();double bonusValue = bonus.Calculate();ITax tax = new Factory().CreateTax();double taxValue = tax.Calculate();double salary = 4000 + bonusValue - taxValue;Console.WriteLine(" Salary is:" + salary);Console.ReadLine();}
View Code
运行结果:
此时,如果我们把该系统移植到美国,只需修改Factory工具类,把ChinaBonus替换为AmericanBonus;ChinaTax替换为AmericanTax即可;其实这也有一个副作用,新建了一个Factory类,并且把修改点转移到该类中,并没有解决根本问题;而且这个工具类可能是专属于美国企业或者中国企业的,名称叫:AmericanFactory,ChineseFactory更合适。那我们该解决这个问题?
此时才引入重点,添加抽象工厂方法:
增加一个静态方法,该方法根据一个配置文件动态地判断应该实例化哪个工厂类;
<?xml version="1.0" encoding="utf-8" ?> <configuration><appSettings><add key="factoryName" value="ChinaFactory"></add></appSettings> </configuration>
/// <summary>/// AbstractFactory/// </summary>public abstract class AbstractFactory{public static AbstractFactory GetInstance(){string factoryName = ConfigurationManager.AppSettings["factoryName"];AbstractFactory instance;switch (factoryName){case "ChinaFactory":instance = new ChinaFactory(); break;case "AmericanFactory":instance = new AmericanFactory(); break;default:instance = null; break;}return instance;}public abstract IBonus CreateBonus();public abstract ITax CreateTax();}public class ChinaFactory : AbstractFactory{public override IBonus CreateBonus(){return new ChinaBonus();}public override ITax CreateTax(){return new ChinaTax();}}public class AmericanFactory : AbstractFactory{public override IBonus CreateBonus(){return new AmericanBonus();}public override ITax CreateTax(){return new AmericanTax();}}
View Code
客户端调用:
static void Main(string[] args){AbstractFactory instanceFac = AbstractFactory.GetInstance();double bonusValue = instanceFac.CreateBonus().Calculate();double taxValue = instanceFac.CreateTax().Calculate();double salary = 4000 + bonusValue - taxValue;Console.WriteLine(" Salary is:" + salary);Console.ReadLine();}
View Code
运行结果:
此时当系统在美国企业和中国企业之间切换时,我们只需要修改配置改为AmericanFactory即可。
相信你看到这里也会赶脚到抽象工厂的强大吧!其实以上解决方案并不是最完美的,不知是否满过你的锐眼?
抽象工厂类中采用了分支判断,一旦业务更加广泛,不只是美国,有拓展至德国,此时我们不但要增加新的业务规则类:德国Tax、德国Bonus分别实现ITax和IBonus接口,新增德国Factory继承自AbstractFactory,而且还要添加AbstractFactory中的case分支,这依然不能满足OCP!至于该如何解决该问题,且看下节分析!
6、总结
- 最后使用抽象工厂模式后,我们会发现客户端完全依赖于抽象类,它不必去理解中国和美国企业具体的业务规则如何实现;面对的只是业务规则接口IBonus和ITax;从而把业务规则与客户端调用完全分离,从而降低耦合度;
- 完完全全地理解抽象工厂模式的意义非常重大,可以说对它的理解是你对OOP理解上升到一个新的里程碑的重要标志。学会了用抽象工厂模式编写框架类,你将理解OOP的精华:面向接口编程。
注:虽然案例是搜集的资料,但通过自己的整理与测试!希望博友能够尊重我的劳动成果,大家互相学习、共同进步!
转载于:https://www.cnblogs.com/tianboblog/p/3979519.html
设计模式系列一创建型之(抽象工厂模式)相关推荐
- 每天学习一个设计模式(八):创建型之抽象工厂模式
目录 一.基本概念 二.通俗解释 三.应用场景 1.使用简单工厂模式的解决方案 2.抽象工厂模式 抽象工厂模式结构 在什么情况下应当使用抽象工厂模式 抽象工厂模式的起源 四.抽象工厂模式的优缺点 抽象 ...
- 设计模式详解(四)抽象工厂模式
文章目录 1. 简介 2. 代码实例 3. 抽象工厂的优缺点 1. 简介 定义:为创建一组相关或相互依赖的对象提供一个接口,而且无须指定它们的具体类 与工厂方法模式不同(工厂方法的升级,在工厂方法模式 ...
- JAVA设计模式是个什么玩意儿_02_抽象工厂模式
1. 定义 2. 思路 抽象工厂模式是工厂方法模式的进一步抽象,为创建一组相关或相互依赖的对象提供一个接口,无需指定它们的具体类.抽象工厂通常用于创一族产品,并且这组产品分不同的等级,不同的工厂生产不 ...
- 浅谈设计模式之单例模式、适配器模式、抽象工厂模式
带你读懂几种常见的设计模式 第三弹 本文已经是设计模式系列的第三篇文章了,今天来讲讲单例模式.抽象工厂模式和适配器模式. 1.单例模式 单例模式让一个类最多只有一个实例.具体的做法是: 让类的构造方法 ...
- 设计模式(三):旅行的角度理解抽象工厂模式
之前的文章介绍了工厂模式,这篇文章介绍工厂模式的强化版本抽象工厂模式(Abstract Factory Pattern) . 我们知道工厂模式是用于在延迟创建具体的对象,抽象工厂模式可以看作是面向工厂 ...
- 设计模式系列(创建型模式)之三单例模式
单例模式 单例模式指一个类只有一个实例,且该类能自行创建这个实例的一种模式. 单例模式有 3 个特点: 单例类只有一个实例对象: 该单例对象必须由单例类自行创建: 单例类对外提供一个访问该单例的全局访 ...
- 【设计模式系列】行为型之责任链模式
目录 背景 理论 实践 总结 背景 责任链模式,小编在自己项目迭代过程也参与了维护和设计实现:本篇博文主要针对该设计模型进行总结和实践: 简单举例日常生活或者工作中能够接触的责任链模式场景:采购审批流 ...
- 设计模式(20):创建型-抽象工厂模式(Abstract Factory)
设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性. 毫无疑问,设计模式于 ...
- .NET设计模式(3): 抽象工厂模式
抽象工厂模式(Abstract Factory Pattern) 引入: 在前面介绍的两个创建型模式里面,我们解决的都是有关" new"的问题,用它们来避免显式指定类创建对象.我写 ...
最新文章
- 使用MongoDB存储Docker日志(续)
- 【组合数学】生成函数 ( 线性性质 | 乘积性质 )
- 【递推DP】POJ1163The Triangle
- LCS最长公共子序列
- 数据库学习3 Distinct Group By
- 【不吹不黑】详解容器技术架构、网络和生态
- Spring Boot笔记-自动配置(Spring Boot封装成jar被其他项目引用)
- 一分钟获得幸福的99个方式
- HTTP请求报文分析
- 组件、组件化 与 模块化
- ACM算法分类及完成情况
- Anaconda下载及详细安装图文教程(基于Windows操作系统)
- PAT考试大纲/如何刷pat(想要在pat甲级拿80到90分)
- python原生是什么意思_什么是 云原生?
- str中的join方法,fromkeys(),set集合,深浅拷贝(重点)
- 【金猿产品展】HIO——一体化抖音电商运营投放管理平台
- AjaxPro2 方法未定义,对象不支持此方法或属性,解决办法
- 4、Kafka API实战
- sv基础-数据类型(一)
- ARM裸板调试之串口打印及栈初步分析
热门文章
- 侯捷C++复现 :explicit在构造函数中
- 小鑫の日常系列故事(六)——奇遇记 (sdut oj)
- 解决Mybatis结合db2时sql语句换行出现的问题
- 系统架构设计——高可扩展性架构
- 双进程有名管道通信应用实例
- 关于Https的加密过程理解
- Lift Splat Shoot Encoding Images from Arbitrary Camera Rigs by Implicitly Unprojecting to 3D 论文阅读笔记
- 【vue】 前端 基于 vue-simple-uploader 实现大文件断点续传和分片上传
- EN 13950隔热/隔音合成石膏灰泥墙板—CE认证
- linux卸载桌面Ubuntu,Ubuntu 10.04 安装卸载KDE桌面之折腾记