拿什么拯救你,初涉架构

本身的序文表明:

 本文原著者:Radoslaw
Sadowski,原来的小说链接为:C#
BAD PRACTICES: Learn how to make a good code by bad
example。

本系列还有任何小说,后续将慢慢翻译。

 

此文为译文,最初的作品地址请点击。
本文通过重构3个废品代码,演说了什么写出美好的代码。开发人士及代码审核人士需遵循此标准开发和复核代码。此规范以C#为例,JAVA的童鞋一并参考,C++的童鞋自行脑补吧。

走向.NET架构划设想计—第叁章—分层设计,初涉架构(中篇) 

引言:

自家的名字叫Radoslaw
Sadowski,笔者后天是一个微软技术开发人士。作者从初阶工作时就1间接触的微软技术.

在做事一年后,小编看出的质感很差的代码的数量基本上都得以写成一整本书了。

那一个经验让笔者变成了3个想要清洁代码的性心理障碍病人。

写那篇小说的指标是为着通过显示品质很差的类的例子来验证什么下笔出到底的、可延长的和可保险的代码。笔者会通过好的书写方式和设计形式来解释坏的代码带来的难题,以及替换他的好的缓解措施。

首先片段是指向那1个负有C#基础知识的开发人士——小编会议及展览示1些大面积的失实,然后再展现一些让代码变得可读性的方法与技术。高级部分重点针对这一个至少存有设计形式概念的开发职员——我将会显示完全彻底的、单元可测试的代码。

为了能够知道那篇小说你需求至少精晓以下四个部分的基本知识:

  • C#语言
  • 借助于注入、工厂设计情势、策略设计方式

正文中所涉及的事例都将会是切实中如实的现实的天性,而不是用装饰格局来做披萨或然用政策格局来做总计器那样的言传身教。

(ps解释:看过设计情势相关的书本的人相应会知晓许多那地点的图书都是用那种例子,只是为着协助读者了然设计方式)

                           
  美高梅开户网址 1 
     
  美高梅开户网址 2

因为本身发现这系列型的产品不好用来分解,相反这几个理论性的例证却是十分适合用来在本文中做解释的。

作者们平日会听到说不要用那一个,要用这一个,然则却不精晓这种替换的理由。今天自身将会全力解释和验证那三个好的书写习惯以及设计方式是的确是在营救大家的付出生活!


此文为译文,最初的作品地址请点击。
本文通过重构一个遗弃物代码,演讲了什么写出不错的代码。开发职员及代码审核职员需依照此标准开发和查处代码。此标准以C#为例,JAVA的童鞋一并参考,C++的童鞋自行脑补吧。

  前言:自从上篇发布之后,大家反映了重重题材,因为前篇讲的东西不是很深,或许我们看完之后未有啥样感觉.本章(前篇,中篇,后篇)的关键目标其实首先是提出倒霉的设计,然后比较的建议三个对立比较客观的分支架构,同时本篇也为连续讲述架构形式和设计方式等的文章做个铺垫。

 提示:

  •  在本文中自己不会花时间来讲解C#的特点和事关形式等等(笔者也诠释不完),网上有诸多关于这方面包车型大巴好的辩护的例子。小编将汇总讲述怎么着在大家平常工作中选拔那个东西。
  • 事例是一种相比便于的凸起大家要评释的题材的办法,不过仅限于描述的题材——因为自身发现当自个儿在求学怎么着包涵注重大代码的例证时,小编意识在明亮作品的完整思索方面会有不便。
  •  笔者不是说小编文中说的方式是惟壹的缓解办法,作者只是能有限协助这几个情势将会是让您的代码变得更加高品质的门路。
  • 自作者并不珍重下边那几个代码的什么样错误处理,日志记录等等。作者要表明的只是用来消除壹般编码一些题指标措施。

那就起来吧….

简介

那篇小说的指标是显得什么将壹段垃圾代码重构成2个干净的、可扩充性和可有限帮助的代码。我将分解什么通过一流实践和越来越好的设计方式来改写它。

读书本文你要求有以下基础:

  • c# 基础
  • 凭借注入,工厂格局,策略形式

此文中的例子源于实际项目,那里不会有什么样使用装饰情势营造的披萨,也不会利用政策形式的总计器,那么些事例是老大好的认证,不过它们很难被在事实上项目中使用。


 

这么些倒霉透了的类…

上边包车型地铁例证是我们切实中的类:

 1 public class Class1
 2 {
 3   public decimal Calculate(decimal amount, int type, int years)
 4   {
 5     decimal result = 0;
 6     decimal disc = (years > 5) ? (decimal)5/100 : (decimal)years/100; 
 7     if (type == 1)
 8     {
 9       result = amount;
10     }
11     else if (type == 2)
12     {
13       result = (amount - (0.1m * amount)) - disc * (amount - (0.1m * amount));
14     }
15     else if (type == 3)
16     {
17       result = (0.7m * amount) - disc * (0.7m * amount);
18     }
19     else if (type == 4)
20     {
21       result = (amount - (0.5m * amount)) - disc * (amount - (0.5m * amount));
22     }
23     return result;
24   }
25 }

地方这么些事例真的是一种越发差的书写方式。你能知晓那么些类是用来干嘛的么?那么些事物是用来做一些意外的演算的么?大家小说就从她起先入手来讲学吧…

先天自个儿来告诉你,刚刚那1个类是用来当消费者在网上买东西的时候为他们总计对应折扣的折扣总计和管理的类。

-难以置信吧!

-可是那是真的!

那种写法真的是将难以阅读、难以保险和麻烦扩张这几种集合在同步了,而且装有着太差的书写习惯和错误的情势。

除外还有别的什么难点么?

1.命有名的模特式-从源代码中大家能够连蒙带猜猜测出来这几个总计方法和出口结果是怎样。而且我们想要从这一个类中提取总计算法将会是一件10分困难的工作。

如此带来的侵凌是:

最要紧的标题是:浪费时间,

美高梅开户网址 3

 

假若大家须要满意客户的经济贸易咨询,要像她们出示算法细节,只怕我们须要修改这段代码,那将开销大家相当长的年月去通晓大家的盘算情势的逻辑。尽管大家不记录她或重构代码,下次大家/其余开发职员再看那段代码的时候,还是须求开支一样的时刻来研商这个代码是干嘛的。而且在修改的同时还易于失误,导致原先的测算整体失误。

 2.魔法数字

 美高梅开户网址 4

在这些事例中type是变量,你能猜到它意味着着客户账户的等级么?If-else
if
说话是用来落实如何选用总括出产品价格折扣的办法。

当今我们不知道怎么的账户是一,二,3或四。将来想象一下,当您不得不为了那三个有价值的VIP客户改变他们的折扣总括方式的时候,你试着从那三个代码中找出修改的办法—那一个进度只怕会成本你很短的日子不说,还很有望犯错以至于修改那个基础的相似的客户的账户,毕竟像二依旧三那么些用语毫无描述性的。可是在大家犯错现在,那么些1般的客户却很喜欢,因为她俩取得了VIP客户的折扣。:)

3.未有明显的bug

因为大家的代码质量很差,而且可读性子外差,所以大家只怕无限制就疏忽掉很多那几个重大的作业。想象一下,今后黑马在系统中追加1种新的客户类型-金卡用户,而在我们的连串中任何壹种新的账户类型最终获得的标价将是0元。为啥呢?因为在大家的if-else
if
语句中平昔不别的景况是满足新的意况的,所以1旦是未处理过的账户类型,最终重回值都将变成0。1旦大家的老总娘发现那件事,他将会老羞成怒-终归她一度免费卖给这么用户很多浩大东西了!

美高梅开户网址 5

4.从不可读性

小编们无法不承认上边那段代码的可读性是真的倒霉。

她让大家开销了太多的小时去掌握那段代码,同时期码隐藏不当的可能率太大了,而那便是尚未可读性的最根本的概念。

 5.魔法数字(再一次)

您从代码中能知道好像0.壹,0.七,0.5那一个数字的情致么?好的,小编肯定本身不明了。唯有大家友好编排这几个代码我们才清楚那是什么看头,外人是心有余而力不足清楚的。

你尝试想想倘若让您改改下边那句代码,你会怎么:

result = (amount – (0.5m * amount)) – disc * (amount – (0.5m *
amount));

因为那一个方法完全不可读,所以你改改的历程中不得不尝试着把第一个0.伍改成0.4而保持第二个0.五不懂。那大概会是一个bug,可是却是最佳的最合适的修章。因为那么些0.伍如何都不曾报告大家。

同等的事也存在将years变量转换成disc变量的变换进度中:

decimal disc = (years > 5) ? (decimal)5/100 : (decimal)years/100;

那是用来计量折扣率的,会经过账户在我们系统的时刻的百分比来获取。好的,那么现在题材来了,假诺时光刚刚好正是5啊?

6.简短-不要频仍做无用功

虽说第二应声的时候不简单看出来,然而仔细讨论一下就会发觉:大家的代码里有无数再度的地点。例如:disc *
(amount – (0.1m * amount));

而与之有壹样效劳的还有(只是变了2个参数而已):disc * (amount –
(0.5m * amount))

在那三个算术中,唯1的区分就只是一个静态参数,而小编辈完全能够用1个可变的参数来代表。

借使大家不试着在写代码的时候从一直ctri+c,ctrl+v中摆脱出来,那我们将赶上的题材正是大家只可以修改代码中的部分机能,因为大家不知底有多少地点须求修改。上面包车型地铁逻辑是总括出在大家系统中种种客户对应年限得到的折扣,所以一旦大家只是贸然修改两到3处,很简单造成别的地点的内外不一致。

7.种种类具有太多的扑朔迷离的职责区域

笔者们写的类至少背负了四个职责:

  1. 分选计算的运算法则
  2. 为每种差异景观的账户总结折扣率
  3. 依据每一个客人的定期总括出相应的折扣率

本条背离了单纯性权利原则。那么那会推动什么损伤吗?如果我们想要改变上诉一个特点中的五个,那就代表只怕会碰触到有的别样的我们并不想修改的性状。所以在改动的时候大家只能再一次测试全体的类,那么那就招致了很重的时光的荒废。

1段垃圾代码

在我们真实的制品中有这么1个类:

public class Class1
{
  public decimal Calculate(decimal amount, int type, int years)
  {
decimal result = 0;
decimal disc = (years > 5) ? (decimal)5/100 : (decimal)years/100; 
if (type == 1)
{
  result = amount;
}
else if (type == 2)
{
  result = (amount - (0.1m * amount)) - disc * (amount - (0.1m * amount));
}
else if (type == 3)
{
  result = (0.7m * amount) - disc * (0.7m * amount);
}
else if (type == 4)
{
  result = (amount - (0.5m * amount)) - disc * (amount - (0.5m * amount));
}
return result;
  }
}

那是1段分外倒霉的代码(贰胡:假如你没以为那段代码很倒霉,这您日前景况可能很糟糕了),我们不太明了那几个类的目标是何等。它恐怕是一个网上商城的折扣管理类,负责为客户总括折扣。

拿什么拯救你,初涉架构。本条类完全具备了不可读、不可维护、不可扩张的风味,它接纳了成都百货上千坏的实施和反情势的布署性。

上面大家渐渐分析那里毕竟有稍许问题?

  • 取名难题 –
    大家只好通过估计这一个类到底是为着总计什么。这实际上是在浪费时间。
    若是咱们并未有有关文书档案也许重构那段代码,这大家下1次恐怕须要花多量的年月才能精晓那段代码的现实意思。

  • 魔数 –
    在那一个例子中,你能猜到变量type是指客户账户的情况呢。通过if-else来挑选总结降价后的产品价格。
    当今,我们压根不明了账户状态壹,二,三,6分头是何许看头。
    别的,你知道0.1,0.7,0.伍都以哪些意思吧?
    让我们想像一下,倘诺您要修改下边那行代码:
    result = (amount - (0.5m * amount)) - disc * (amount - (0.5m * amount));

  • 隐形的BUG –
    因为代码乌烟瘴气,大家或然会失去卓殊首要的事务。试想一下,借使大家的系统中新增了一类账户状态,而新的账户等级不满足任何3个if-else条件。那时,重返值会固定为0。

  • 不可读 –
    大家不得不认同,那是壹段不可读的代码。不可读=越来越多的精晓时间+扩展发生错误的风险

  • DPAJEROY – 不要爆发重复的代码
    咱俩恐怕不能够1眼就找出它们,但它们确实存在。
    例如:disc *(amount - (0.1m * amount));
    同等的逻辑:
    disc *(amount - (0.5m * amount));
    此间唯有贰个数值不平等。如果大家鞭长莫及抽身再度的代码,大家会遇到许多题材。比如某段代码在四个地方有再一次,当大家要求修改那有的逻辑时,你很可能只修改到了2至三处。

  • 单一职责规范
    本条类至少存有八个职分:
    1 选择总括算法
    二 依照账户状态计算折扣
    3 依照账户网龄计算折扣
    它违反了10足任务规范。那会带来如何风险?要是大家就要修改第三个成效的话,会影响到其余第二个功效。那就象征,大家每一趟修改都会变动大家本不想修改的代码。因而,我们不得不对全体类进行测试,那实则很浪费时间。

简介

那篇文章的目标是呈现如何将1段垃圾代码重构成3个彻底的、可扩展性和可保养的代码。笔者将分解怎么样通过一级实践和更加好的设计格局来改写它。

翻阅本文你要求有以下基础:

  • c# 基础
  • 依傍注入,工厂情势,策略情势

此文中的例子源于实际项目,那里不会有何使用装饰情势构建的披萨,也不会动用政策情势的总计器,这几个事例是十三分好的表达,然则它们很难被在实质上项目中央银行使。

本篇的议题如下:

一. style=”font-family: 大篆; color: red”>评释示例须要

二. style=”font-family: 陶文; color: red”>业务层设计

3. style=”font-family: 行书; color: red”>服务层设计

肆. style=”font-family: 陶文; color: red”>数据访问层设计

5. style=”font-family: 黑体; color: red”>呈现层设计

6. style=”color: red”>UI style=”font-family: 宋体; color: red”>层设计

那就起来重构吧…

在接下去的九个步骤中自小编将向你显得大家怎样制止上诉难题来营造三个根本的易维护,同时又有利于单元测试的看起来一目明白的代码。

 

重构

经过以下9布,笔者会告诉你们如何防止上述风险并促成三个根本的、可保险的、可测试的代码。

  1. 命名,命名,命名
    那是精美代码的最首要方面之一。大家只须要改变方法,参数和变量的命名。未来,咱们可以确切的明白下边包车型大巴类是背负什么的了。

    public decimal ApplyDiscount(decimal price, int accountStatus, int timeOfHavingAccountInYears)
    {
        decimal priceAfterDiscount = 0;
        decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > 5) ? (decimal)5 / 100 : (decimal)timeOfHavingAccountInYears / 100;
        if (accountStatus == 1)
        {
            priceAfterDiscount = price;
        }
        else if (accountStatus == 2)
        {
            priceAfterDiscount = (price - (0.1m * price)) - (discountForLoyaltyInPercentage * (price - (0.1m * price)));
        }
        else if (accountStatus == 3)
        {
            priceAfterDiscount = (0.7m * price) - (discountForLoyaltyInPercentage * (0.7m * price));
        }
        else if (accountStatus == 4)
        {
            priceAfterDiscount = (price - (0.5m * price)) - (discountForLoyaltyInPercentage * (price - (0.5m * price)));
        }
    
        return priceAfterDiscount;
    }
    

而是大家任然不知晓账户状态壹,二,三到底是怎么样看头。

  1. 魔数
    在C#中幸免魔数大家壹般接纳枚举来替换它们。那里运用AccountStatus
    枚举来替换if-else中的魔数。
    public enum AccountStatus { NotRegistered = 1, SimpleCustomer = 2, ValuableCustomer = 3, MostValuableCustomer = 4 }
    今昔大家来探望重构后的类,大家能够很简单的表露哪多少个账户状态应当用什么算法来测算折扣。混合账户状态的高危机快速的大跌了。

     public class DiscountManager
     {
         public decimal ApplyDiscount(decimal price, AccountStatus accountStatus, int timeOfHavingAccountInYears)
         {
             decimal priceAfterDiscount = 0;
             decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > 5) ? (decimal)5 / 100 : (decimal)timeOfHavingAccountInYears / 100;
    
             if (accountStatus == AccountStatus.NotRegistered)
             {
                 priceAfterDiscount = price;
             }
             else if (accountStatus == AccountStatus.SimpleCustomer)
             {
                 priceAfterDiscount = (price - (0.1m * price)) - (discountForLoyaltyInPercentage * (price - (0.1m * price)));
             }
             else if (accountStatus == AccountStatus.ValuableCustomer)
             {
                 priceAfterDiscount = (0.7m * price) - (discountForLoyaltyInPercentage * (0.7m * price));
             }
             else if (accountStatus == AccountStatus.MostValuableCustomer)
             {
                 priceAfterDiscount = (price - (0.5m * price)) - (discountForLoyaltyInPercentage * (price - (0.5m * price)));
             }
             return priceAfterDiscount;
         }
     }
    
  2. 越来越多的代码可读性
    在这一步中,大家应用switch-case 来替换 if-else
    if
    来增强代码可读性。
    并且,小编还将1些长度相当长的语句才分成两行。比如:**priceAfterDiscount = (price - (0.5m * price)) - (discountForLoyaltyInPercentage * (price - (0.5m * price)));**
    被涂改为:
    **priceAfterDiscount = (price - (0.5m * price));priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);**
    以下是欧洲经济共同体的修改:

     public class DiscountManager
     {
         public decimal ApplyDiscount(decimal price, AccountStatus accountStatus, int timeOfHavingAccountInYears)
         {
             decimal priceAfterDiscount = 0;
             decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > 5) ? (decimal)5 / 100 : (decimal)timeOfHavingAccountInYears / 100;
             switch (accountStatus)
             {
                 case AccountStatus.NotRegistered:
                     priceAfterDiscount = price;
                     break;
                 case AccountStatus.SimpleCustomer:
                     priceAfterDiscount = (price - (0.1m * price));
                     priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
                     break;
                 case AccountStatus.ValuableCustomer:
                     priceAfterDiscount = (0.7m * price);
                     priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
                     break;
                 case AccountStatus.MostValuableCustomer:
                     priceAfterDiscount = (price - (0.5m * price));
                     priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
                     break;
             }
             return priceAfterDiscount;
         }
     }
    
  3. 免除隐形的BUG
    正如笔者辈从前提到的,大家的ApplyDiscount方法或许将为新的客户情形重回0。
    大家怎么样才能消除那几个标题?答案正是抛出NotImplementedException。
    当咱们的诀窍获得账户状态作为输入参数,不过参数值或者包蕴我们未规划到的无人问津情形。这时,我们无法怎么样也不做,抛出相当是此时最佳的做法。

        public decimal ApplyDiscount(decimal price, AccountStatus accountStatus, int timeOfHavingAccountInYears)
        {
            decimal priceAfterDiscount = 0;
            decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > 5) ? (decimal)5 / 100 : (decimal)timeOfHavingAccountInYears / 100;
            switch (accountStatus)
            {
                case AccountStatus.NotRegistered:
                    priceAfterDiscount = price;
                    break;
                case AccountStatus.SimpleCustomer:
                    priceAfterDiscount = (price - (0.1m * price));
                    priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
                    break;
                case AccountStatus.ValuableCustomer:
                    priceAfterDiscount = (0.7m * price);
                    priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
                    break;
                case AccountStatus.MostValuableCustomer:
                    priceAfterDiscount = (price - (0.5m * price));
                    priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
                    break;
                default:
                    throw new NotImplementedException();
            }
            return priceAfterDiscount;
        }
    
  4. 解析算法
    在这几个例子中,大家经过五个正经来测算客户折扣:

  • 账户状态

  • 账户网龄
    透过网龄计算的算法都好像那样:
    (discountForLoyaltyInPercentage * priceAfterDiscount)
    而是对于账户状态为ValuableCustomer的算法却是:
    0.7m * price
    大家把它修改成和任何账户状态壹样的算法:
    price - (0.3m * price)

          public class DiscountManager
         {
         public decimal ApplyDiscount(decimal price, AccountStatus     accountStatus, int timeOfHavingAccountInYears)
         {
         decimal priceAfterDiscount = 0;
         decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > 5) ? (decimal)5 / 100 : (decimal)timeOfHavingAccountInYears / 100;
         switch (accountStatus)
         {
             case AccountStatus.NotRegistered:
                 priceAfterDiscount = price;
                 break;
             case AccountStatus.SimpleCustomer:
                 priceAfterDiscount = (price - (0.1m * price));
                 priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
                 break;
             case AccountStatus.ValuableCustomer:
                 priceAfterDiscount = (price - (0.3m * price));
                 priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
                 break;
             case AccountStatus.MostValuableCustomer:
                 priceAfterDiscount = (price - (0.5m * price));
                 priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
                 break;
             default:
                 throw new NotImplementedException();
         }
         return priceAfterDiscount;
         }
         }
    
  1. 解除魔数的另一种格局
    行使静态常量来替换魔数。0.一m,0.二m,0.三m,作者m,大家并不知道它们是怎么看头。
    此外decimal discountForLoyaltyInPercentage =
    (timeOfHavingAccountInYears > 5) ? (decimal)5/100 :
    (decimal)timeOfHavingAccountInYears/100;
    中,数字五也格外神秘。
    大家必须让它们更拥有描述性,那时使用常量会是多个相比好的主意。
    咱俩来定义几个静态类:

     public static class Constants
     {
     public const int MAXIMUM_DISCOUNT_FOR_LOYALTY = 5;
     public const decimal DISCOUNT_FOR_SIMPLE_CUSTOMERS = 0.1m;
     public const decimal DISCOUNT_FOR_VALUABLE_CUSTOMERS = 0.3m;
     public const decimal DISCOUNT_FOR_MOST_VALUABLE_CUSTOMERS = 0.5m;
     }
    

随后修改DiscountManager类:

    public class DiscountManager
    {
    public decimal ApplyDiscount(decimal price, AccountStatus accountStatus, int timeOfHavingAccountInYears)
    {
        decimal priceAfterDiscount = 0;
        decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > Constants.MAXIMUM_DISCOUNT_FOR_LOYALTY) ? (decimal)Constants.MAXIMUM_DISCOUNT_FOR_LOYALTY / 100 : (decimal)timeOfHavingAccountInYears / 100;
        switch (accountStatus)
        {
            case AccountStatus.NotRegistered:
                priceAfterDiscount = price;
                break;
            case AccountStatus.SimpleCustomer:
                priceAfterDiscount = (price - (Constants.DISCOUNT_FOR_SIMPLE_CUSTOMERS * price));
                priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
                break;
            case AccountStatus.ValuableCustomer:
                priceAfterDiscount = (price - (Constants.DISCOUNT_FOR_VALUABLE_CUSTOMERS * price));
                priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
                break;
            case AccountStatus.MostValuableCustomer:
                priceAfterDiscount = (price - (Constants.DISCOUNT_FOR_MOST_VALUABLE_CUSTOMERS * price));
                priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
                break;
            default:
                throw new NotImplementedException();
        }
        return priceAfterDiscount;
    }
    }
  1. 破除重复的代码
    为了排除重复的代码,那里将一部分算法提取出来。首先,我们树立多个扩展方法:

     public static class PriceExtensions
     {
     public static decimal ApplyDiscountForAccountStatus(this decimal price, decimal discountSize)
     {
         return price - (discountSize * price);
     }
    
     public static decimal ApplyDiscountForTimeOfHavingAccount(this decimal price, int timeOfHavingAccountInYears)
     {
         decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > Constants.MAXIMUM_DISCOUNT_FOR_LOYALTY) ? (decimal)Constants.MAXIMUM_DISCOUNT_FOR_LOYALTY / 100 : (decimal)timeOfHavingAccountInYears / 100;
         return price - (discountForLoyaltyInPercentage * price);
     }
     }
    

透过措施名称,大家就能够掌握它的任务是何等,未来涂改大家的例子:

    public class DiscountManager
    {
    public decimal ApplyDiscount(decimal price, AccountStatus accountStatus, int timeOfHavingAccountInYears)
    {
        decimal priceAfterDiscount = 0;
        switch (accountStatus)
        {
            case AccountStatus.NotRegistered:
                priceAfterDiscount = price;
                break;
            case AccountStatus.SimpleCustomer:
                priceAfterDiscount = price.ApplyDiscountForAccountStatus(Constants.DISCOUNT_FOR_SIMPLE_CUSTOMERS)
                  .ApplyDiscountForTimeOfHavingAccount(timeOfHavingAccountInYears);
                break;
            case AccountStatus.ValuableCustomer:
                priceAfterDiscount = price.ApplyDiscountForAccountStatus(Constants.DISCOUNT_FOR_VALUABLE_CUSTOMERS)
                  .ApplyDiscountForTimeOfHavingAccount(timeOfHavingAccountInYears);
                break;
            case AccountStatus.MostValuableCustomer:
                priceAfterDiscount = price.ApplyDiscountForAccountStatus(Constants.DISCOUNT_FOR_MOST_VALUABLE_CUSTOMERS)
                  .ApplyDiscountForTimeOfHavingAccount(timeOfHavingAccountInYears);
                break;
            default:
                throw new NotImplementedException();
        }
        return priceAfterDiscount;
    }
    }
  1. 除去没用的代码
    大家应当写出大约的代码,因为简短的代码=收缩BUG发生的机率,并且也让我们缩小精晓事情逻辑的光阴。
    大家发现,那里三种情状的客户调用了一样的艺术:
    .ApplyDiscountForTimeOfHavingAccount(timeOfHavingAccountInYears);
    那里能够统一代码:

     public class DiscountManager
     {
     public decimal ApplyDiscount(decimal price, AccountStatus accountStatus, int timeOfHavingAccountInYears)
     {
         decimal priceAfterDiscount = 0;
         switch (accountStatus)
         {
             case AccountStatus.NotRegistered:
                 priceAfterDiscount = price;
                 break;
             case AccountStatus.SimpleCustomer:
                 priceAfterDiscount = price.ApplyDiscountForAccountStatus(Constants.DISCOUNT_FOR_SIMPLE_CUSTOMERS);
                 break;
             case AccountStatus.ValuableCustomer:
                 priceAfterDiscount = price.ApplyDiscountForAccountStatus(Constants.DISCOUNT_FOR_VALUABLE_CUSTOMERS);
                 break;
             case AccountStatus.MostValuableCustomer:
                 priceAfterDiscount = price.ApplyDiscountForAccountStatus(Constants.DISCOUNT_FOR_MOST_VALUABLE_CUSTOMERS);
                 break;
             default:
                 throw new NotImplementedException();
         }
         priceAfterDiscount = priceAfterDiscount.ApplyDiscountForTimeOfHavingAccount(timeOfHavingAccountInYears);
         return priceAfterDiscount;
     }
     }
    

九.最终,获得根本的代码
谈起底,让大家透过引进信赖注入和工厂方法格局来取得终极的本子吧。
先来看卡最终结出:

    public class DiscountManager
    {
    private readonly IAccountDiscountCalculatorFactory _factory;
    private readonly ILoyaltyDiscountCalculator _loyaltyDiscountCalculator;

    public DiscountManager(IAccountDiscountCalculatorFactory factory, ILoyaltyDiscountCalculator loyaltyDiscountCalculator)
    {
        _factory = factory;
        _loyaltyDiscountCalculator = loyaltyDiscountCalculator;
    }

    public decimal ApplyDiscount(decimal price, AccountStatus accountStatus, int timeOfHavingAccountInYears)
    {
        decimal priceAfterDiscount = 0;
        priceAfterDiscount = _factory.GetAccountDiscountCalculator(accountStatus).ApplyDiscount(price);
        priceAfterDiscount = _loyaltyDiscountCalculator.ApplyDiscount(priceAfterDiscount, timeOfHavingAccountInYears);
        return priceAfterDiscount;
    }
    }

    public interface ILoyaltyDiscountCalculator
    {
    decimal ApplyDiscount(decimal price, int timeOfHavingAccountInYears);
    }

    public class DefaultLoyaltyDiscountCalculator : ILoyaltyDiscountCalculator
    {
    public decimal ApplyDiscount(decimal price, int timeOfHavingAccountInYears)
    {
        decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > Constants.MAXIMUM_DISCOUNT_FOR_LOYALTY) ? (decimal)Constants.MAXIMUM_DISCOUNT_FOR_LOYALTY / 100 : (decimal)timeOfHavingAccountInYears / 100;
        return price - (discountForLoyaltyInPercentage * price);
    }
    }

    public interface IAccountDiscountCalculatorFactory
    {
    IAccountDiscountCalculator GetAccountDiscountCalculator(AccountStatus accountStatus);
    }

    public class DefaultAccountDiscountCalculatorFactory : IAccountDiscountCalculatorFactory
    {
    public IAccountDiscountCalculator GetAccountDiscountCalculator(AccountStatus accountStatus)
    {
        IAccountDiscountCalculator calculator;
        switch (accountStatus)
        {
            case AccountStatus.NotRegistered:
                calculator = new NotRegisteredDiscountCalculator();
                break;
            case AccountStatus.SimpleCustomer:
                calculator = new SimpleCustomerDiscountCalculator();
                break;
            case AccountStatus.ValuableCustomer:
                calculator = new ValuableCustomerDiscountCalculator();
                break;
            case AccountStatus.MostValuableCustomer:
                calculator = new MostValuableCustomerDiscountCalculator();
                break;
            default:
                throw new NotImplementedException();
        }

        return calculator;
        }
    }

    public interface IAccountDiscountCalculator
    {
    decimal ApplyDiscount(decimal price);
    }

    public class NotRegisteredDiscountCalculator : IAccountDiscountCalculator
    {
    public decimal ApplyDiscount(decimal price)
    {
        return price;
    }
    }

    public class SimpleCustomerDiscountCalculator : IAccountDiscountCalculator
    {
    public decimal ApplyDiscount(decimal price)
    {
        return price - (Constants.DISCOUNT_FOR_SIMPLE_CUSTOMERS * price);
    }
    }

    public class ValuableCustomerDiscountCalculator : IAccountDiscountCalculator
    {
    public decimal ApplyDiscount(decimal price)
    {
        return price - (Constants.DISCOUNT_FOR_VALUABLE_CUSTOMERS * price);
    }
    }

    public class MostValuableCustomerDiscountCalculator : IAccountDiscountCalculator
    {
    public decimal ApplyDiscount(decimal price)
    {
        return price - (Constants.DISCOUNT_FOR_MOST_VALUABLE_CUSTOMERS * price);
    }
    }

先是,大家摆脱了扩展方法(静态类),假若我们想对ApplyDiscount办法开始展览单元测试是比较困难的,撤除我们对PriceExtensions扩张类也进行测试。
为了幸免这几个难题,大家创立了DefaultLoyaltyDiscountCalculator类来替换ApplyDiscountForTimeOfHavingAccount这么些扩充方法,此类还完结了ILoyaltyDiscountCalculator接口。未来,当我们要测试DiscountManager类时,大家只构造函数注入ILoyaltyDiscountCalculator接口的贯彻即可。那里运用了信赖注入。
经过这样做,大家将网龄折扣的算法迁移到类似DefaultLoyaltyDiscountCalculator的分化类中,那样当大家修改某贰个算法不会覆盖到其余业务。
对此依据账户状态来测算折扣的业务,大家必要在DiscountManager中除去七个职分:

  • 根据账户状态选用总计的算法

  • 贯彻计算算法
    那里我们透过DefaultAccountDiscountCalculatorFactory工厂类来缓解那几个题材,DefaultAccountDiscountCalculatorFactory工厂类完成了IAccountDiscountCalculatorFactory接口。
    笔者们的工厂将决定取舍哪三个倒扣算法。接着,工厂类被通过构造函数注入到DiscountManager类中。
    上边小编只供给在DiscountManager 中使用工厂:
    priceAfterDiscount = _factory.GetAccountDiscountCalculator(accountStatus).ApplyDiscount(price);
    如上,我们消除了第3个难题,上边大家供给贯彻计算算法。遵照账户状态,提供分裂的算法,那刚好适合政策形式。我们需求创设多个政策:NotRegisteredDiscountCalculator,SimpleCustomerDiscountCalculator,MostValuableCustomerDiscountCalculator早已悬空出来的接口IAccountDiscountCalculator
    好了,将来我们有可一段干净可读的代码了,那段代码中拥有的类都唯有3个职分:

  • DiscountManager – 管理

  • DefaultLoyaltyDiscountCalculator – 网龄总括折扣

  • DefaultAccountDiscountCalculatorFactory – 依照账户状态选择折扣策略

  • NotRegisteredDiscountCalculator,SimpleCustomerDiscountCalculator,MostValuableCustomerDiscountCalculator-总计折扣算法
    大家来比较一下修改前后的代码:

      public class Class1
      {
      public decimal Calculate(decimal amount, int type, int years)
      {
          decimal result = 0;
          decimal disc = (years > 5) ? (decimal)5 / 100 : (decimal)years / 100;
          if (type == 1)
          {
              result = amount;
          }
          else if (type == 2)
          {
              result = (amount - (0.1m * amount)) - disc * (amount - (0.1m * amount));
          }
          else if (type == 3)
          {
              result = (0.7m * amount) - disc * (0.7m * amount);
          }
          else if (type == 4)
          {
              result = (amount - (0.5m * amount)) - disc * (amount - (0.5m * amount));
          }
          return result;
      }
      }
    

修改后:

    public class DiscountManager
    {
    private readonly IAccountDiscountCalculatorFactory _factory;
    private readonly ILoyaltyDiscountCalculator _loyaltyDiscountCalculator;

    public DiscountManager(IAccountDiscountCalculatorFactory factory, ILoyaltyDiscountCalculator loyaltyDiscountCalculator)
    {
        _factory = factory;
        _loyaltyDiscountCalculator = loyaltyDiscountCalculator;
    }

    public decimal ApplyDiscount(decimal price, AccountStatus accountStatus, int timeOfHavingAccountInYears)
    {
        decimal priceAfterDiscount = 0;
        priceAfterDiscount = _factory.GetAccountDiscountCalculator(accountStatus).ApplyDiscount(price);
        priceAfterDiscount = _loyaltyDiscountCalculator.ApplyDiscount(priceAfterDiscount, timeOfHavingAccountInYears);
        return priceAfterDiscount;
    }
    }

一段垃圾代码

在大家真正的产品中有那般1个类:

public class Class1
{
public decimal Calculate(decimal amount, int type, int years)
{
decimal result = 0;
decimal disc = (years > 5) ? (decimal)5/100 : (decimal)years/100; 
if (type == 1)
{
  result = amount;
}
else if (type == 2)
{
  result = (amount - (0.m * amount)) - disc * (amount - (0.m * amount));
}
else if (type == 3)
{
  result = (0.m * amount) - disc * (0.m * amount);
}
else if (type == 4)
{
  result = (amount - (0.m * amount)) - disc * (amount - (0.m * amount));
}
return result;
}
}

那是1段卓殊不好的代码(2胡:如若你没觉得那段代码很倒霉,那您眼下景象恐怕很倒霉了),大家不太领悟这一个类的目标是怎么。它恐怕是一个网上商城的折扣管理类,负责为客户总计折扣。

以此类完全拥有了不足读、不可维护、不可增添的特点,它选拔了广大坏的履行和反情势的统一筹划。

下边大家稳步分析那里究竟有稍许难点?

  • 命名难点 –
    我们只可以通过揣摸那一个类到底是为了总括什么。那实际上是在浪费时间。
    要是大家向来不相关文书档案或许重构那段代码,那大家下三回只怕需求花大批量的时间才能分晓那段代码的求实意思。

  • 魔数 –
    在那几个例子中,你能猜到变量type是指客户账户的情景呢。通过if-else来选取总计优惠后的产品价格。
    未来,大家压根不精通账户状态一,二,三,四分别是何许看头。
    其它,你掌握0.1,0.7,0.5都是什么意思呢?
    让大家想像一下,假若您要修改下边那行代码:
    result = (amount - (0.5m * amount)) - disc * (amount - (0.5m * amount));

  • 藏匿的BUG –
    因为代码非凡糟糕,大家大概会失掉格外主要的政工。试想一下,假如我们的体系中新增了壹类账户状态,而新的账户等级不知足任何叁个if-else条件。那时,重回值会固定为0。

  • 不足读 –
    咱们不得不认可,那是壹段不可读的代码。不可读=更多的明白时间+增加爆发错误的风险

  • D昂CoraY – 不要发生重复的代码
    大家兴许不能1眼就找出它们,但它们确实存在。
    例如:disc *(amount - (0.1m * amount)); 同样的逻辑:
    disc *(amount - (0.5m * amount));
    那里只有一个数值不雷同。假如我们无能为力摆脱再一次的代码,大家会遇上很多题材。比如某段代码在四个地方有双重,当大家需求修改那部分逻辑时,你相当大概只修改到了贰至3处。

  • 单纯职责规范 这些类至少存有八个职务: 一 采纳总结算法 2遵照账户状态总计折扣 三 依照账户网龄总括折扣
    它违反了单一职分规范。那会推动哪些风险?假若大家将要修改第多个功效的话,会潜移默化到其余第三个职能。那就代表,我们每趟修改都会变动大家本不想修改的代码。由此,大家不得不对整个类实行测试,那实质上很浪费时间。

 ** ** **

I:命名,命名,命名

恕小编直言,那是代码中最要害的一步。大家只是修章/参数/变量那一个的名字,而现行我们能够直观的垂询到上面这几个类代表怎样意思。

 1 public class DiscountManager
 2 {
 3   public decimal ApplyDiscount(decimal price, int accountStatus, int timeOfHavingAccountInYears)
 4   {
 5     decimal priceAfterDiscount = 0;
 6     decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > 5) ? (decimal)5/100 : (decimal)timeOfHavingAccountInYears/100; 
 7     if (accountStatus == 1)
 8     {
 9       priceAfterDiscount = price;
10     }
11     else if (accountStatus == 2)
12     {
13       priceAfterDiscount = (price - (0.1m * price)) - (discountForLoyaltyInPercentage * (price - (0.1m * price)));
14     }
15     else if (accountStatus == 3)
16     {
17       priceAfterDiscount = (0.7m * price) - (discountForLoyaltyInPercentage * (0.7m * price));
18     }
19     else if (accountStatus == 4)
20     {
21       priceAfterDiscount = (price - (0.5m * price)) - (discountForLoyaltyInPercentage * (price - (0.5m * price)));
22     }
23  
24     return priceAfterDiscount;
25   }
26 }

虽说那样,我们依然不知底一,2,3,四意味着怎么着,那就此起彼伏往下啊!

总结

本文通过容易易懂的法子重构了1段难点代码,它显得了何等在骨子里情形中使用最棒实践和设计情势来帮忙大家写出干净的代码。
就自作者的劳作经历来说,本文中冒出的二流做法是隔叁差五发出的。编写那种代码的人一连认为他们力所能及维持那种规则,但不幸的是系统和作业往往都会特别复杂,每一遍修改那类代码时都会带来巨大的危害。

重构

由此以下九布,小编会告诉你们怎么样制止上述风险并贯彻一个根本的、可保障的、可测试的代码。

  1. 取名,命名,命名
    那是地道代码的最关键方面之一。大家只需求改变方法,参数和变量的命名。以后,我们能够适当的驾驭上面包车型客车类是背负什么的了。

    public decimal ApplyDiscount(decimal price, int accountStatus, int timeOfHavingAccountInYears)
    {
        decimal priceAfterDiscount = 0;
        decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > 5) ? (decimal)5 / 100 : (decimal)timeOfHavingAccountInYears / 100;
        if (accountStatus == 1)
        {
            priceAfterDiscount = price;
        }
        else if (accountStatus == 2)
        {
            priceAfterDiscount = (price - (0.1m * price)) - (discountForLoyaltyInPercentage * (price - (0.1m * price)));
        }
        else if (accountStatus == 3)
        {
            priceAfterDiscount = (0.7m * price) - (discountForLoyaltyInPercentage * (0.7m * price));
        }
        else if (accountStatus == 4)
        {
            priceAfterDiscount = (price - (0.5m * price)) - (discountForLoyaltyInPercentage * (price - (0.5m * price)));
        }
    
        return priceAfterDiscount;
    }
    

    不过大家任然不精晓账户状态一,贰,叁到底是何等看头。

  2. 魔数
    在C#中幸免魔数大家1般选择枚举来替换它们。那里运用AccountStatus 枚举来替换if-else中的魔数。
    public enum AccountStatus {   NotRegistered = 1,   SimpleCustomer = 2,   ValuableCustomer = 3,   MostValuableCustomer = 4 }
    今后我们来看看重构后的类,大家可以很简单的揭露哪2个账户状态应当用什么算法来计算折扣。混合账户状态的危机神速的骤降了。

    public class DiscountManager
    {
        public decimal ApplyDiscount(decimal price, AccountStatus accountStatus, int timeOfHavingAccountInYears)
        {
            decimal priceAfterDiscount = 0;
            decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > 5) ? (decimal)5 / 100 : (decimal)timeOfHavingAccountInYears / 100;
    
            if (accountStatus == AccountStatus.NotRegistered)
            {
                priceAfterDiscount = price;
            }
            else if (accountStatus == AccountStatus.SimpleCustomer)
            {
                priceAfterDiscount = (price - (0.1m * price)) - (discountForLoyaltyInPercentage * (price - (0.1m * price)));
            }
            else if (accountStatus == AccountStatus.ValuableCustomer)
            {
                priceAfterDiscount = (0.7m * price) - (discountForLoyaltyInPercentage * (0.7m * price));
            }
            else if (accountStatus == AccountStatus.MostValuableCustomer)
            {
                priceAfterDiscount = (price - (0.5m * price)) - (discountForLoyaltyInPercentage * (price - (0.5m * price)));
            }
            return priceAfterDiscount;
        }
    }
    
  3. 越多的代码可读性
    在这一步中,咱们利用switch-case 来替换 if-else
    if
    来拉长代码可读性。
    同时,小编还将有个别长度非常长的语句才分成两行。比如:**priceAfterDiscount = (price - (0.5m * price)) - (discountForLoyaltyInPercentage * (price - (0.5m * price)));**
    被涂改为:
    **priceAfterDiscount = (price - (0.5m * price));priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);**
    以下是完全的修改:

    public class DiscountManager
    {
        public decimal ApplyDiscount(decimal price, AccountStatus accountStatus, int timeOfHavingAccountInYears)
        {
            decimal priceAfterDiscount = 0;
            decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > 5) ? (decimal)5 / 100 : (decimal)timeOfHavingAccountInYears / 100;
            switch (accountStatus)
            {
                case AccountStatus.NotRegistered:
                    priceAfterDiscount = price;
                    break;
                case AccountStatus.SimpleCustomer:
                    priceAfterDiscount = (price - (0.1m * price));
                    priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
                    break;
                case AccountStatus.ValuableCustomer:
                    priceAfterDiscount = (0.7m * price);
                    priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
                    break;
                case AccountStatus.MostValuableCustomer:
                    priceAfterDiscount = (price - (0.5m * price));
                    priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
                    break;
            }
            return priceAfterDiscount;
        }
    }
    
  4. 铲除隐形的BUG
    正如大家后面提到的,大家的ApplyDiscount方法可能将为新的客户情形再次回到0。
    大家怎么样才能化解那些难题?答案正是抛出NotImplementedException。
    当大家的格局得到账户状态作为输入参数,但是参数值恐怕带有大家未规划到的不解意况。那时,我们不能如何也不做,抛出卓殊是此时最佳的做法。

        public decimal ApplyDiscount(decimal price, AccountStatus accountStatus, int timeOfHavingAccountInYears)
        {
            decimal priceAfterDiscount = 0;
            decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > 5) ? (decimal)5 / 100 : (decimal)timeOfHavingAccountInYears / 100;
            switch (accountStatus)
            {
                case AccountStatus.NotRegistered:
                    priceAfterDiscount = price;
                    break;
                case AccountStatus.SimpleCustomer:
                    priceAfterDiscount = (price - (0.1m * price));
                    priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
                    break;
                case AccountStatus.ValuableCustomer:
                    priceAfterDiscount = (0.7m * price);
                    priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
                    break;
                case AccountStatus.MostValuableCustomer:
                    priceAfterDiscount = (price - (0.5m * price));
                    priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
                    break;
                default:
                    throw new NotImplementedException();
            }
            return priceAfterDiscount;
        }
    
  5. 剖析算法 在那一个事例中,大家透过五个正经来估测计算客户折扣:

  • 账户状态
  • 账户网龄 通过网龄总括的算法都好像那样:
    (discountForLoyaltyInPercentage * priceAfterDiscount)
    然则对于账户状态为ValuableCustomer的算法却是: 0.7m * price
    大家把它修改成和别的账户状态同样的算法: price - (0.3m * price)

         public class DiscountManager
        {
        public decimal ApplyDiscount(decimal price, AccountStatus     accountStatus, int timeOfHavingAccountInYears)
        {
        decimal priceAfterDiscount = 0;
        decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > 5) ? (decimal)5 / 100 : (decimal)timeOfHavingAccountInYears / 100;
        switch (accountStatus)
        {
            case AccountStatus.NotRegistered:
                priceAfterDiscount = price;
                break;
            case AccountStatus.SimpleCustomer:
                priceAfterDiscount = (price - (0.1m * price));
                priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
                break;
            case AccountStatus.ValuableCustomer:
                priceAfterDiscount = (price - (0.3m * price));
                priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
                break;
            case AccountStatus.MostValuableCustomer:
                priceAfterDiscount = (price - (0.5m * price));
                priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
                break;
            default:
                throw new NotImplementedException();
        }
        return priceAfterDiscount;
        }
        }
    
  1. 免去魔数的另一种方法
    使用静态常量来替换魔数。0.1m,0.二m,0.三m,小编m,大家并不知道它们是如何意思。
    别的decimal discountForLoyaltyInPercentage =
    (timeOfHavingAccountInYears > 5) ? (decimal)5/100 :
    (decimal)timeOfHavingAccountInYears/100;
    中,数字5也万分神秘。
    大家无法不让它们更享有描述性,那时使用常量会是1个相比好的章程。
    我们来定义2个静态类:

    public static class Constants
    {
    public const int MAXIMUM_DISCOUNT_FOR_LOYALTY = 5;
    public const decimal DISCOUNT_FOR_SIMPLE_CUSTOMERS = 0.1m;
    public const decimal DISCOUNT_FOR_VALUABLE_CUSTOMERS = 0.3m;
    public const decimal DISCOUNT_FOR_MOST_VALUABLE_CUSTOMERS = 0.5m;
    }
    

    随即修改DiscountManager类:

    public class DiscountManager
    {
    public decimal ApplyDiscount(decimal price, AccountStatus accountStatus, int timeOfHavingAccountInYears)
    {
        decimal priceAfterDiscount = 0;
        decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > Constants.MAXIMUM_DISCOUNT_FOR_LOYALTY) ? (decimal)Constants.MAXIMUM_DISCOUNT_FOR_LOYALTY / 100 : (decimal)timeOfHavingAccountInYears / 100;
        switch (accountStatus)
        {
            case AccountStatus.NotRegistered:
                priceAfterDiscount = price;
                break;
            case AccountStatus.SimpleCustomer:
                priceAfterDiscount = (price - (Constants.DISCOUNT_FOR_SIMPLE_CUSTOMERS * price));
                priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
                break;
            case AccountStatus.ValuableCustomer:
                priceAfterDiscount = (price - (Constants.DISCOUNT_FOR_VALUABLE_CUSTOMERS * price));
                priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
                break;
            case AccountStatus.MostValuableCustomer:
                priceAfterDiscount = (price - (Constants.DISCOUNT_FOR_MOST_VALUABLE_CUSTOMERS * price));
                priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
                break;
            default:
                throw new NotImplementedException();
        }
        return priceAfterDiscount;
    }
    }
    
  2. 破除重复的代码
    为了扫除重复的代码,那里将1部分算法提取出来。首先,大家创制多少个扩充方法:

    public static class PriceExtensions
    {
    public static decimal ApplyDiscountForAccountStatus(this decimal price, decimal discountSize)
    {
        return price - (discountSize * price);
    }
    
    public static decimal ApplyDiscountForTimeOfHavingAccount(this decimal price, int timeOfHavingAccountInYears)
    {
        decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > Constants.MAXIMUM_DISCOUNT_FOR_LOYALTY) ? (decimal)Constants.MAXIMUM_DISCOUNT_FOR_LOYALTY / 100 : (decimal)timeOfHavingAccountInYears / 100;
        return price - (discountForLoyaltyInPercentage * price);
    }
    }
    

    透过措施名称,大家就能够精通它的职责是什么,今后涂改大家的事例:

    public class DiscountManager
    {
    public decimal ApplyDiscount(decimal price, AccountStatus accountStatus, int timeOfHavingAccountInYears)
    {
        decimal priceAfterDiscount = 0;
        switch (accountStatus)
        {
            case AccountStatus.NotRegistered:
                priceAfterDiscount = price;
                break;
            case AccountStatus.SimpleCustomer:
                priceAfterDiscount = price.ApplyDiscountForAccountStatus(Constants.DISCOUNT_FOR_SIMPLE_CUSTOMERS)
                  .ApplyDiscountForTimeOfHavingAccount(timeOfHavingAccountInYears);
                break;
            case AccountStatus.ValuableCustomer:
                priceAfterDiscount = price.ApplyDiscountForAccountStatus(Constants.DISCOUNT_FOR_VALUABLE_CUSTOMERS)
                  .ApplyDiscountForTimeOfHavingAccount(timeOfHavingAccountInYears);
                break;
            case AccountStatus.MostValuableCustomer:
                priceAfterDiscount = price.ApplyDiscountForAccountStatus(Constants.DISCOUNT_FOR_MOST_VALUABLE_CUSTOMERS)
                  .ApplyDiscountForTimeOfHavingAccount(timeOfHavingAccountInYears);
                break;
            default:
                throw new NotImplementedException();
        }
        return priceAfterDiscount;
    }
    }
    
  3. 除去没用的代码
    我们应有写出简约的代码,因为简短的代码=收缩BUG产生的机率,并且也让大家裁减掌握事情逻辑的光阴。
    大家发现,这里三种景况的客户调用了千篇1律的秘籍:
    .ApplyDiscountForTimeOfHavingAccount(timeOfHavingAccountInYears);
    那里能够统一代码:

    public class DiscountManager
    {
    public decimal ApplyDiscount(decimal price, AccountStatus accountStatus, int timeOfHavingAccountInYears)
    {
        decimal priceAfterDiscount = 0;
        switch (accountStatus)
        {
            case AccountStatus.NotRegistered:
                priceAfterDiscount = price;
                break;
            case AccountStatus.SimpleCustomer:
                priceAfterDiscount = price.ApplyDiscountForAccountStatus(Constants.DISCOUNT_FOR_SIMPLE_CUSTOMERS);
                break;
            case AccountStatus.ValuableCustomer:
                priceAfterDiscount = price.ApplyDiscountForAccountStatus(Constants.DISCOUNT_FOR_VALUABLE_CUSTOMERS);
                break;
            case AccountStatus.MostValuableCustomer:
                priceAfterDiscount = price.ApplyDiscountForAccountStatus(Constants.DISCOUNT_FOR_MOST_VALUABLE_CUSTOMERS);
                break;
            default:
                throw new NotImplementedException();
        }
        priceAfterDiscount = priceAfterDiscount.ApplyDiscountForTimeOfHavingAccount(timeOfHavingAccountInYears);
        return priceAfterDiscount;
    }
    }
    

    玖.谈到底,得到彻底的代码
    最后,让我们由此引进正视注入和工厂方法形式来获取最后的版本吧。
    先来看卡最终结出:

    public class DiscountManager
    {
    private readonly IAccountDiscountCalculatorFactory _factory;
    private readonly ILoyaltyDiscountCalculator _loyaltyDiscountCalculator;
    
    public DiscountManager(IAccountDiscountCalculatorFactory factory, ILoyaltyDiscountCalculator loyaltyDiscountCalculator)
    {
        _factory = factory;
        _loyaltyDiscountCalculator = loyaltyDiscountCalculator;
    }
    
    public decimal ApplyDiscount(decimal price, AccountStatus accountStatus, int timeOfHavingAccountInYears)
    {
        decimal priceAfterDiscount = 0;
        priceAfterDiscount = _factory.GetAccountDiscountCalculator(accountStatus).ApplyDiscount(price);
        priceAfterDiscount = _loyaltyDiscountCalculator.ApplyDiscount(priceAfterDiscount, timeOfHavingAccountInYears);
        return priceAfterDiscount;
    }
    }
    
    public interface ILoyaltyDiscountCalculator
    {
    decimal ApplyDiscount(decimal price, int timeOfHavingAccountInYears);
    }
    
    public class DefaultLoyaltyDiscountCalculator : ILoyaltyDiscountCalculator
    {
    public decimal ApplyDiscount(decimal price, int timeOfHavingAccountInYears)
    {
        decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > Constants.MAXIMUM_DISCOUNT_FOR_LOYALTY) ? (decimal)Constants.MAXIMUM_DISCOUNT_FOR_LOYALTY / 100 : (decimal)timeOfHavingAccountInYears / 100;
        return price - (discountForLoyaltyInPercentage * price);
    }
    }
    
    public interface IAccountDiscountCalculatorFactory
    {
    IAccountDiscountCalculator GetAccountDiscountCalculator(AccountStatus accountStatus);
    }
    
    public class DefaultAccountDiscountCalculatorFactory : IAccountDiscountCalculatorFactory
    {
    public IAccountDiscountCalculator GetAccountDiscountCalculator(AccountStatus accountStatus)
    {
        IAccountDiscountCalculator calculator;
        switch (accountStatus)
        {
            case AccountStatus.NotRegistered:
                calculator = new NotRegisteredDiscountCalculator();
                break;
            case AccountStatus.SimpleCustomer:
                calculator = new SimpleCustomerDiscountCalculator();
                break;
            case AccountStatus.ValuableCustomer:
                calculator = new ValuableCustomerDiscountCalculator();
                break;
            case AccountStatus.MostValuableCustomer:
                calculator = new MostValuableCustomerDiscountCalculator();
                break;
            default:
                throw new NotImplementedException();
        }
    
        return calculator;
        }
    }
    
    public interface IAccountDiscountCalculator
    {
    decimal ApplyDiscount(decimal price);
    }
    
    public class NotRegisteredDiscountCalculator : IAccountDiscountCalculator
    {
    public decimal ApplyDiscount(decimal price)
    {
        return price;
    }
    }
    
    public class SimpleCustomerDiscountCalculator : IAccountDiscountCalculator
    {
    public decimal ApplyDiscount(decimal price)
    {
        return price - (Constants.DISCOUNT_FOR_SIMPLE_CUSTOMERS * price);
    }
    }
    
    public class ValuableCustomerDiscountCalculator : IAccountDiscountCalculator
    {
    public decimal ApplyDiscount(decimal price)
    {
        return price - (Constants.DISCOUNT_FOR_VALUABLE_CUSTOMERS * price);
    }
    }
    
    public class MostValuableCustomerDiscountCalculator : IAccountDiscountCalculator
    {
    public decimal ApplyDiscount(decimal price)
    {
        return price - (Constants.DISCOUNT_FOR_MOST_VALUABLE_CUSTOMERS * price);
    }
    }
    

    先是,大家摆脱了扩张方法(静态类),假如我们想对ApplyDiscount格局开始展览单元测试是相比较困难的,裁撤我们对PriceExtensions增添类也进行测试。
    为了防止那几个难点,大家成立了DefaultLoyaltyDiscountCalculator 类来替换ApplyDiscountForTimeOfHavingAccount这一个扩充方法,此类还落到实处了ILoyaltyDiscountCalculator接口。现在,当大家要测试DiscountManager类时,大家只构造函数注入ILoyaltyDiscountCalculator接口的贯彻即可。那里运用了依赖注入。
    通过如此做,我们将网龄折扣的算法迁移到近似DefaultLoyaltyDiscountCalculator 的分歧类中,那样当大家修改某多个算法不会覆盖到别的作业。
    对于依据账户状态来测算折扣的业务,大家须求在DiscountManager中删去多个任务:

  • 基于账户状态选用总结的算法
  • 完毕计算算法
    这里大家因此DefaultAccountDiscountCalculatorFactory工厂类来消除那些标题,DefaultAccountDiscountCalculatorFactory工厂类完结了IAccountDiscountCalculatorFactory接口。
    大家的厂子将控制取舍哪3个倒扣算法。接着,工厂类被通过构造函数注入到DiscountManager类中。
    下边小编只须要在DiscountManager 中利用工厂:
    priceAfterDiscount = _factory.GetAccountDiscountCalculator(accountStatus).ApplyDiscount(price);
    以上,大家缓解了第3个难题,上面我们供给贯彻总结算法。根据账户状态,提供分化的算法,这刚刚合乎政策方式。我们需求创设七个政策:NotRegisteredDiscountCalculator,SimpleCustomerDiscountCalculator,MostValuableCustomerDiscountCalculator现已一无全体出来的接口IAccountDiscountCalculator
    好了,以往大家有可一段干净可读的代码了,那段代码中享有的类都只有3个义务:
  • DiscountManager – 管理
  • DefaultLoyaltyDiscountCalculator – 网龄计算折扣
  • DefaultAccountDiscountCalculatorFactory – 依照账户状态选取折扣策略
  • NotRegisteredDiscountCalculator,SimpleCustomerDiscountCalculator,MostValuableCustomerDiscountCalculator-总计折扣算法
    大家来比较一下修改前后的代码:

    public class Class1
    {
    public decimal Calculate(decimal amount, int type, int years)
    {
        decimal result = 0;
        decimal disc = (years > 5) ? (decimal)5 / 100 : (decimal)years / 100;
        if (type == 1)
        {
            result = amount;
        }
        else if (type == 2)
        {
            result = (amount - (0.m * amount)) - disc * (amount - (0.m * amount));
        }
        else if (type == 3)
        {
            result = (0.m * amount) - disc * (0.m * amount);
        }
        else if (type == 4)
        {
            result = (amount - (0.m * amount)) - disc * (amount - (0.m * amount));
        }
        return result;
    }
    }
    

    修改后:

    public class DiscountManager
    {
    private readonly IAccountDiscountCalculatorFactory _factory;
    private readonly ILoyaltyDiscountCalculator _loyaltyDiscountCalculator;
    
    public DiscountManager(IAccountDiscountCalculatorFactory factory, ILoyaltyDiscountCalculator loyaltyDiscountCalculator)
    {
        _factory = factory;
        _loyaltyDiscountCalculator = loyaltyDiscountCalculator;
    }
    
    public decimal ApplyDiscount(decimal price, AccountStatus accountStatus, int timeOfHavingAccountInYears)
    {
        decimal priceAfterDiscount = 0;
        priceAfterDiscount = _factory.GetAccountDiscountCalculator(accountStatus).ApplyDiscount(price);
        priceAfterDiscount = _loyaltyDiscountCalculator.ApplyDiscount(priceAfterDiscount, timeOfHavingAccountInYears);
        return priceAfterDiscount;
    }
    }
    

  1. 阐明示例须求

II:魔法数

在C#中制止出现不掌握的魔法数的章程是透过枚举来代表。作者通过枚举方法来替代在if-else if 语句中冒出的代表账户状态的魔法数。

1 public enum AccountStatus
2 {
3   NotRegistered = 1,
4   SimpleCustomer = 2,
5   ValuableCustomer = 3,
6   MostValuableCustomer = 4
7 }

近来在看我们重构了的类,大家得以很简单的揭露那么些总括法则是用来依照不用状态来计量折扣率的。将账户状态弄混的可能率就大幅度削减了。

 1 public class DiscountManager
 2 {
 3   public decimal ApplyDiscount(decimal price, AccountStatus accountStatus, int timeOfHavingAccountInYears)
 4   {
 5     decimal priceAfterDiscount = 0;
 6     decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > 5) ? (decimal)5/100 : (decimal)timeOfHavingAccountInYears/100;
 7  
 8     if (accountStatus == AccountStatus.NotRegistered)
 9     {
10       priceAfterDiscount = price;
11     }
12     else if (accountStatus == AccountStatus.SimpleCustomer)
13     {
14       priceAfterDiscount = (price - (0.1m * price)) - (discountForLoyaltyInPercentage * (price - (0.1m * price)));
15     }
16     else if (accountStatus == AccountStatus.ValuableCustomer)
17     {
18       priceAfterDiscount = (0.7m * price) - (discountForLoyaltyInPercentage * (0.7m * price));
19     }
20     else if (accountStatus == AccountStatus.MostValuableCustomer)
21     {
22       priceAfterDiscount = (price - (0.5m * price)) - (discountForLoyaltyInPercentage * (price - (0.5m * price)));
23     }
24     return priceAfterDiscount;
25   }
26 }

总结

本文通过简单易懂的办法重构了1段难点代码,它显得了何等在骨子里情形中利用最棒实践和设计情势来提携大家写出干净的代码。
就本身的工作经历来说,本文中冒出的不成做法是时常发生的。编写那种代码的人一而再认为他俩能够保持那种规则,但不幸的是系统和业务往往都会越加复杂,每一趟修改那类代码时都会带来巨大的危机。

  本篇还是用事先的电子商务网址中的贰个不难易行的景色来叙述:在页面上必要体现产品的列表消息。并且依据产品的品类差异,计算出相应的折扣。 

III:愈来愈多的可读性

在这一步中大家将因此将if-else
if
 语句改为switch-case 语句,来充实小说的可读性。

再者,笔者也将二个十分长的乘除方法拆分为两句话来写。以后大家将“
通过账户状态来测算折扣率”与“通过账户定期来测算折扣率”那2者分别来总括。

例如:priceAfterDiscount = (price – (0.5m * price)) –
(discountForLoyaltyInPercentage * (price – (0.5m * price)));

我们将它重构为:priceAfterDiscount = (price – (0.5m * price));
priceAfterDiscount = priceAfterDiscount –
(discountForLoyaltyInPercentage * priceAfterDiscount);

那正是修改后的代码:

 1 public class DiscountManager
 2 {
 3   public decimal ApplyDiscount(decimal price, AccountStatus accountStatus, int timeOfHavingAccountInYears)
 4   {
 5     decimal priceAfterDiscount = 0;
 6     decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > 5) ? (decimal)5/100 : (decimal)timeOfHavingAccountInYears/100;
 7     switch (accountStatus)
 8     {
 9       case AccountStatus.NotRegistered:
10         priceAfterDiscount = price;
11         break;
12       case AccountStatus.SimpleCustomer:
13         priceAfterDiscount = (price - (0.1m * price));
14         priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
15         break;
16       case AccountStatus.ValuableCustomer:
17         priceAfterDiscount = (0.7m * price);
18         priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
19         break;
20       case AccountStatus.MostValuableCustomer:
21         priceAfterDiscount = (price - (0.5m * price));
22         priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
23         break;
24     }
25     return priceAfterDiscount;
26   }
27 }

  在上篇中,我们曾经筹划项指标逻辑分层。大家再来回看下:

IV:未有明了的bug

作者们终于找到我们隐藏的bug了!

因为本人刚刚提到的我们的艺术中对此不相符的账户状态会在造成对于拥有商品最后都再次回到0。就算很不幸,但却是真的。

那大家该怎么修复那几个题目吗?这就唯有因此未有不当提示了。

美高梅开户网址 6

你是否会想,这些会不会是支付的两样,应该不会被交付到错误提醒中去?不,他会的!

当大家的办法通过取得账户状态作为参数的时候,大家并不想程序让大家不得预感的来头发展,造成不可预测的失误。

 那种景况是相对不允许出现的,所以我们务必经过抛出尤其来预防那种意况。

上面包车型地铁代码正是经过抛出特别后修改的以幸免出现不满意条件的情况-修章是将抛出10分制止 switch-case语句中的default 句中。

 1 public class DiscountManager
 2 {
 3   public decimal ApplyDiscount(decimal price, AccountStatus accountStatus, int timeOfHavingAccountInYears)
 4   {
 5     decimal priceAfterDiscount = 0;
 6     decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > 5) ? (decimal)5/100 : (decimal)timeOfHavingAccountInYears/100;
 7     switch (accountStatus)
 8     {
 9       case AccountStatus.NotRegistered:
10         priceAfterDiscount = price;
11         break;
12       case AccountStatus.SimpleCustomer:
13         priceAfterDiscount = (price - (0.1m * price));
14         priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
15         break;
16       case AccountStatus.ValuableCustomer:
17         priceAfterDiscount = (0.7m * price);
18         priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
19         break;
20       case AccountStatus.MostValuableCustomer:
21         priceAfterDiscount = (price - (0.5m * price));
22         priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
23         break;
24       default:
25         throw new NotImplementedException();
26     }
27     return priceAfterDiscount;
28   }
29 }

 

V:分析盘算办法

在我们的事例中大家有多个概念给客户的折扣率的正规:

  1. 账户状态;
  2. 账户在我们系统中设有的定期

对于年限的推断折扣率的章程,全体的计量方式都有点类似:

(discountForLoyaltyInPercentage * priceAfterDiscount)

理所当然,也依然存在分歧的:0.7m * price

因此大家把那个改成那样:price – (0.3m * price)

 1 public class DiscountManager
 2 {
 3   public decimal ApplyDiscount(decimal price, AccountStatus accountStatus, int timeOfHavingAccountInYears)
 4   {
 5     decimal priceAfterDiscount = 0;
 6     decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > 5) ? (decimal)5/100 : (decimal)timeOfHavingAccountInYears/100;
 7     switch (accountStatus)
 8     {
 9       case AccountStatus.NotRegistered:
10         priceAfterDiscount = price;
11         break;
12       case AccountStatus.SimpleCustomer:
13         priceAfterDiscount = (price - (0.1m * price));
14         priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
15         break;
16       case AccountStatus.ValuableCustomer:
17         priceAfterDiscount = (price - (0.3m * price));
18         priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
19         break;
20       case AccountStatus.MostValuableCustomer:
21         priceAfterDiscount = (price - (0.5m * price));
22         priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
23         break;
24       default:
25         throw new NotImplementedException();
26     }
27     return priceAfterDiscount;
28   }
29 }

现今大家将整治全部通过账户状态的乘除形式改为同样种格式:price –
((static_discount_in_美高梅开户网址 ,percentages/100) * price)

美高梅开户网址 7

VI:通过别的艺术再摆脱魔法数

接下去让大家的秋波放在通过账户状态总括折扣率的乘除格局中的静态变量:(static_discount_in_percentages/100)

接下来带入下边数字距离试试:0.一m,0.三m,0.五m

这个数字其实也是1种档次的魔法数-他们也未曾平昔告知大家他们代表着哪些。

咱俩也有平等的景况,比如将“有账户的时刻”折价为“忠诚折扣”。

decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears
> 5) ? (decimal)5/100 : (decimal)timeOfHavingAccountInYears/100;

数字伍让咱们的代码变得神秘了起来。

我们无法不做些什么让那一个变得更具表现性。

自笔者会用别的一种办法来制止魔法数的表述的出现-相当于C#中的常量(关键词是const),笔者强烈提出在我们的应用程序中等专业高校门定义2个静态类来储存那几个常量。

在大家的例证中,作者是创办了上边包车型大巴类:

1 public static class Constants
2 {
3   public const int MAXIMUM_DISCOUNT_FOR_LOYALTY = 5;
4   public const decimal DISCOUNT_FOR_SIMPLE_CUSTOMERS = 0.1m;
5   public const decimal DISCOUNT_FOR_VALUABLE_CUSTOMERS = 0.3m;
6   public const decimal DISCOUNT_FOR_MOST_VALUABLE_CUSTOMERS = 0.5m;
7 }

经过一定的改动,大家的DiscountManager类就改为了那般了:

 1 public class DiscountManager
 2 {
 3   public decimal ApplyDiscount(decimal price, AccountStatus accountStatus, int timeOfHavingAccountInYears)
 4   {
 5     decimal priceAfterDiscount = 0;
 6     decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > Constants.MAXIMUM_DISCOUNT_FOR_LOYALTY) ? (decimal)Constants.MAXIMUM_DISCOUNT_FOR_LOYALTY/100 : (decimal)timeOfHavingAccountInYears/100;
 7     switch (accountStatus)
 8     {
 9       case AccountStatus.NotRegistered:
10         priceAfterDiscount = price;
11         break;
12       case AccountStatus.SimpleCustomer:
13         priceAfterDiscount = (price - (Constants.DISCOUNT_FOR_SIMPLE_CUSTOMERS * price));
14         priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
15         break;
16       case AccountStatus.ValuableCustomer:
17         priceAfterDiscount = (price - (Constants.DISCOUNT_FOR_VALUABLE_CUSTOMERS * price));
18         priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
19         break;
20       case AccountStatus.MostValuableCustomer:
21         priceAfterDiscount = (price - (Constants.DISCOUNT_FOR_MOST_VALUABLE_CUSTOMERS * price));
22         priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
23         break;
24       default:
25         throw new NotImplementedException();
26     }
27     return priceAfterDiscount;
28   }
29 }

自身梦想你也认可自个儿这么些点子会愈来愈使代码本身变得更享有表达性:)

想必部分朋友认为从斯马特 UI立刻跳到这种分层设计,就像快了些。其实也终于1个思维的弹跳吧。上边就来看看那种分层是什么缓解在此以前SmartUI的难题的。 

VII:不要再另行啦!

美高梅开户网址 8

 

作者们能够透过分拆算法的格局来运动大家的计量方法,而不是独自简单的复制代码。

作者们会透过扩大方法。

首先我们会创制七个扩展方法。

 1 public static class PriceExtensions
 2 {
 3   public static decimal ApplyDiscountForAccountStatus(this decimal price, decimal discountSize)
 4   {
 5     return price - (discountSize * price);
 6   }
 7  
 8   public static decimal ApplyDiscountForTimeOfHavingAccount(this decimal price, int timeOfHavingAccountInYears)
 9   {
10      decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > Constants.MAXIMUM_DISCOUNT_FOR_LOYALTY) ? (decimal)Constants.MAXIMUM_DISCOUNT_FOR_LOYALTY/100 : (decimal)timeOfHavingAccountInYears/100;
11     return price - (discountForLoyaltyInPercentage * price);
12   }
13 }

正如方法的名字一般,笔者不再须求单独解释1回他们的法力是什么。未来就从头在大家的事例中接纳这一个代码吧:

 

 1 public class DiscountManager
 2 {
 3   public decimal ApplyDiscount(decimal price, AccountStatus accountStatus, int timeOfHavingAccountInYears)
 4   {
 5     decimal priceAfterDiscount = 0;
 6     switch (accountStatus)
 7     {
 8       case AccountStatus.NotRegistered:
 9         priceAfterDiscount = price;
10         break;
11       case AccountStatus.SimpleCustomer:
12         priceAfterDiscount = price.ApplyDiscountForAccountStatus(Constants.DISCOUNT_FOR_SIMPLE_CUSTOMERS)
13           .ApplyDiscountForTimeOfHavingAccount(timeOfHavingAccountInYears);
14         break;
15       case AccountStatus.ValuableCustomer:
16         priceAfterDiscount = price.ApplyDiscountForAccountStatus(Constants.DISCOUNT_FOR_VALUABLE_CUSTOMERS)
17           .ApplyDiscountForTimeOfHavingAccount(timeOfHavingAccountInYears);
18         break;
19       case AccountStatus.MostValuableCustomer:
20         priceAfterDiscount = price.ApplyDiscountForAccountStatus(Constants.DISCOUNT_FOR_MOST_VALUABLE_CUSTOMERS)
21           .ApplyDiscountForTimeOfHavingAccount(timeOfHavingAccountInYears);
22         break;
23       default:
24         throw new NotImplementedException();
25     }
26     return priceAfterDiscount;
27   }
28 }

推而广之方法让代码看起来更为友善了,不过那些代码照旧静态的类,所以会让你单元测试的时候碰到困难,甚至不可能。那么出于摆脱那么些题材的打算大家在结尾一步来化解这几个标题。作者将显得那几个是什么样简化大家的行事生活的。不过对于自身个人而言,笔者爱好,可是并不算是热衷粉。

不顾,你以往允许大家的代码看起来友善多了这点么?

那大家就继续下去吧!

 

VIII:移除那么些多余的代码

在写代码的时候条件上是大家的代码越是精简越好。精简的代码的表示,越少的错误的也许性,在阅读通晓代码逻辑的时候开支的小运越少。

为此现在开始精简我们的代码吧。

大家能够随便发现大家三种客户账户下具有1样的点子:

.ApplyDiscountForTimeOfHavingAccount(timeOfHavingAccountInYears);

作者们可以还是不可以只写3回啊?大家后面将未注册的用户放在了抛出十二分中,因为大家的折扣率只会盘算注册用户的时间限制,并不曾给未注册用户留有成效设定。所以,大家相应给未注册用户设定的时刻为多少啊?
-0年

那正是说相应的折扣率也将变成0了,那样大家就能够安全的将折扣率交付给未注册用户选用了,那就从头吧!

 1 public class DiscountManager
 2 {
 3   public decimal ApplyDiscount(decimal price, AccountStatus accountStatus, int timeOfHavingAccountInYears)
 4   {
 5     decimal priceAfterDiscount = 0;
 6     switch (accountStatus)
 7     {
 8       case AccountStatus.NotRegistered:
 9         priceAfterDiscount = price;
10         break;
11       case AccountStatus.SimpleCustomer:
12         priceAfterDiscount = price.ApplyDiscountForAccountStatus(Constants.DISCOUNT_FOR_SIMPLE_CUSTOMERS);
13         break;
14       case AccountStatus.ValuableCustomer:
15         priceAfterDiscount = price.ApplyDiscountForAccountStatus(Constants.DISCOUNT_FOR_VALUABLE_CUSTOMERS);
16         break;
17       case AccountStatus.MostValuableCustomer:
18         priceAfterDiscount = price.ApplyDiscountForAccountStatus(Constants.DISCOUNT_FOR_MOST_VALUABLE_CUSTOMERS);
19         break;
20       default:
21         throw new NotImplementedException();
22     }
23     priceAfterDiscount = priceAfterDiscount.ApplyDiscountForTimeOfHavingAccount(timeOfHavingAccountInYears);
24     return priceAfterDiscount;
25   }
26 }

大家还能将那一行移除到switch-case语句外面。好处正是:越来越少的代码量!

  二.  业务层设计

IX:进步-最后的拿到彻底整洁的代码

好了,今后我们能够像阅读壹本书1样方便来审视我们的代码了,不过那就够了么?大家能够将代码变得最好简单的!

美高梅开户网址 9

好的,那就起头做1些改变来促成那一个指标呢。我们得以行使依赖注入和动用政策方式那两种艺术。

那便是我们前天最后整理出来的代码了:

 1 public class DiscountManager
 2 {
 3   private readonly IAccountDiscountCalculatorFactory _factory;
 4   private readonly ILoyaltyDiscountCalculator _loyaltyDiscountCalculator;
 5  
 6   public DiscountManager(IAccountDiscountCalculatorFactory factory, ILoyaltyDiscountCalculator loyaltyDiscountCalculator)
 7   {
 8     _factory = factory;
 9     _loyaltyDiscountCalculator = loyaltyDiscountCalculator;
10   }
11  
12   public decimal ApplyDiscount(decimal price, AccountStatus accountStatus, int timeOfHavingAccountInYears)
13   {
14     decimal priceAfterDiscount = 0;
15     priceAfterDiscount = _factory.GetAccountDiscountCalculator(accountStatus).ApplyDiscount(price);
16     priceAfterDiscount = _loyaltyDiscountCalculator.ApplyDiscount(priceAfterDiscount, timeOfHavingAccountInYears);
17     return priceAfterDiscount;
18   }
19 }

 1 public interface ILoyaltyDiscountCalculator
 2 {
 3   decimal ApplyDiscount(decimal price, int timeOfHavingAccountInYears);
 4 }
 5  
 6 public class DefaultLoyaltyDiscountCalculator : ILoyaltyDiscountCalculator
 7 {
 8   public decimal ApplyDiscount(decimal price, int timeOfHavingAccountInYears)
 9   {
10     decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > Constants.MAXIMUM_DISCOUNT_FOR_LOYALTY) ? (decimal)Constants.MAXIMUM_DISCOUNT_FOR_LOYALTY/100 : (decimal)timeOfHavingAccountInYears/100;
11     return price - (discountForLoyaltyInPercentage * price);
12   }
13 }

 1 public interface IAccountDiscountCalculatorFactory
 2 {
 3   IAccountDiscountCalculator GetAccountDiscountCalculator(AccountStatus accountStatus);
 4 }
 5  
 6 public class DefaultAccountDiscountCalculatorFactory : IAccountDiscountCalculatorFactory
 7 {
 8   public IAccountDiscountCalculator GetAccountDiscountCalculator(AccountStatus accountStatus)
 9   {
10     IAccountDiscountCalculator calculator;
11     switch (accountStatus)
12     {
13       case AccountStatus.NotRegistered:
14         calculator = new NotRegisteredDiscountCalculator();
15         break;
16       case AccountStatus.SimpleCustomer:
17         calculator = new SimpleCustomerDiscountCalculator();
18         break;
19       case AccountStatus.ValuableCustomer:
20         calculator = new ValuableCustomerDiscountCalculator();
21         break;
22       case AccountStatus.MostValuableCustomer:
23         calculator = new MostValuableCustomerDiscountCalculator();
24         break;
25       default:
26         throw new NotImplementedException();
27     }
28  
29     return calculator;
30   }
31 }

 1 public interface IAccountDiscountCalculator
 2 {
 3   decimal ApplyDiscount(decimal price);
 4 }
 5  
 6 public class NotRegisteredDiscountCalculator : IAccountDiscountCalculator
 7 {
 8   public decimal ApplyDiscount(decimal price)
 9   {
10     return price;
11   }
12 }
13  
14 public class SimpleCustomerDiscountCalculator : IAccountDiscountCalculator
15 {
16   public decimal ApplyDiscount(decimal price)
17   {
18     return price - (Constants.DISCOUNT_FOR_SIMPLE_CUSTOMERS * price);
19   }
20 }
21  
22 public class ValuableCustomerDiscountCalculator : IAccountDiscountCalculator
23 {
24   public decimal ApplyDiscount(decimal price)
25   {
26     return price - (Constants.DISCOUNT_FOR_VALUABLE_CUSTOMERS * price);
27   }
28 }
29  
30 public class MostValuableCustomerDiscountCalculator : IAccountDiscountCalculator
31 {
32   public decimal ApplyDiscount(decimal price)
33   {
34     return price - (Constants.DISCOUNT_FOR_MOST_VALUABLE_CUSTOMERS * price);
35   }
36 }

率先大家摆脱了扩展方法(也正是静态类),之所以要摆脱这种是因为扩大方法与折扣总结方式之间存在了紧耦合的涉及。借使大家想要单元测试大家的秘籍ApplyDiscount的时候将变得不太不难,因为我们必须统壹测试与之牢牢关系的类PriceExtensions。

为了制止那个,作者创立了DefaultLoyaltyDiscountCalculator 类,那里面含有了ApplyDiscountForTimeOfHavingAccount增加方法,同事自个儿通过架空切口ILoyaltyDiscountCalculator隐身了他的求实实现。未来,当作者想测试大家的类DiscountManager的时候,小编就能够透过 ILoyaltyDiscountCalculator仿照注入虚构对象到DiscountManager类中通过构造函数字彰显示测试功用。那里我们选拔的就叫正视注入方式。

美高梅开户网址 10

在做那些的还要,咱们也将总括折扣率那个效应安全的移交到另二个例外的类中,要是大家想要修改那一段的逻辑,那大家就只需求修改DefaultLoyaltyDiscountCalculator** **类就好了,而不需求转移别的的地点,那样收缩了在改变他的时候产生破坏其余地点的高危机,同时也不须要再充实单独测试的时辰了。

下边是大家在DiscountManager类中利用分其余逻辑类:

priceAfterDiscount =
_loyaltyDiscountCalculator.ApplyDiscount(priceAfterDiscount,
timeOfHavingAccountInYears);

为了针对账户状态的逻辑来总括折扣率,我创制了某个相比复杂的事物。大家在DiscountManager类中有三个职责必要表达出去。

  1. 据他们说账户状态如何挑选相应的盘算办法。
  2. 格外总括方法的细节

为了将第2个义务移交出去,小编创制了工厂类(DefaultAccountDiscountCalculatorFactory),为了达成工厂情势,然后再把这一个隐形到虚幻IAccountDiscountCalculatorFactory里面去。

美高梅开户网址 11

我们的厂子会操纵接纳哪个种类计算方法。最终大家由此信赖注册形式构造函数将工厂形式注射到DiscountManager类中

下边正是使用了工厂的DiscountManager类:

priceAfterDiscount =
_factory.GetAccountDiscountCalculator(accountStatus).ApplyDiscount(price);

 以上会针对不一样的账户状态重临几时的策略,然后调用ApplyDiscount 方法。

首先个权利已经被接通出去了,接下去便是第1个了。

 接下来大家就从头钻探策略了…..

美高梅开户网址 12

因为分裂的账户状态会有永不的折扣计算方法,所以大家要求差别的兑现政策。座椅相当适用于政策形式。

在大家的例子中,我们有两种政策:

NotRegisteredDiscountCalculator SimpleCustomerDiscountCalculator MostValuableCustomerDiscountCalculator**

她俩带有了现实的折扣总括格局的实现并被藏在了指雁为羹IAccountDiscountCalculator里。

那就允许大家的类DiscountManager应用合适的策略,而不须要精通具体的贯彻。大家的类只必要知道与ApplyDiscount方法相关的IAccountDiscountCalculator 接口重临的靶子的档次。

NotRegisteredDiscountCalculator, SimpleCustomerDiscountCalculator,
MostValuableCustomerDiscountCalculator
那么些类富含了现实的经过账户状态选择切合计算的总结格局的实现。因为大家的那多个政策看起来相似,咱们唯一能做的大半就唯有针对这两种总结策略创建2个措施然后每一种策略类通过2个不要的参数来调用她。因为那会让我们的代码变得更其多,所以小编明天决定不那样做了。

好了,到近年来截至大家的代码变得可读了,而且每一个类都只有三个义务了-这样修改他的时候会单独1壹对应了:

  1. DiscountManager-管理代码流
  2. DefaultLoyaltyDiscountCalculator-可信赖的乘除折扣率的不二等秘书诀
  3. DefaultAccountDiscountCalculatorFactory-决定遵照账户状态采取哪位策略来测算
  4. **NotRegisteredDiscountCalculator, SimpleCustomerDiscountCalculatorMostValuableCustomerDiscountCalculator **
    依据账户状态总计折扣率

当今初步相比今后与事先的秘籍:

 1 public class Class1
 2 {
 3     public decimal Calculate(decimal amount, int type, int years)
 4     {
 5         decimal result = 0;
 6         decimal disc = (years > 5) ? (decimal)5 / 100 : (decimal)years / 100;
 7         if (type == 1)
 8         {
 9             result = amount;
10         }
11         else if (type == 2)
12         {
13             result = (amount - (0.1m * amount)) - disc * (amount - (0.1m * amount));
14         }
15         else if (type == 3)
16         {
17             result = (0.7m * amount) - disc * (0.7m * amount);
18         }
19         else if (type == 4)
20         {
21             result = (amount - (0.5m * amount)) - disc * (amount - (0.5m * amount));
22         }
23         return result;
24     }
25 }

那是大家的新的重构的代码:

1 public decimal ApplyDiscount(decimal price, AccountStatus accountStatus, int timeOfHavingAccountInYears)
2 {
3   decimal priceAfterDiscount = 0;
4   priceAfterDiscount = _factory.GetAccountDiscountCalculator(accountStatus).ApplyDiscount(price);
5   priceAfterDiscount = _loyaltyDiscountCalculator.ApplyDiscount(priceAfterDiscount, timeOfHavingAccountInYears);
6   return priceAfterDiscount;
7 }

  记得在事先的斯马特 UI的事例中,程序的事情逻辑是一向写在了ASPX页前边边的cs代码中的。现在,采用分段的办法,大家接纳了世界模型来公司来电子商务中的业务逻辑。

总结

在本文中,代码被Infiniti简化了,使得全体的技艺和格局的表达更易于了。它突显了怎么着缓解左近的编制程序难题,以及利用优异的推行和设计形式以妥善、干净的法子消除这几个题指标利益。

在自笔者的办事经验中,作者频仍在那篇小说中强调了不良的做法。它们明显存在于广大运用场地,而不是在叁个类中,如在自身的例子中那么,那使得发现它们更是不方便,因为它们隐藏在适合的代码之间。写那种代码的人连连争辨说,他们依据的是大约愚钝的平整。不幸的是,差不离全数的种类都在成人,变得拾分复杂。然后,这些简单的、不可扩充的代码中的每二个改动都以分外重大的,并且拉动了伟大的高危机。

请牢记,您的代码将短期存在于生产环境中,并将在各种事情须求变动上拓展改动。因而编写过于不难、不可扩充的代码非常快就会发生严重的后果。最后一点是对开发人士有利,尤其是那多少个在您自个儿事后维护您的代码。

只要您有1部分标题依照小说不要犹豫联系笔者!

  有关领域模型的1些东西,我们在继续的稿子中会讲解的。

美高梅开户网址 13PS:

基于文章敲的源代码:

链接: 密码:9spz

  注:领域模型情势被设计用来公司复杂的政工逻辑和涉嫌。

 

  上边包车型大巴类图就呈现了小编们在此以前的电子商务的要求中所用到的事务模型。

美高梅开户网址 14

  Product类就表示了电子商务中的每三个出品。

  普赖斯类将会包罗可算折扣的事务逻辑,并且用政策情势来具体落到实处折扣的算法-。

  在Model添加1个接口类:IDiscountStrategy:

  

public interface IDiscountStrategy
{
        decimal ApplyExtraDiscountsTo(decimal OriginalSalePrice);
}

 

 

 

  这些接口就用来落到实处不一样降价的国策,这是策略形式的壹种采用。这些格局允许大家在运行的时候改变分裂的算法完结。在本例子中,Price类将会基于分裂的成品来贯彻分裂的优惠策略。在大家前边的不行SmartUI例子中,其实那些降价的算法大家已经写了,可是未有分离出来,导致了历次加二个减价的算法的国策,程序就供给转移,重新编写翻译,布置。也正是说降价的1部分是个变化点,大家应有分离出来的。 

注:策略形式:用1个类来封装叁个算法的落到实处,并且经过切换算法的落到实处允许在运行时修改2个对象的行为。

 

在电子商务中,不是每一种商品都会减价的,其实大家要落实的优惠策略唯有一种。不过只要这样,大家在写代码的时候就要if-else判断是还是不是是优惠的货色,其实那里还是揭示了变化点的:假使国庆那天,全数的货物都优惠了,那么大家就得修改代码。其实大家得以这么怀想:不降价的意况也究竟1种降价,其余的货品优惠恐怕是⑦折,不减价的意况便是10折。 

 

美高梅开户网址 15美高梅开户网址 16代码

 public class TradeDiscountStrategy : IDiscountStrategy 
{        
        public decimal ApplyExtraDiscountsTo(decimal OriginalSalePrice)
        {
            decimal price = OriginalSalePrice;            
            
            price = price * 0.6M;            

            return price;
        }     
}

public class NullDiscountStrategy :
IDiscountStrategy
{       
        public decimal ApplyExtraDiscountsTo(decimal
OriginalSalePrice)
        {
            return OriginalSalePrice;
        }
}

 

 

上边大家来探望普赖斯类的达成。

 

美高梅开户网址 17美高梅开户网址 18代码

public class Price
{
        private IDiscountStrategy _discountStrategy = new NullDiscountStrategy(); 
        private decimal _rrp;
        private decimal _sellingPrice;

        public Price(decimal RRP, decimal SellingPrice)
        {
            _rrp = RRP;
            _sellingPrice = SellingPrice;
        }

        public void SetDiscountStrategyTo(IDiscountStrategy DiscountStrategy)
        {
            _discountStrategy = DiscountStrategy; 
        }

        public decimal SellingPrice
        {
            get { return _discountStrategy.ApplyExtraDiscountsTo(_sellingPrice); }
        }

        public decimal RRP
        {
            get { return _rrp; }
        }

        public decimal Discount
        {
            get { 
                if (RRP > SellingPrice) 
                    return (RRP – SellingPrice); 
                else
                    return 0;}
        }

        public decimal Savings
        {
            get{
                if (RRP > SellingPrice)
                    return 1 – (SellingPrice / RRP);
                else
                    return 0;}
        }        
}

  

  Price类在设计中正是用了“依赖倒置原则”,因为它从不应用某3个切实的降价完结算法,而且正视于接口抽象,至于今后终归会哪个种类的优惠算法,其实是由货物的门类来决定的。 

  大家依然持续的看,今后看望Product类。

  

public class Product
{
        public int Id { get; set; }
        public string Name { get; set; }
        public Price Price { get; set; }
}

 

        

         今后怀有的业务实体就曾经创建了。至于对商品是还是不是优惠,其实那是由客户代码来控制:依照客户代码传入的货品的品种区别,然后调用差异的政策,接纳了差异的减价算法计算折扣。所以大家那边来添加1个象征商品品种的枚举:  

 public enum CustomerType
 {
        Standard = 0,
        Trade = 1
 }

 

 

  大家将会把选用哪个种类降价的政策的逻辑写在一个单身的地点,也等于说:只要客户代码传入相应的参数音信,大家就活动的创导3个分外的打折策略对象。很明朗,这里能够运用工厂方法来贯彻,如下:  

 

美高梅开户网址 19美高梅开户网址 20代码

public static class DiscountFactory
{
        public static IDiscountStrategy GetDiscountStrategyFor(CustomerType customerType)
        {
            switch (customerType)
            {
                case CustomerType.Trade:
                    return new TradeDiscountStrategy(); 
                default:
                    return new NullDiscountStrategy(); 
            }
        }
}

 

 

  在上头的逻辑分层中,大家树立了3个Repository的类库,其实我们即使想使用Repository形式来完结”持久化非亲非故性”—–业务类完全不用管怎样保存和获取数据。而且由Repository决定数据的源于和保留的地点,只怕是数据库,也恐怕就是内部存储器,不过不管怎么,业务类是毫无管这么些的。所以上面用1个接口来达成灵活性:  

 

 public interface IProductRepository
 {
        IList<Product> FindAll();
 }

 

 

  假如现在有不少的货品,大家想知道她们的折扣价格,最简易的不二诀要正是遍历他们,判断项目,然后利用区别的降价策略。为了越发的可读,我们能够为货品列表建立增添方法,如下:

  

美高梅开户网址 21美高梅开户网址 22代码

 public static class ProductListExtensionMethods
 {
      public static void Apply(this IList<Product> products, IDiscountStrategy discountStrategy)
      {
            foreach (Product p in products)
            {
                p.Price.SetDiscountStrategyTo(discountStrategy);
            }
      }
 }

 

 

  为了简化客户代码的调用工作,大家提供3个类似门户(gateway),也许是Façade的概念:把纷纭的操作逻辑隐藏,留给客户代码一个简短易用的API。大家那边创办1个Service类,如下:

  

美高梅开户网址 23美高梅开户网址 24代码

public class ProductService
{
   private IProductRepository _productRepository;

   public ProductService(IProductRepository productRepository)
   {
         _productRepository = productRepository;
   }

   public IList<Product> GetAllProductsFor(CustomerType customerType)
   {
      IDiscountStrategy discountStrategy = DiscountFactory.GetDiscountStrategyFor(customerType);
      IList<Product> products = _productRepository.FindAll();
      products.Apply(discountStrategy);
      return products;
    }    
}

 

 

  只要客户代码(如出示层中的代码)直接调用下面的点子就能够了,而且商品的折扣也遵照传入的商品品种区别来总结。

 

  三.       服务层设计

  服务层就担任应用程序的进口的剧中人物。有时候,能够被认为是façade.不仅如此,因为service分为领域逻辑的service和流派的service。门户的service平常为突显层提供强类型的View Model(有时也称之为Presentation Model)。 2个View Model正是给三个专程的View来使用的。在本例中,大家将会创制Product的View Model来体现商品的新闻。一般情状下,大家毫不把业务类间接揭发给展现层,那样很简单紧耦合,所以在中等就上二个View
Model,其实View Model和业务类的构造基本上,只是View
Model做了一些调整,便于最后的来得。关于View
Model详细的,后文讲述。

  注:Façade方式:为当中负责的子系统提供三个总结的接口供外部访问。

** 

  上面我们就来探望Product的View Model是怎么着写的:

 

美高梅开户网址 25美高梅开户网址 26代码

public class ProductViewModel
    {
        public int ProductId { get; set; }
        public string Name { get; set; }
        public string RRP { get; set; }
        public string SellingPrice { get; set; }
        public string Discount { get; set; }
        public string Savings { get; set; }
    }

 

 

  可以见见,其实View Model正是做了有个别显得逻辑的拍卖。在此地正是多加了有的字段,这个字段正是在UI的GridView中展示用的。大家事先的斯马特 UI的方法中,还创设了模版列来展现Product类中尚无的字段,其实就相当于在UI中作了自然的体现逻辑的处理。那里我们直接展现ViewModel.

 

  我们应该很熟习Web
Service:在客户端和劳动应用请求/响应的消息机制举办通讯的。我们那里的客户代码和瑟维斯也利用那种办法,因为很有一点都不小可能大家在配置的时候瑟维斯的代码和客户代码(展现层)在分化机器上。

  请求的音信的协会如下:  

 

 public class ProductListRequest
 {
        public CustomerType CustomerType { get; set; }
 }

 

 

  服务在响应请求的时候也要定义格式,而且我们得以在响应中参与更加多的性情来判断那些请求是或不是中标。所以在底下的代码中,大家进入了Message属性,用来在呼吁退步的时候显得错误消息,还添加了一个Success属性用来判定请求的情形:  

 

public class ProductListResponse
{
   public bool Success { get; set; }
   public string Message { get; set; }
   public IList<ProductViewModel> Products { get; set; }
}

 

 

  还有少数毫不遗忘了:因为Product和它对应的View Model结构不一样的,而Service再次回到的又是ViewModel的响应,那么就必要把收获到的Product转换为View Model的组织。可以把转换的代码写在2个一定的地点(能够认为是个Mapping的经过),为了阅读的有利,我们能够为List<Product>添加扩充方法,直接调用,如下:

  

 

美高梅开户网址 27美高梅开户网址 28代码

public static class ProductMapperExtensionMethods
    {
        public static IList<ProductViewModel> ConvertToProductListViewModel(this IList<Model.Product> products)
        {
            IList<ProductViewModel> productViewModels = new List<ProductViewModel>();

            foreach(Model.Product p in products)
            {
                productViewModels.Add(p.ConvertToProductViewModel());  
            }

            return productViewModels;
        }

        public static ProductViewModel ConvertToProductViewModel(this Model.Product product)
        { 
            ProductViewModel productViewModel = new ProductViewModel();
            productViewModel.ProductId = product.Id;
            productViewModel.Name = product.Name;
            productViewModel.RRP =product.Price.RRP;
            productViewModel.SellingPrice =product.Price.SellingPrice;
            
            if (product.Price.Discount > 0)
                productViewModel.Discount = product.Price.Discount;

            if (product.Price.Savings < 1 && product.Price.Savings > 0)
                productViewModel.Savings = product.Price.Savings.ToString(“#%”);

            return productViewModel;
        }
    }

 

 

  最终,我们参加叁个Product瑟维斯来与业务层的Service 类进行交互,业务层的Service会再次回到商品列表,然后大家以后增进的那个ProductService会把列表转为ProductViewModels。

 

大家兴许认为奇怪:为何那里添加了多个ProductService,以前在工作层加三个,今后又加一个,是还是不是命名分外大概成效重新?其实在上1篇已经提过:有时在事情层类添加一个service层,主假诺用来协会业务流程的,日常要多少个事情类组合在联合署名使用,那样事关心体贴大是为了简化客户程序(也便是调用那一个业务层的代码)的调用,达成类似Façade的机能。

 

咱俩明天添加的ProductService正是政工层中**service层**的客户程序,因为我们调用了业务层的service,往往有时,大家不想把团结系统的业务类的构造平昔揭发给外界,如展现层,而且也期望提供进一步吻合展现层所需的数据结构,那么大家就添加了这么些ProductService,提供从工作类到ViewModel的更换。而且在那么些ProductSevice中,我们也足以兑现部分可怜处理体制,要是涉嫌到了分布式调用,那么大家还足以用那一个ProductService类向展现层和UI那边隐藏分布式的音讯:达成代理方式。

今天就写在到此地,在写的长河中发觉这篇有点长了,所以分为3篇(前,中,后)揭橥!不理解的地点大家多研究一下,也足以告诉本人!下篇前几天发表!见谅!

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图