设计模式(3)--抽象工厂模式(Absrtact Factory Pattern)

定义

抽象工厂模式的实质就是提供接口来创建一系列相关或独立的对象而不指定这些对象的具体类。

理解

在软件系统中,经常面临着“一系列相互依赖的对象”的创建工作;同时由于需求的变化,往往存在着更多系列对象的创建工作。如何应对这种变化?如何绕过常规的对象的创建方法(熟悉的new操作符),提供一种“封装机制”来避免客户程序和这种“多系列具体对象创建工作”的紧耦合?这就是我们要说的抽象工厂模式。抽象工厂模式提供了一种方式,可以将一组具有同一主题的单独的工厂封装起来。在正常使用中,客户端程序需要创建抽象工厂的具体实现,然后使用抽象工厂作为接口来创建这一主题的具体对象。客户端程序不需要知道(或关心)它从这些内部的工厂方法中获得对象的具体类型,因为客户端程序仅使用这些对象的通用接口。抽象工厂模式将一组对象的实现细节与他们的一般使用分离开来。

结构

优点

  1. 分离了具体的类;

  2. 易于交换产品的系列;
  3. 有利于保持产品的一致性。

缺点

  1. 在产品族中扩展新的产品是很困难的,它需要修改抽象工厂的接口。

适用情形

  1. 一个系统不应该依赖于产品实例如何被创建、组合和表达的细节,这对于所有形态工厂模式的都是重要的;

  2. 提供一个产品类库,只想提供它的接口而不是它的实现类;
  3. 系统有多余一个的产品族但是系统只消费其中一个产品;
  4. 设计上要求属于同一产品族的产品是在一起使用的。

代码示例说明

(这里借用TerryLee大神的例子和代码)

中国企业需要一项简单的财务计算:每月月底,财务人员要计算员工的工资。

员工的工资 = (基本工资 + 奖金 - 个人所得税)。这是一个放之四海皆准的运算法则。

为了简化系统,我们假设员工基本工资总是4000美金。

中国企业奖金和个人所得税的计算规则是:

         奖金 = 基本工资(4000) * 10%

         个人所得税 = (基本工资 + 奖金) * 40%

我们现在要为此构建一个软件系统(代号叫Softo),满足中国企业的需求。

此时采用最简单的方式,类图如下所示:

代码如下所示:

using System;

namespace ChineseSalary
{
    /// <summary>
    /// 计算中国个人所得税
    /// </summary>
    public class ChineseTax
    {
        public double Calculate()
        {
            return (Constant.BASE_SALARY + (Constant.BASE_SALARY * 0.1)) * 0.4;
        }
    }
}

using System;

namespace ChineseSalary
{
    /// <summary>
    /// 计算中国个人奖金
    /// </summary>
    public class ChineseBonus
    {
        public double Calculate()
        {
            return Constant.BASE_SALARY * 0.1;
        }
    }
}

namespace ChineseSalary
{
    /// <summary>
    /// 公用的常量
    /// </summary>
    public class Constant
    {
        public static double BASE_SALARY = 4000;
    }
}

using System;

namespace ChineseSalary
{
    /// <summary>
    /// 客户端程序调用
    /// </summary>
    public class Calculator
    {
        public static void Main(string[] args)
        {
            ChineseBonus bonus = new ChineseBonus();
            double bonusValue  = bonus.Calculate();

            ChineseTax tax = new ChineseTax();
            double taxValue = tax.Calculate();

            double salary = 4000 + bonusValue - taxValue; 

            Console.WriteLine("Chinaese Salary is:" + salary);
            Console.ReadLine();
        }
    }
}

美国企业工资的计算与中国大致相同,但是在奖金和个人所得税计算规则不同于中国:

奖金 = 基本工资 * 15 %

个人所得税 = (基本工资 * 5% + 奖金 * 25%)

我们仅仅将ChineseTax、ChineseBonus修改为AmericanTax、AmericanBonus。 只是修改了部分类的名称和类方法的内容,结构没有什么变化,修改后的类图如下所示:

代码如下所示:

using System;

namespace AmericanSalary
{
    /// <summary>
    /// 计算美国个人奖金
    /// </summary>
    public class AmericanBonus
    {
        public double Calculate()
        {
            return Constant.BASE_SALARY * 0.1;
        }
    }
}

using System;

namespace AmericanSalary
{
    /// <summary>
    /// 计算美国个人所得税
    /// </summary>
    public class AmericanTax
    {
        public double Calculate()
        {
            return (Constant.BASE_SALARY + (Constant.BASE_SALARY * 0.1)) * 0.4;
        }
    }
}

using System;

namespace AmericanSalary
{
    /// <summary>
    /// 公用的常量
    /// </summary>
    public class Constant
    {
        public static double BASE_SALARY = 4000;
    }
}

using System;

namespace AmericanSalary
{
    /// <summary>
    /// 客户端程序调用
    /// </summary>
    public class Calculator
    {
        public static void Main(string[] args)
        {
            AmericanBonus bonus = new AmericanBonus();
            double bonusValue  = bonus.Calculate();

            AmericanTax tax = new AmericanTax();
            double taxValue = tax.Calculate();

            double salary = 4000 + bonusValue - taxValue; 

            Console.WriteLine("American Salary is:" + salary);
            Console.ReadLine();
        }
    }
}

开始只考虑将Softo系统运行于中国企业,但随着MaxDO公司业务向海外拓展, MaxDO需要将该系统移植给美国使用。需要将上面中国和美国两种情况整合在一个系统。移植时,MaxDO不得不抛弃中国企业的业务规则类ChineseTax和ChineseBonus, 然后为美国企业新建两个业务规则类: AmericanTax,AmericanBonus。最后修改了业务规则调用Calculator类。

结果我们发现:每当Softo系统移植的时候,就抛弃原来的类。现在,如果中国华为要购买该系统,我们不得不再次抛弃AmericanTax,AmericanBonus,修改回原来的业务规则。一个可以立即想到的做法就是在系统中保留所有业务规则模型,即保留中国和美国企业工资运算规则。

前面系统的整合问题在于:当系统在客户在美国和中国企业间切换时仍然需要修改Caculator代码。

一个维护性良好的系统应该遵循“开闭原则”。即:封闭对原来代码的修改,开放对原来代码的扩展(如类的继承,接口的实现)

我们发现不论是中国企业还是美国企业,他们的业务运规则都采用同样的计算接口。 于是很自然地想到建立两个业务接口类Tax,Bonus,然后让AmericanTax、AmericanBonus和ChineseTax、ChineseBonus分别实现这两个接口,类图如下所示:

具体代码如下所示:

using System;

namespace InterfaceSalary
{
    /// <summary>
    /// 奖金抽象类
    /// </summary>
    public abstract class Bonus
    {
        public abstract double Calculate();
    }
}

using System;

namespace InterfaceSalary
{
    /// <summary>
    /// 计算中国个人奖金
    /// </summary>
    public class ChineseBonus:Bonus
    {
        public override double Calculate()
        {
            return Constant.BASE_SALARY * 0.1;
        }
    }
}

using System;

namespace InterfaceSalary
{
    /// <summary>
    /// 个人所得税抽象类
    /// </summary>
    public abstract class Tax
    {
        public abstract double Calculate();
    }
}

using System;

namespace InterfaceSalary
{
    /// <summary>
    /// 计算中国个人所得税
    /// </summary>
    public class ChineseTax:Tax
    {
        public override double Calculate()
        {
            return (Constant.BASE_SALARY + (Constant.BASE_SALARY * 0.1)) * 0.4;
        }
    }
}

using System;

namespace InterfaceSalary
{
    /// <summary>
    /// 公用的常量
    /// </summary>
    public class Constant
    {
        public static double BASE_SALARY = 4000;
    }
}

using System;

namespace InterfaceSalary
{
    /// <summary>
    /// 客户端程序调用
    /// </summary>
    public class Calculator
    {
        public static void Main(string[] args)
        {
            Bonus bonus = new ChineseBonus();
            double bonusValue  = bonus.Calculate();

            Tax tax = new ChineseTax();
            double taxValue = tax.Calculate();

            double salary = 4000 + bonusValue - taxValue; 

            Console.WriteLine("Chinaese Salary is:" + salary);
            Console.ReadLine();
        }
    }
}

然而,上面增加的接口几乎没有解决任何问题,因为当系统的客户在美国和中国企业间切换时Caculator代码仍然需要修改。只不过修改少了两处,但是仍然需要修改ChineseBonus,ChineseTax部分。致命的问题是:我们需要将这个移植工作转包给一个叫Hippo的软件公司。 由于版权问题,我们并未提供Softo系统的源码给Hippo公司,因此Hippo公司根本无法修改Calculator,导致实际上移植工作无法进行。

为此,我们考虑增加一个工具类(命名为Factory),代码如下:

using System;

namespace FactorySalary
{
    /// <summary>
    /// Factory类
    /// </summary>
    public class Factory
    {
        public Tax CreateTax()
        {
            return new ChineseTax();
        }

        public Bonus CreateBonus()
        {
            return new ChineseBonus();
        }
    }
}

修改后的客户端代码:

using System;

namespace FactorySalary
{
    /// <summary>
    /// 客户端程序调用
    /// </summary>
    public class Calculator
    {
        public static void Main(string[] args)
        {
            Bonus bonus = new Factory().CreateBonus();
            double bonusValue  = bonus.Calculate();

            Tax tax = new Factory().CreateTax();
            double taxValue = tax.Calculate();

            double salary = 4000 + bonusValue - taxValue; 

            Console.WriteLine("Chinaese Salary is:" + salary);
            Console.ReadLine();
        }
    }
}

不错,我们解决了一个大问题,设想一下:当该系统从中国企业移植到美国企业时,我们现在需要做什么?

答案是: 对于Caculator类我们什么也不用做。我们需要做的是修改Factory类。

很显然,前面的解决方案带来了一个副作用:就是系统不但增加了新的类Factory,而且当系统移植时,移植工作仅仅是转移到Factory类上,工作量并没有任何缩减,而且还是要修改系统的源码。 从Factory类在系统移植时修改的内容我们可以看出: 实际上它是专属于美国企业或者中国企业的。名称上应该叫AmericanFactory,ChineseFactory更合适.

解决方案是增加一个抽象工厂类AbstractFactory,增加一个静态方法,该方法根据一个配置文件(App.config或者Web.config) 一个项(比如factoryName)动态地判断应该实例化哪个工厂类,这样,我们就把移植工作转移到了对配置文件的修改。修改后的类图如下:

抽象工厂类的代码如下(使用反射模式):

using System;
using System.Reflection;

namespace AbstractFactory
{
    /// <summary>
    /// Factory类
    /// </summary>
    public abstract class AbstractFactory
    {
//        public AbstractFactory GetInstance()
//        {
//            string factoryName = Constant.STR_FACTORYNAME.ToString();
//
//            AbstractFactory instance;
//
//            if(factoryName == "ChineseFactory")
//                instance = new ChineseFactory();
//            else if(factoryName == "AmericanFactory")
//                instance = new AmericanFactory();
//            else
//                instance = null;
//
//            return instance;
//        }

        public AbstractFactory GetInstance()
        {
            string factoryName = Constant.STR_FACTORYNAME.ToString();

            AbstractFactory instance;

            if(factoryName != "")
                instance = (AbstractFactory)Assembly.Load(factoryName).CreateInstance(factoryName);
            else
                instance = null;

            return instance;
        }

        public abstract Tax CreateTax();

        public abstract Bonus CreateBonus();
    }
}

配置文件如下所示:

设计模式(3)--抽象工厂模式(Absrtact Factory Pattern)

时间: 2024-12-28 13:18:39

设计模式(3)--抽象工厂模式(Absrtact Factory Pattern)的相关文章

【设计模式】 抽象工厂模式 Abstract Factory Pattern

简单工厂模式是一个工厂类根据工厂方法的参数创建不出不同的产品, 工厂方法模式是每一个产品都有一个一一对应的工厂负责创建该产品.那么今天要讲的抽象工厂模式是一个工厂能够产生关联的一系列产品.抽象工厂模式相对于简单工厂和工厂方法模式来着更具抽象性. 一.抽象工厂模式演绎 我们先来看一个简单的需求: 甲方要开发一套办公自动化软件,其中有一个非常重要的功能就是要能够导入Word 文档和Excel 文档. 开发人员拿到需求后就开始编码了,  很快代码写完了: public class ImportTool

二十四种设计模式:抽象工厂模式(Abstract Factory Pattern)

抽象工厂模式(Abstract Factory Pattern) 介绍提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类. 示例有Message和MessageModel,Message有一个Insert()方法,该方法的参数是MessageModel. AbstractMessageModel using System; using System.Collections.Generic; using System.Text; namespace Pattern.Abstract

设计模式-04抽象工厂模式(Abstract Factory Pattern)

1.模式动机 在工厂方法模式中具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品,工厂方法也具有唯一性,一般情况下,一个具体工厂中只有一个工厂方法或者一组重载的工厂方法.但是有时候我们需要一个工厂可以提供多个产品对象,而不是单一的产品对象. 为了更清晰地理解工厂方法模式,需要先引入两个概念: 产品等级结构:产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机.海信电视机.TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌

设计模式 - 抽象工厂模式(abstract factory pattern) 详解

抽象工厂模式(abstract factory pattern) 详解 本文地址: http://blog.csdn.net/caroline_wendy/article/details/27091671 参考工厂模式: http://blog.csdn.net/caroline_wendy/article/details/27081511 抽象工厂模式: 提供一个接口, 用于创建相关或依赖对象的家族, 而不需要明确指定具体类. 全部代码: http://download.csdn.net/de

设计模式 - 抽象工厂模式(abstract factory pattern) 具体解释

抽象工厂模式(abstract factory pattern) 详细解释 本文地址: http://blog.csdn.net/caroline_wendy/article/details/27091671 參考工厂模式: http://blog.csdn.net/caroline_wendy/article/details/27081511 抽象工厂模式: 提供一个接口, 用于创建相关或依赖对象的家族, 而不须要明白指定详细类. 所有代码: http://download.csdn.net/

抽象工厂模式(abstarct factory pattern)和工厂模式(factory pattern)的比较

抽象工厂模式和工厂模式从字面上来看就有必然的联系,他们都是创建型模式.总结来说,工厂模式(factory pattern)只是个小工厂,只提供一层接口的实现类的输出,而抽象工厂模式(abstract factory pattern)是工厂模式的进一步升级,可以上升到两层以上的工厂模式继承,是工厂的工厂.一计算机来说,工厂模式可以是不同型号显示器,CPU或者网卡的提供者,而抽象工厂模式是显示器工厂.CPU工厂和网卡工厂的工厂.可以通过抽象工厂来获得CPU工厂,进而获得某个型号的CPU.整个依赖关系

抽象工厂模式(Abstract Factory Pattern)

抽象工厂模式概述 定义:提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类 抽象工厂抽象工厂,顾名思义,就是比工厂模式更抽象的工厂模式.在工厂模式中,一个具体工厂只负责生产一个具体产品.而在抽象工厂模式中,一个具体工厂可以生产一组相关的产品,这些产品称为产品族,产品族中的每一个产品部分属于每一个产品继承等级结构 首先我们先了解下什么是产品族和产品等级结构.产品等级结构即产品的继承结构,好比一个抽象类是汽车,其子类包括奔驰,宝马,大众,保时捷.....这样抽象汽车与具体汽车品牌之间

设计模式 笔记 抽象工厂模式 Abstract Factory

//---------------------------15/04/09---------------------------- //Abstract Factory 抽象工厂----对象创建型模式 /* 1:意图:提供一个创建一系列相关或相互依赖对象的接口,而无需制定他们具体的类. 2:别名:Kit 3:动机 4:适应性: 1>一个系统要独立于它的产品的创建.组合和表示时. 2>一个系统要由多个产品系列中的一个来配置时. 3>当你要强调一系列相关的产品对象的设计以便进行联合使用时.

设计模式——4.抽象工厂模式

1. 模式动机 在工厂方法模式中具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品,工厂方法也具有唯一性,一般情况下,一个具体工厂中只有一个工厂方法或者一组重载的工厂方法.但是有时候我们需要一个工厂可以提供多个产品对象,而不是单一的产品对象. 为了更清晰地理解工厂方法模式,需要先引入两个概念: 产品等级结构 :产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机.海信电视机.TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体

Android设计模式——抽象工厂模式(Abstract Factory)

二十三种设计模式分为三大类: 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接模式.组合模式.享元模式. 行为型模式,共十一种:策略模式.模板方法模式.观察者模式.迭代子模式.责任链模式.命令模式.备忘录模式.状态模式.访问者模式.中介者模式.解释器模式. 1 package com.example.main; 2 3 import android.app.Activity; 4 import