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。如果发生了变化,那么只需要在当前的程序集当中测试,并且它的附加程序集也不会有太多繁文缛节的事情。

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

C#使用设计模式和软件设计原则构建应用程序 PartIII,布布扣,bubuko.com

时间: 2025-01-02 18:59:24

C#使用设计模式和软件设计原则构建应用程序 PartIII的相关文章

主流MVC框架的设计模式及遵守的软件设计原则

原文地址,会不断更新  http://it.zuocheng.net/mvc-design-pattern-design-principle-summary   作程的技术博客 本文以主流的MVC框架为例,比如Java 的SSH.PHP的Symfony和Zend Framework ,在简单地剖析他们的设计原理之后,找到其中使用的设计模式:鉴赏他们的代码实现,查看设计者们都遵守了哪些软件设计原则.作此文,一为学习,二为总结.其中下面所写内容可能并不全面,也可能不准确,但会不断修改完善. 框架模式

设计模式之六大设计原则

在上篇博文中提到了开放-封闭原则,没有细谈,这次我们来总结一下设计模式的几大原则. 1开放-封闭原则:是指软件实体(类.模块.函数等)应该可以扩展,但是不可修改. 对原则的理解:开闭原则是最具有理想主义色彩的一个原则,它是面向对象设计的终极目标,下面所要介绍的几个原则可以看成是为了符合开闭原则所作的努力和解决办法.对于开闭原则通俗的理解就是,能不改就不改,能少改尽可能的少改.周所周知,物质是运动的,世界是变化的,想要让一个事物永恒不变是不可能的,所以要想让软件绝对符合开闭原则是不可能的. 2单一

【小话设计模式】面向对象设计原则

1.单一职责原则 单一职责原则的核心思想就是:系统中的每一个对象都应该只有一个单独的职责,而所有对象所关注的就是自身职责的完成.英文缩写SRP  Single Responsibility Principle 单一职责原则-->"高内聚,低耦合",每个类应该只有一个职责,此外只能提供一种功能,而引起类变化的原因应该只有一个.在设计模式中,所有的设计模式都遵循这一原则. 优点: 可以降低类的复杂度: 提高类的可读性,提高系统的可维护性: 变更引起的风险降低. 2.里氏替换原则 里氏

设计模式小结——六大设计原则

设计模式是一套由软件界前辈们总结出的可以反复使用的编程经验,旨在提高代码的可重用性,提高系统的可维护性,以及解决一系列复杂问题.设计模式包括6大设计原则和23种种设计模式.6大设计原则:单一职责原则SRP 应该有却仅有一个原因引起类的变更,即类最好只实现一种功能.高内聚. 单一职责的实现方式是一个职责一个接口. 单一职责适用于类和接口,同样适用于方法,一个方法也应该只做好一件事.里氏替换原则LSP 所有能使用父类的地方必须能透明地使用其子类的对象. 子类必须完全实现父类的方法,如果子类不能完整实

[转载] 一些软件设计原则

转载地址:http://blog.jobbole.com/685/ 下面这些原则,不单单只是软件开发,可以推广到其它生产活动中,甚至我们的生活中. Don’t Repeat Yourself(DRY) DRY是一个最简单的法则,也是最容易被理解的.但它也可能是最难被应用的(因为要做到这样,我们需要在泛型设计上做相当的努力,这并不是一件容易 的事).它意味着,当我们在两个或多个地方的时候发现一些相似的代码的时候,我们需要把他们的共性抽象出来形一个唯一的新方法,并且改变现有的地方的代码 让他们以一些

设计模式——6大设计原则

1.单一职责原则 单一职责原则的英文名称是Single Responsibility Principle,简称是SRP. 单一职责的定义是:有且仅有一个原因引起类的变更. 单一职责原则要求一个接口或者一个类只有一个原因引起变化,也就是说一个接口或类只有一个职责,它就负责一件事情. 建议是:接口一定要做到单一职责,类的世界尽量做到只有一个原因引起变化.2.里氏替换原则 里氏替换原则的英文名称是Liskov Substitution Principle,简称是LSP. 里氏替换原则的定义:所有引用基

一些软件设计原则【转载】

本文一定要转,总结得非常好, 设计必读. 转自陈皓老师的 <一些软件设计的原则>,根据自己的理解调整了下顺序,少部分字句做了修改. 一个好的程序员通常由其操作技能.知识水平,经验层力和能力四个方面组成.在这里想和大家说说设计中的一些原则,我认为这些东西属于长期经验总结出来的知识.这些原则,每一个程序员都应该了解.但是请不要教条主义,在使用的时候还是要多多考虑实际情况.其实,下面这些原则,不单单只是软件开发,可以推广到其它生产活动中,甚至我们的生活中. 根本设计原则 根本设计原则是我认为的最最基

软件设计原则和方法通俗理解

网上有很多关于软件设计原则的说法,很精确,很官方,但是对于有些初学者来说可能是不知所云,到最后把自己给郁闷到了,学习软件应该是一件愉快的事情. 那么软件设计原则有哪些呢? (1)可靠性 做出一个可靠的软件,跟女人找一个可靠的男人一样,女人找男人,需要男人品质好,人品好,靠谱,可信赖,可依靠,身材高大,等等.软件设计也是一样,在软件的设计阶段就要非常注意软件的可靠性,不要等到最后用的时候发现软件这里不行那里不行,或者说在使用软件过程中一旦发现问题还是可以恢复使用,不能直接崩溃. (2)健壮性 这个

初识设计模式、软件设计的六大原则

总结:本篇文字分为两个部分.第一部分:设计模式基本常识:第二部分:软件设计中的六大原则,并详细分析了单一职责原则.(本篇文章的时间轴参考:为知笔记支撑文件夹\Java设计模式(时间序列图).vsdx) 部分一:初识设计模式 什么是设计模式?James拿到这个论点时,很是迷惑! 模式?是不是一个模子?模式识别--计算机领域的经典问题? 设计模拟?软件的设计模式?不懂!!! 但是在实际编码.调试过程中,James的遇到过很是难解的问题:工程代码中有过多的冗余代码--代码复用性不高:需求一旦改变,需要