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. 每天学习一个设计模式(八):创建型之抽象工厂模式

    目录 一.基本概念 二.通俗解释 三.应用场景 1.使用简单工厂模式的解决方案 2.抽象工厂模式 抽象工厂模式结构 在什么情况下应当使用抽象工厂模式 抽象工厂模式的起源 四.抽象工厂模式的优缺点 抽象 ...

  2. 设计模式详解(四)抽象工厂模式

    文章目录 1. 简介 2. 代码实例 3. 抽象工厂的优缺点 1. 简介 定义:为创建一组相关或相互依赖的对象提供一个接口,而且无须指定它们的具体类 与工厂方法模式不同(工厂方法的升级,在工厂方法模式 ...

  3. JAVA设计模式是个什么玩意儿_02_抽象工厂模式

    1. 定义 2. 思路 抽象工厂模式是工厂方法模式的进一步抽象,为创建一组相关或相互依赖的对象提供一个接口,无需指定它们的具体类.抽象工厂通常用于创一族产品,并且这组产品分不同的等级,不同的工厂生产不 ...

  4. 浅谈设计模式之单例模式、适配器模式、抽象工厂模式

    带你读懂几种常见的设计模式 第三弹 本文已经是设计模式系列的第三篇文章了,今天来讲讲单例模式.抽象工厂模式和适配器模式. 1.单例模式 单例模式让一个类最多只有一个实例.具体的做法是: 让类的构造方法 ...

  5. 设计模式(三):旅行的角度理解抽象工厂模式

    之前的文章介绍了工厂模式,这篇文章介绍工厂模式的强化版本抽象工厂模式(Abstract Factory Pattern) . 我们知道工厂模式是用于在延迟创建具体的对象,抽象工厂模式可以看作是面向工厂 ...

  6. 设计模式系列(创建型模式)之三单例模式

    单例模式 单例模式指一个类只有一个实例,且该类能自行创建这个实例的一种模式. 单例模式有 3 个特点: 单例类只有一个实例对象: 该单例对象必须由单例类自行创建: 单例类对外提供一个访问该单例的全局访 ...

  7. 【设计模式系列】行为型之责任链模式

    目录 背景 理论 实践 总结 背景 责任链模式,小编在自己项目迭代过程也参与了维护和设计实现:本篇博文主要针对该设计模型进行总结和实践: 简单举例日常生活或者工作中能够接触的责任链模式场景:采购审批流 ...

  8. 设计模式(20):创建型-抽象工厂模式(Abstract Factory)

    设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性. 毫无疑问,设计模式于 ...

  9. .NET设计模式(3): 抽象工厂模式

    抽象工厂模式(Abstract Factory Pattern) 引入: 在前面介绍的两个创建型模式里面,我们解决的都是有关" new"的问题,用它们来避免显式指定类创建对象.我写 ...

最新文章

  1. 使用MongoDB存储Docker日志(续)
  2. 【组合数学】生成函数 ( 线性性质 | 乘积性质 )
  3. 【递推DP】POJ1163The Triangle
  4. LCS最长公共子序列
  5. 数据库学习3 Distinct Group By
  6. 【不吹不黑】详解容器技术架构、网络和生态
  7. Spring Boot笔记-自动配置(Spring Boot封装成jar被其他项目引用)
  8. 一分钟获得幸福的99个方式
  9. HTTP请求报文分析
  10. 组件、组件化 与 模块化
  11. ACM算法分类及完成情况
  12. Anaconda下载及详细安装图文教程(基于Windows操作系统)
  13. PAT考试大纲/如何刷pat(想要在pat甲级拿80到90分)
  14. python原生是什么意思_什么是 云原生?
  15. str中的join方法,fromkeys(),set集合,深浅拷贝(重点)
  16. 【金猿产品展】HIO——一体化抖音电商运营投放管理平台
  17. AjaxPro2 方法未定义,对象不支持此方法或属性,解决办法
  18. 4、Kafka API实战
  19. sv基础-数据类型(一)
  20. ARM裸板调试之串口打印及栈初步分析

热门文章

  1. 侯捷C++复现 :explicit在构造函数中
  2. 小鑫の日常系列故事(六)——奇遇记 (sdut oj)
  3. 解决Mybatis结合db2时sql语句换行出现的问题
  4. 系统架构设计——高可扩展性架构
  5. 双进程有名管道通信应用实例
  6. 关于Https的加密过程理解
  7. Lift Splat Shoot Encoding Images from Arbitrary Camera Rigs by Implicitly Unprojecting to 3D 论文阅读笔记
  8. 【vue】 前端 基于 vue-simple-uploader 实现大文件断点续传和分片上传
  9. EN 13950隔热/隔音合成石膏灰泥墙板—CE认证
  10. linux卸载桌面Ubuntu,Ubuntu 10.04 安装卸载KDE桌面之折腾记