设计模式系列一创建型之(抽象工厂模式)

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;
        }
    }

客户端调用:

        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);
        }   

运行结果:



以上满足了中国企业需求,但为了拓展国际市场,需求发生变更;我们要把该系统移植给美国公司使用,员工工资计算规则不变;但是奖金和个人所得税不同于中国,其规则如下:

奖金 = 基本工资 * 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;
        }
    }

客户端调用:

       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();

        }

运行结果:



为了以后业务拓展,我们打算把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;
        }
    }

运行结果:



然而,上面增加的接口几乎没有解决任何问题,因为当系统的客户在美国和中国企业间切换时,客户端仍然需要修改;

为此增加一个工具类Factory:

    public class Factory
    {
        public IBonus CreateBonus()
        {
            return new ChinaBonus();
        }
        public ITax CreateTax()
        {
            return new ChinaTax();
        }
    }

客户端调用:

     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();
        }

运行结果:

此时,如果我们把该系统移植到美国,只需修改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();
        }
    }

客户端调用:

       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();
        }

运行结果:

此时当系统在美国企业和中国企业之间切换时,我们只需要修改配置改为AmericanFactory即可。

相信你看到这里也会赶脚到抽象工厂的强大吧!其实以上解决方案并不是最完美的,不知是否满过你的锐眼?

抽象工厂类中采用了分支判断,一旦业务更加广泛,不只是美国,有拓展至德国,此时我们不但要增加新的业务规则类:德国Tax、德国Bonus分别实现ITax和IBonus接口,新增德国Factory继承自AbstractFactory,而且还要添加AbstractFactory中的case分支,这依然不能满足OCP!至于该如何解决该问题,且看下节分析!



6、总结

  • 最后使用抽象工厂模式后,我们会发现客户端完全依赖于抽象类,它不必去理解中国和美国企业具体的业务规则如何实现;面对的只是业务规则接口IBonus和ITax;从而把业务规则与客户端调用完全分离,从而降低耦合度;
  • 完完全全地理解抽象工厂模式的意义非常重大,可以说对它的理解是你对OOP理解上升到一个新的里程碑的重要标志。学会了用抽象工厂模式编写框架类,你将理解OOP的精华:面向接口编程。

注:虽然案例是搜集的资料,但通过自己的整理与测试!希望博友能够尊重我的劳动成果,大家互相学习、共同进步!

时间: 2024-12-18 21:29:48

设计模式系列一创建型之(抽象工厂模式)的相关文章

&quot;围观&quot;设计模式(8)--创建型之简单工厂模式、工厂方法模式、抽象工厂模式

工厂模式的核心思想在我认为是将类创建的权利授予给工厂类,其他的类不允许创建,授予了权限的类创建好之后,需要某些的对象的时候,可以去工厂当中去取.也就是像一个工厂一样,用的人不需要关心对象怎么来的,你只需要关心怎么用就好了.工厂模式细分为三种,简单工厂.工厂方法.抽象工厂三种模式.这三种模式比较相似,往往会引发混淆,本文主要结合实际的例子去进行区分.理清三者之间的关系与适用范围. 概述 简单工厂 对于简单工厂,我个人的理解是,直接实现一个方法,要生产什么由这个方法以及传入的参数来决定. 工厂方法

java/android 设计模式学习笔记(4)---抽象工厂模式

再来介绍一下抽象工厂模式(Abstact Factory Pattern),也是创建型模式之一,上篇博客主要介绍了工厂方法模式.抽象工厂模式和工厂方法模式稍有区别.工厂方法模式中工厂类生产出来的产品都是具体的,也就是说每个工厂都会生产某一种具体的产品,但是如果工厂类中所生产出来的产品是多种多样的,工厂方法模式也就不再适用了,就要使用抽象工厂模式了. 抽象工厂模式的起源或者最早的应用,是对不同操作系统的图形化解决方案,比如在不同操作系统中的按钮和文字框的不同处理,展示效果也不一样,对于每一个操作系

大话设计模式C++实现-第15章-抽象工厂模式

一.UML图 二.概念 抽象方法模式(Abstract Factory):提供一个创建一系列相关或互相依赖对象的接口,而无需指定他们详细的类. 三.包括的角色 (1)抽象工厂 (2)详细工厂:包含详细工厂1和详细工厂2.详细工厂1用于生产详细产品A1和详细产品B1,详细工厂2用于生产详细产品A2和详细产品B2: (3)抽象产品:包含抽象产品A和抽象产品B. (4)详细产品:包含抽象产品A所相应的详细产品A1和A2.以及抽象产品B所相应的详细产品B1和B2. 说明:在<大话设计模式>中,上述的1

设计模式学习笔记(十一:抽象工厂模式)

1.1概述 提供一个创建一系列或相互依赖对象的接口,而无须指定他们具体的类.这就是抽象工厂模式的定义. 设计某些系统时可能需要为用户提供一系列相关的对象,但系统不希望用户直接使用new运算符实例化这些对象,而是应当由系统来控制这些对象的创建,否则用户不仅要清楚地知道使用哪些类来创建这些对象,而且还必须要清楚对象之间是如何关联的,使得用户的代码和这些类形成紧耦合.缺乏弹性.不利于维护. 例如,军队要为士兵(用户)提供机关枪.手枪以及相应的子弹,但军队系统不希望由士兵提供来生产机关枪.手枪以及子弹,

设计模式学习(五)、抽象工厂模式

1.抽象工厂模式的概念: 提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类. 2.抽象工厂模式的结构图 3.抽象工厂模式角色说明 抽象工厂(AbstractFactory)角色:所有的具体工厂都必须实现该接口,这个接口包含了一组方法来生产产品.具体工厂(ConcreteFactory)角色:实现不同的产品家族. 4.抽象工厂模式的实现 <?php /** * Created by PhpStorm. * User: Administrator * Date: 16-10-18

java-设计模式(创建型)-【工厂模式】

1.FactoryMode(工厂模式) 定义:提供创建对象的接口,方便对象的创建. 2.静态工厂方法模式(Static Factory Method Mode) 定义:对于对象的创建,交给工厂类内部的静态方法实现. 使用场景:适用于基类的子类较少时,且基本不会再增加子类时. 坏处,当子类增加时,需要修改工厂类的内部,违背了开闭原则. 基类与子类: public interface Sender { void send (); } class SenderMail implements Sende

《大话设计模式》学习笔记11:抽象工厂模式

切换数据库类型示例: 1.User类: public class User { public int Id { get; set; } public string Name { get; set; } } 2.AbstractProduct(以User为例): public interface IUser { void Insert(User user); User GetUser(int id); } 3.Product(以User.SQL Server为例): public class Sq

PYTHON设计模式,创建型之简单工厂模式

这个系统,感觉思路清爽,,相信多练练,多思考,就会熟悉的.. http://www.jianshu.com/p/2450b785c329 #!/usr/bin/evn python #coding:utf8 class Pizza(object): def prepare(self, type): print 'prepare {type} pizza'.format(type=type) def bake(self, type): print 'bake {type} pizza'.forma

设计模式演练——抽象工厂模式

1.小C的故事 下面讲述的是关于x星系喵星和汪星两个宿敌星球之间的故事.首先把镜头聚焦到喵星,它是主角登场的地方.(本故事纯属虚构,如有雷同,纯属巧合)        喵星纪元9035年,汪星精锐舰队聚集在近喵星轨道,企图一举拿下喵星.大部分喵星人已经被转移到防空工事中.喵星国王下令出动最强战力迎击来敌.喵小c是战斗机编队到王牌飞行员,参加了上百场战斗,获得了很多荣誉,他是本次先头部队的一员.        “装载初级武器,装载初级防御罩,装载初级逃生仓.准备完毕,出发!”,航空港的扩音机发出急