65.9K
CodeProject 正在变化。 阅读更多。
Home

使用设计模式和原则构建 C# 应用程序

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.90/5 (76投票s)

2013 年 4 月 5 日

CPOL

9分钟阅读

viewsIcon

165509

我写这篇文章是为了演示如何从头开始使用 SOLID 原则和常见设计模式构建应用程序

介绍 

本文将作为一系列文章的第一部分,介绍如何使用常见的企业架构原则和设计模式在 C# 中构建软件应用程序。文章将首先提供一种快速、粗糙但并非理想的编码示例,然后解释如何重构代码以使其更符合通用的编码指南。我将假设读者熟悉 C# 和基本面向对象原则。我将引用 SOLID 原则和某些设计模式,而不会在此定义它们。市面上有很多文章可以做到这一点。

那么,我们想要完成什么呢?让我想一个类比。您想建造一栋坚固的房子。您怎么做到这一点?希望建筑师和建造者知道他们在做什么:打好稳定的地基,以正确的方式进行框架搭建,电气工程符合规范等等。那么,这就是我们在编码时试图完成的目标。我们希望我们的代码是 SOLID 的(查找该缩写以获取更多解释)。我会在过程中解释它的含义。我们如何实现这一点?希望建筑师和开发人员知道他们在做什么并使用设计模式。您使用设计模式作为创建稳健代码的一种技术。SOLID 是目标,设计模式是实现该目标的一种方式。

开始

让我们从典型的电子商务场景开始。我们需要一个对象来表示订单(Order)、订单项(Order Item)和客户(Customer)。给定一个 Order 对象,该对象有一个方法可以计算总价(通过对订单项成本的总和应用税费),那么您能想到的最简单但最糟糕的编程方法是什么?当然是将税费例程逻辑放在 Order 类中。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace solid
{
 
    public class Order
    {
        List<OrderItem> _orderItems = new List<OrderItem>();
        public decimal CalculateTotal(Customer customer)
        {
            decimal total = _orderItems.Sum((item)=>{
                return item.Cost * item.Quantity;
            });
 
            decimal tax;
            if (customer.StateCode == "TX")
                tax = total * .08m;
            else if (customer.StateCode == "FL")
                tax = total * .09m;
            else
                tax = .03m;
 
            total = total + tax;
            return total;
        }
 
  
    }
    public class OrderItem
    {
        public int Quantity;
        public string Code;
        public decimal Cost;
    }
    public class Customer
    {
        public string StateCode;
        public string ZipCode;
        public string County;
    }
}

这样做有什么问题?从学术上讲,它违反了 SOLID 编程原则的第一条规则:单一职责。换句话说,Order 对象应该只负责处理与 Order 紧密相关的事务,例如计算订单项的总成本。它不应该还要负责按州管理税费例程。您甚至可以说它不应该负责计算订单项的总数,而它的唯一职责是协调计算订单项总数、添加税费以及可能添加运费或应用折扣的工作流程。但为了简单起见,我们暂时保持现状。

那么,还有什么问题呢?这段代码违反了另一项 SOLID 原则:开闭原则。换句话说,一旦定义了一个类,它就不应该改变(它是封闭的),而只能通过继承或运行时变异来扩展(对扩展开放)。这可能看起来奇怪或不切实际,但理想情况下,如果从业务规则收集、设计到编码的每一步都做得正确,一旦类经过测试并获准用于生产,您就不应该再更改其源代码。然而,在这里我们可以看到事实并非如此。任何时候添加或更改州税例程,Order 类的代码都必须更改。那么该怎么办?让我们稍微修改一下代码并开始使用一种模式。 

策略模式 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace solid
{
    public class Order
    {
        List<OrderItem> _orderItems = new List<OrderItem>();
        public decimal CalculateTotal(Customer customer)
        {
            decimal total = _orderItems.Sum((item) =>
            {
                return item.Cost * item.Quantity;
            });
 
            
 
            total = total + new Tax().CalculateTax(customer,total);
            return total;
        }
        public class Tax
        {
            public decimal CalculateTax(Customer customer, decimal total)
            {
                decimal tax;
                if (customer.StateCode == "TX")
                    tax = total * .08m;
                else if (customer.StateCode == "FL")
                    tax = total * .09m;
                else
                    tax = .03m;
                return tax;
            }
        } 
    }   
}

我们所做的只是创建了一个名为 Tax 的新类,并将税费例程逻辑放在其中。现在 Order 对象不再负责税费逻辑,可以将这项工作委托给 tax 对象来处理。这是策略模式的一个简单示例。简单定义一下,它就是将所有 独立的 逻辑放在自己的类中。很好,但现在有什么问题呢?我们违反了另一项 SOLID 原则:依赖倒置。它说类应该依赖于抽象而不是具体实现。在 C# 中,抽象由接口或抽象类表示。在我们上面的代码中,我们通过 

new Tax(); 

更不用说 Tax 对象仍然违反开闭原则,因为税费例程仍然可以添加或更改。我们就是赢不了。让我们继续重构。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace solid
{
    public class Order
    {
        List<OrderItem> _orderItems = new List<OrderItem>();
        public decimal CalculateTotal(Customer customer)
        {
            decimal total = _orderItems.Sum((item) =>
            {
                return item.Cost * item.Quantity;
            });
 

            ITax tax = new Tax();
            total = total + tax.CalculateTax(customer, total);
            return total;
        }
 

        public interface ITax
        {
            decimal CalculateTax(Customer customer, decimal total);
        }
        public class Tax : ITax
        {
            public decimal CalculateTax(Customer customer, decimal total)
            {
                decimal tax;
                if (customer.StateCode == "TX")
                    tax = total * .08m;
                else if (customer.StateCode == "FL")
                    tax = total * .09m;
                else
                    tax = .03m;
                return tax;
            }
        }
    }
}

所以,我们现在所做的是创建了一个税费接口来抽象实现。这现在好了一点,但我们仍然在实例化一个具体的类。Order 类不应该为此负责。它不应该关心它正在处理哪种 ITax 对象,只关心它是一个具有 CalculateTax 方法的 ITax 实例。在上面的代码中,它知道它正在创建 Tax 对象。这不好。我们需要其他东西来负责选择创建哪种 ITax 对象。

工厂模式 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace solid
{
 
    public class Order
    {
        List<OrderItem> _orderItems = new List<OrderItem>();
        public decimal CalculateTotal(Customer customer)
        {
            decimal total = _orderItems.Sum((item) =>
            {
                return item.Cost * item.Quantity;
            });
 

            ITax tax = new TaxFactory().GetTaxObject();
            total = total + tax.CalculateTax(customer, total);
            return total;
        }
       public class TaxFactory
       {
            public ITax GetTaxObject()
            {
                return new Tax();
            }
       } 
        public interface ITax
        {
            decimal CalculateTax(Customer customer, decimal total);
        }
        public class Tax : ITax
        {
            public decimal CalculateTax(Customer customer, decimal total)
            {
                decimal tax;
                if (customer.StateCode == "TX")
                    tax = total * .08m;
                else if (customer.StateCode == "FL")
                    tax = total * .09m;
                else
                    tax = .03m;
                return tax;
            }
        }
    }
}

简单定义一下,这种模式说对于任何给定的场景,应该有一个类,其唯一 职责 是根据一些变化的标准来创建其他类。使用工厂模式,我们创建一个负责为我们创建 tax 对象的类。现在 Order 对象完全不知道我们如何创建 tax 对象,也不知道它正在处理哪个 ITax 的具体实现。它只关心它有一个具有 CalculateTax 方法的 ITax。这时您可能想知道这样做有什么意义,它只是更多的代码,而 TaxFactory 只返回一种税费对象,不太有用。让我们引入一个新问题:如果引入了一个新的业务规则,规定如果存在县,则使用特定的税费例程,否则就使用州税费例程。现在我们需要更改代码。让我们从糟糕的方式开始编码。我们可能会这样做:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace solid
{
 
    public class Order
    {
        List<OrderItem> _orderItems = new List<OrderItem>();
        public decimal CalculateTotal(Customer customer)
        {
            decimal total = _orderItems.Sum((item) =>
            {
                return item.Cost * item.Quantity;
            });
 

            ITax tax = new TaxFactory().GetTaxObject();
            total = total + tax.CalculateTax(customer, total);
            return total;
        }
 
        public interface ITax
        {
            decimal CalculateTax(Customer customer, decimal total);
        }
        public class Tax : ITax
        {
            public decimal CalculateTax(Customer customer, decimal total)
            {
                decimal tax;
                if (!string.IsNullOrEmpty(customer.County))
                {
                    if (customer.County == "Travis")
                        tax = total * .085m;
                    else if (customer.County == "Hays")
                        tax = total * .095m;
                    else
                        tax = .03m;
 
                }
                else
                {
                    if (customer.StateCode == "TX")
                        tax = total * .08m;
                    else if (customer.StateCode == "FL")
                        tax = total * .09m;
                    else
                        tax = .03m;
                }
                return tax;
            }
        }
 

        public class TaxFactory
        {
            public ITax GetTaxObject()
            {
                return new Tax();
            }
        } 
    }
}

我们在 tax 对象中加入了条件语句来检查县。嗯,我们打破了更多规则。我们更改了一个不应该改变的类(开闭原则)。现在 Tax 对象负责决定是使用县税还是州税例程的逻辑(单一职责)。 如果业务规则再次更新,该类将不得不再次更改。听起来我们需要添加另一个类。

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace solid
{
  
    public class Order
    {
        List<OrderItem> _orderItems = new List<OrderItem>();
        public decimal CalculateTotal(Customer customer)
        {
            decimal total = _orderItems.Sum((item) =>
            {
                return item.Cost * item.Quantity;
            });
 

            ITax tax = new TaxFactory().GetTaxObject();
            total = total + tax.CalculateTax(customer, total);
            return total;
        }
 
        public interface ITax
        {
            decimal CalculateTax(Customer customer, decimal total);
        }
        public class Tax : ITax
        {
            public decimal CalculateTax(Customer customer, decimal total)
            {
                decimal tax;
 
                    if (customer.StateCode == "TX")
                        tax = total * .08m;
                    else if (customer.StateCode == "FL")
                        tax = total * .09m;
                    else
                        tax = .03m;
                
                return tax;
            }
        }
        public class TaxByCounty : ITax
        {
            public decimal CalculateTax(Customer customer, decimal total)
            {
                decimal tax;
                if (customer.County == "Travis")
                    tax = total * .08m;
                else if (customer.County == "Hays")
                    tax = total * .09m;
                else
                    tax = .03m;
                return tax;
            }
        }
 
        public  class TaxFactory
        {
            public  ITax GetTaxObject()
            {
                return new Tax();
            }
        } 
    }
}

在这里,我们添加了一个新类来处理基于县的税费例程,即 TaxCounty。太好了,现在我们有了一个类来处理所有这些,但是需要有人决定是使用 TaxCounty 对象还是普通的 Tax 对象,谁来决定?TaxFactory 似乎是一个不错的选择。工厂对象是为了做出决定并确定返回哪个类。但是等等,现在我们必须更改 TaxFactory,因为它没有客户的引用来做出决定。 让我们回去做一些我们一开始就应该做的事情。 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace solid
{
  
    public class Order
    {
        List<OrderItem> _orderItems = new List<OrderItem>();
        public decimal CalculateTotal(Customer customer)
        {
            decimal total = _orderItems.Sum((item) =>
            {
                return item.Cost * item.Quantity;
            });
 

            ITax tax = new TaxFactory().GetTaxObject();
            total = total + tax.CalculateTax(customer, total);
            return total;
        }
 
        public interface ITax
        {
            decimal CalculateTax(Customer customer, decimal total);
        }
        public class Tax : ITax
        {
            public decimal CalculateTax(Customer customer, decimal total)
            {
                decimal tax;
 
                if (customer.StateCode == "TX")
                    tax = total * .08m;
                else if (customer.StateCode == "FL")
                    tax = total * .09m;
                else
                    tax = .03m;
 
                return tax;
            }
        }
        public class TaxByCounty : ITax
        {
            public decimal CalculateTax(Customer customer, decimal total)
            {
                decimal tax;
                if (customer.County == "Travis")
                    tax = total * .08m;
                else if (customer.County == "Hays")
                    tax = total * .09m;
                else
                    tax = .03m;
                return tax;
            }
        }
 
        public interface ITaxFactory
        {
             ITax GetTaxObject();
        }
        public  class TaxFactory:ITaxFactory
        {
            public  ITax GetTaxObject()
            {
                return new Tax();
            }
        }
        public class CustomerBasedTaxFactory : ITaxFactory
        {
            Customer _customer;
            public CustomerBasedTaxFactory(Customer customer)
            {
                _customer = customer;
            }
            public ITax GetTaxObject()
            {
                if (!string.IsNullOrEmpty(_customer.County))
                    return new TaxByCounty();
                else
                    return new Tax();
            }
        } 
    }
}

您可能已经注意到 Order 类中仍然存在一个问题。找到了吗?是的,我们仍然有一个对 TaxFactory 具体实现的引用。让我们为 Factory 类创建一个新的抽象,即 ITaxFactory。现在我们可以创建一个新的工厂,它在构造函数中接受一个 Customer 对象,即 CustomerBasedTaxFactory。现在怎么办?我们想使用 CustomerBasedTaxFactory,但我们不允许在 Order 类中 实例化 它的具体实例,那么该怎么办。这似乎需要另一个工厂来提供工厂,然后这些工厂又需要一个工厂。我们陷入了无限循环。有什么办法可以解决? 

依赖注入 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace solid
{
 
    public class Order
    {
        ITaxFactory _taxFactory;
        public Order(ITaxFactory taxFactory)
        {
            _taxFactory = taxFactory;
        }
        List<OrderItem> _orderItems = new List<OrderItem>();
        public decimal CalculateTotal(Customer customer)
        {
            decimal total = _orderItems.Sum((item) =>
            {
                return item.Cost * item.Quantity;
            });
 

            ITax tax = _taxFactory.GetTaxObject();
            total = total + tax.CalculateTax(customer, total);
            return total;
        }
 
        public interface ITax
        {
            decimal CalculateTax(Customer customer, decimal total);
        }
        public class Tax : ITax
        {
            public decimal CalculateTax(Customer customer, decimal total)
            {
                decimal tax;
 
                if (customer.StateCode == "TX")
                    tax = total * .08m;
                else if (customer.StateCode == "FL")
                    tax = total * .09m;
                else
                    tax = .03m;
 
                return tax;
            }
        }
        public class TaxByCounty : ITax
        {
            public decimal CalculateTax(Customer customer, decimal total)
            {
                decimal tax;
                if (customer.County == "Travis")
                    tax = total * .08m;
                else if (customer.County == "Hays")
                    tax = total * .09m;
                else
                    tax = .03m;
                return tax;
            }
        }
 
        public interface ITaxFactory
        {
             ITax GetTaxObject();
        }
        public  class TaxFactory:ITaxFactory
        {
            public  ITax GetTaxObject()
            {
                return new Tax();
            }
        }
        public class CustomerBasedTaxFactory : ITaxFactory
        {
            Customer _customer;
            public CustomerBasedTaxFactory(Customer customer)
            {
                _customer = customer;
            }
            public ITax GetTaxObject()
            {
                if (!string.IsNullOrEmpty(_customer.County))
                    return new TaxByCounty();
                else
                    return new Tax();
            }
        }
    }
}

依赖注入允许一个类告诉类的使用者它为了正常运行而需要什么。在 Order 类的情况下,它需要一个 tax 工厂来工作,或者需要一个 ITaxFactory。DI 有 3 种方式,我更喜欢构造函数方法,因为它明确地告诉类的使用者它需要什么。您可以看到在上面的代码中,我们添加了一个接受 ITaxFactory 类型并存储其引用的构造函数。 在 CalculateTotal 方法中,它只是使用其对 ITaxFactory 的引用来调用 GetTaxObject。它完全不知道它正在使用哪个 Tax Factory 或哪个 Tax 对象。无知是福。但谁负责决定使用哪个 TaxFactory 呢? 让我们实现一种技术来实现这一点,如下所示。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace solid
{
 
    public class Order
    {
        ITaxFactory _taxFactory;
        public Order()
            : this(new TaxFactory())
        {
        }
        public Order(Customer c)
            : this(new CustomerBasedTaxFactory(c))
        {
        }
        public Order(ITaxFactory taxFactory)
        {
            _taxFactory = taxFactory;
        }
        List<OrderItem> _orderItems = new List<OrderItem>();
        public decimal CalculateTotal(Customer customer)
        {
            decimal total = _orderItems.Sum((item) =>
            {
                return item.Cost * item.Quantity;
            });
 

            ITax tax = _taxFactory.GetTaxObject();
            total = total + tax.CalculateTax(customer, total);
            return total;
        }
 
        public interface ITax
        {
            decimal CalculateTax(Customer customer, decimal total);
        }
        public class Tax : ITax
        {
            public decimal CalculateTax(Customer customer, decimal total)
            {
                decimal tax;
 
                if (customer.StateCode == "TX")
                    tax = total * .08m;
                else if (customer.StateCode == "FL")
                    tax = total * .09m;
                else
                    tax = .03m;
 
                return tax;
            }
        }
        public class TaxByCounty : ITax
        {
            public decimal CalculateTax(Customer customer, decimal total)
            {
                decimal tax;
                if (customer.County == "Travis")
                    tax = total * .08m;
                else if (customer.County == "Hays")
                    tax = total * .09m;
                else
                    tax = .03m;
                return tax;
            }
        }
 
        public interface ITaxFactory
        {
             ITax GetTaxObject();
        }
        public  class TaxFactory:ITaxFactory
        {
            public  ITax GetTaxObject()
            {
                return new Tax();
            }
        }
        public class CustomerBasedTaxFactory : ITaxFactory
        {
            Customer _customer;
            public CustomerBasedTaxFactory(Customer customer)
            {
                _customer = customer;
            }
            public ITax GetTaxObject()
            {
                if (!string.IsNullOrEmpty(_customer.County))
                    return new TaxByCounty();
                else
                    return new Tax();
            }
        }
    }
}

有时被称为“简陋的依赖注入”的方法使用构造函数来硬编码一个默认工厂,如果没有指定则使用它。还有一个称为 IOC 容器的东西,它会在 运行时 自动注入所需的依赖项。这可以根据运行时的某些标准进行配置。因此,让我们引入另一个业务规则:也许大多数时候您想使用普通的税费逻辑,但在某个特殊日子,公司为德克萨斯州的人们承担所有税费 

可空对象 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace solid
{
 
    public class Order
    {
        ITaxFactory _taxFactory;
        public Order()
            : this(new TaxFactory())
        {
        }
        public Order(Customer c)
            : this(new DateBasedTaxFactory(c,new CustomerBasedTaxFactory(c)))
        {
        }
        public Order(ITaxFactory taxFactory)
        {
            _taxFactory = taxFactory;
        }
        List<OrderItem> _orderItems = new List<OrderItem>();
        public decimal CalculateTotal(Customer customer)
        {
            decimal total = _orderItems.Sum((item) =>
            {
                return item.Cost * item.Quantity;
            });
 
            total = total + _taxFactory.GetTaxObject().CalculateTax(customer, total);
            return total;
        }
 
        public interface ITax
        {
            decimal CalculateTax(Customer customer, decimal total);
        }
        public class Tax : ITax
        {
            public decimal CalculateTax(Customer customer, decimal total)
            {
                decimal tax;
 
                if (customer.StateCode == "TX")
                    tax = total * .08m;
                else if (customer.StateCode == "FL")
                    tax = total * .09m;
                else
                    tax = .03m;
 
                return tax;
            }
        }
        public class TaxByCounty : ITax
        {
            public decimal CalculateTax(Customer customer, decimal total)
            {
                decimal tax;
                if (customer.County == "Travis")
                    tax = total * .08m;
                else if (customer.County == "Hays")
                    tax = total * .09m;
                else
                    tax = .03m;
                return tax;
            }
        }
        public class NoTax : ITax
        {
            public decimal CalculateTax(Customer customer, decimal total)
            {
                return 0.0m;
            }
        }
 

        public interface ITaxFactory
        {
            ITax GetTaxObject();
        }
        public class TaxFactory : ITaxFactory
        {
            public ITax GetTaxObject()
            {
                return new Tax();
            }
        }
        public class CustomerBasedTaxFactory : ITaxFactory
        {
            Customer _customer;
            public CustomerBasedTaxFactory(Customer customer)
            {
                _customer = customer;
            }
            public ITax GetTaxObject()
            {
                if (!string.IsNullOrEmpty(_customer.County))
                    return new TaxByCounty();
                else
                    return new Tax();
            }
        }
        public class DateBasedTaxFactory : ITaxFactory
        {
            Customer _customer;
            ITaxFactory _taxFactory;
            public DateBasedTaxFactory(Customer c, ITaxFactory cb)
            {
                _customer = c;
                _taxFactory = cb;
 
            }
 

            public ITax GetTaxObject()
            {
                if (_customer.StateCode == "TX" && 
                       DateTime.Now.Month == 4 && DateTime.Now.Day == 4)
                {
                    return new NoTax();
                }
                else
                    return _taxFactory.GetTaxObject();
            }
        }
    }
}

在上面的代码中,您可以看到我们使用了可空对象模式来创建一个新的 ITax 对象,称为 NoTax,它始终返回 0。 我们还使用了装饰器模式来更改税费工厂的行为。我们创建了一个名为 DateBasedTaxFactory 的新工厂,它接受一个 ITaxFactory 实例作为默认值以及一个客户对象。DateBasedTaxFactory 负责检查日期并决定是否应使用 NoTax 对象。我们将此工厂注入 Order 的构造函数。现在它将自动处理决定是否应使用 NoTax 对象,或者只是让 CustomerBasedTaxFactory 来决定使用什么。 

事情看起来好多了,所有逻辑都封装在自己的类中,但我们可以做得更进一步。看看 Tax 对象,有没有发现什么问题?每次添加新州时,您都必须添加更多的 if 语句,如果逻辑发生变化,您必须更新 Tax 类。您不应该 ever 更改它!那么该怎么办? 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace solid
{
 
    public class Order
    {
        ITaxFactory _taxFactory;
        
 
        public Order(Customer c)
            : this(new DateBasedTaxFactory(c, new CustomerBasedTaxFactory(c)))
        {
        }
        public Order(ITaxFactory taxFactory)
        {
            _taxFactory = taxFactory;
        }
        List<OrderItem> _orderItems = new List<OrderItem>();
        public decimal CalculateTotal(Customer customer)
        {
            decimal total = _orderItems.Sum((item) =>
            {
                return item.Cost * item.Quantity;
            });
 
            total = total + _taxFactory.GetTaxObject().CalculateTax(customer, total);
            return total;
        }
 
        public interface ITax
        {
            decimal CalculateTax(Customer customer, decimal total);
        }
 
        public class TXTax:ITax
        {
            public decimal CalculateTax(Customer customer, decimal total)
            {
                return total * .08m;
            }
        }
        public class FLTax : ITax
        {
            public decimal CalculateTax(Customer customer, decimal total)
            {
                return total * .09m;
            }
        }
        public class TaxByCounty : ITax
        {
            public decimal CalculateTax(Customer customer, decimal total)
            {
                decimal tax;
                if (customer.County == "Travis")
                    tax = total * .08m;
                else if (customer.County == "Hays")
                    tax = total * .09m;
                else
                    tax = .03m;
                return tax;
            }
        }
        public class NoTax : ITax
        {
            public decimal CalculateTax(Customer customer, decimal total)
            {
                return 0.0m;
            }
        }
 

        public interface ITaxFactory
        {
            ITax GetTaxObject();
        }
 
        public class CustomerBasedTaxFactory : ITaxFactory
        {
            Customer _customer;
            static Dictionary<string, ITax> stateTaxObjects = new Dictionary<string, ITax>();
            public CustomerBasedTaxFactory(Customer customer)
            {
                _customer = customer;
            }
            public ITax GetTaxObject()
            {
                ITax tax;
                if (!string.IsNullOrEmpty(_customer.County))
                    tax = new TaxByCounty();
                else
                {
                    if (!stateTaxObjects.Keys.Contains(_customer.StateCode))
                    {
                        tax = (ITax)Activator.CreateInstance(Type.GetType("solid.Order+" + _customer.StateCode + "Tax"));
                        stateTaxObjects.Add(_customer.StateCode, tax);
                    }
                    else
                        tax = stateTaxObjects[_customer.StateCode];
                }
                return tax;
            }
           
        }
        public class DateBasedTaxFactory : ITaxFactory
        {
            Customer _customer;
            ITaxFactory _taxFactory;
            public DateBasedTaxFactory(Customer c, ITaxFactory cb)
            {
                _customer = c;
                _taxFactory = cb;
            }
            public ITax GetTaxObject()
            {
                if (_customer.StateCode == "TX" && DateTime.Now.Month == 4 && DateTime.Now.Day == 4)
                {
                    return new NoTax();
                }
                else
                    return _taxFactory.GetTaxObject();
            }
        } 
    }
}

看看上面的代码,您可能会想,哇,刚才发生了什么。Tax 对象去哪儿了?CustomerBasedTaxFactory 里的那些东西是什么?编程的一些学派认为,您应该尽量少写 if 语句。这与开闭原则有关,因为每次添加新的州税例程时,您都必须用另一个 if 语句(或 switch)修改 Tax 对象。Tax 类中可能会有很多 if 语句。怎么办? 更多的类!我们可以将每个州的税费例程逻辑放入自己的类中,并删除 Tax 对象。但那样的话,if 语句就会被放在工厂类中。没那么快。我们可以使用反射来根据命名约定动态获取正确的对象。CustomerBasedTaxFactory 将负责这一点。需要考虑的一点是,反射会产生一些额外的开销,所以我们应该缓存创建的每个项,我们将使用一个以州代码为键的静态字典来实现这一点。没有更多的 IF 了!我们也可以对基于县的税费做同样的事情。  

我想就到这里吧。希望这对某些人有所帮助。我将在下一篇文章中接续我们上次中断的地方。

© . All rights reserved.