• <译>C#使用设计模式和软件设计原则构建应用程序 PartIII


    依赖注入

    这个原则的要点是什么。为什么你不能对类的实例进行再次硬编码?当我们编码,测试的时候,让我们关注一件很重要的事情。希望你知道单元测试并知道它的重要性。也许在你做任何编码之前你都应该首先设计你的测试,因此你应该很熟悉测试驱动开发。为了定义新功能你应该去写测试,你应该尝试去实现并开始编码直到测试通过。让我们先看看之前的文章的代码。

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

    我们有DateBasedTaxFactory,当我们应该测试一下这个工厂是否能够正常的工作。 是否每年的4月4日这个Tax类型的返回值都应该是0.我们也许会创建一个如下的测试。

    Customer cust = new Customer(){StateCode = "TX", County = "Travis", ZipCode = "78745"};
        DateBasedTaxFactory db = new DateBasedTaxFactory(cust, new CustomerBasedTaxFactory(cust));
        ITax tax = db.GetTaxObject();
        //test for no tax for a certain date
        if(tax.CalculateTax(3m) != 0)
        {
            throw new Exception("the value is supposed to be zero");
        }

    这里有什么问题么?我们不能真正的测试这个!正如你可以在DateBasedTaxFactory中看到的,为了测试当前的日期,它直接使用DateTime对象的Now属性。除非你改变你系统的时间不然我们不能使得NoTax的条件满足。更改系统时间是不理想的。我们还能做其他的事情么?有时这个工厂类有个引用一个隐藏的属性,它是硬编码实现的,它依赖一些需要变化的东西。工厂类需要一个DateTime对象。它不需要DateTime是当前的日期。它不关注给它的日期是什么。为了告诉外面的世界这个类需要什么来工作我们需要使用依赖注入。这将允许我们的测试给他任何需要测试的日期。就如下面所示:

    public class DateBasedTaxFactory : ITaxFactory
    {
        Customer _customer;
        ITaxFactory _taxFactory;
        DateTime _dt;
        public DateBasedTaxFactory(Customer c, ITaxFactory cb,DateTime dt)
        {
            _customer = c;
            _taxFactory = cb;
            _dt = dt;
        }
    
    
        public ITax GetTaxObject()
        {
            if (_customer.StateCode == "TX" && _dt.Month == 4 && _dt.Day == 4)
            {
                return new NoTax();
            }
            else
                return _taxFactory.GetTaxObject();
        }
    } 

    现在我们可以调整我们的测试来发送任何我们想要测试的日期。

    Customer cust = new Customer(){StateCode = "TX",County ="Travis",ZipCode = "78745"};
    DateBasedTaxFactory db = new DateBasedTaxFactory(cust, new CustomerBasedTaxFactory(cust),
        new DateTime(2001,4,4));
    ITax tax = GetTaxObject();
    //test for no tax for a certain date
    if (tax.CalculateTax(3m) != 0)
    {
        throw new Exception("the value is supposed to be zero");
    }

    单一原则/开闭原则

    为什么你的对象应该只做一件事情?为什么你应该从不改变他们?显示生活变化了那么为什么代表生活变化的代码不能变化?让我们看看之前的稳重中的第一个版本的Order类。好的,假如说你的公司有一个坚定的政策,在您的系统中的主要程序集BusinessLogic.dll每两个月只有一个发布版本。假如有个bug或者在这之前需要做些变更,这将是一项繁琐艰巨的任务。但是我们可以用较少的麻烦来定义一个可补充的发布程序集。如果我们使用如下源代码:

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

    如果在TX的税费逻辑发生了变化或者需要一个新的State的税费,我们将必须要去修改Order对象。这将会造成 很大的臭味,因为我们需要去测试并发布BusinessLogic.dll.由于它有税费有关,如果事情发生了很大的变化并且他将要投入生产ASAP中是,法律和金钱是一个不错的选择。

    从其他的文章中我们已经做了我们需要做的事情,例如:

     public interface ITax
        {
            decimal CalculateTax(decimal total);
        }
        public class TXTax:ITax
        {
            public decimal CalculateTax(decimal total)
            {
                return total * 0.08m;
            }
        }
        public class CustomerBasedTaxFactory:ITaxFactory
        {
            Customer _customer;
            static Dictionary<string, ITax> stateTaxObjects = new Dictionary<string, ITax>();
            static Dictionary<string, ITax> countyTaxObjects = new Dictionary<string, ITax>();
            public CustomerBasedTaxFactory(Customer customer)
            {
                _customer = customer;
            }
            public ITax GetTaxObject()
            {
                ITax tax;
                if(!string.IsNullOrEmpty(_customer.County))
                {
                    if (!countyTaxObjects.Keys.Contains(_customer.StateCode))
                    {
                        tax = (ITax)Activator.CreateInstance("Tax", "Solid.taxes." + _customer.County + "CountyTax");
                        countyTaxObjects.Add(_customer.StateCode, tax);
                    }
                    else
                        tax = countyTaxObjects[_customer.StateCode];
                }
                else
                {
                    if (!stateTaxObjects.Keys.Contains(_customer.StateCode))
                    {
                        tax = (ITax)Activator.CreateInstance("Tax", "Solid.taxes." + _customer.StateCode + "Tax");
                        stateTaxObjects.Add(_customer.StateCode, tax);
                    }
                    else
                        tax = stateTaxObjects[_customer.StateCode];
                }
                return tax;
            }
        }

    我们有我们的TaxFactory来创建Tax对象并且所有的Tax逻辑都是在它的单独类中来完成的。因此到现在为止ITax类可以被引入到其他的程序集当中来做一些Tax相关的任务。Tax.dll。如果发生了变化,那么只需要在当前的程序集当中测试,并且它的附加程序集也不会有太多繁文缛节的事情。

    好了,就这样吧,下次再见。

  • 相关阅读:
    类和对象
    使用JAVA理解程序逻辑
    类的有参
    1.人机猜拳
    类的无参方法
    类和对象
    Java解析XML
    集合框架一
    Java中的包
    继承
  • 原文地址:https://www.cnblogs.com/szkk/p/3917003.html
Copyright © 2020-2023  润新知