支行设计,拿什么拯救你

By admin in 4858.com on 2019年3月25日

本人的序文表明:

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

本种类还有其余小说,后续将逐日翻译。

 

此文为译文,原著地址请点击。
本文通过重构八个废物代码,演讲了何等写出能够的代码。开发人士及代码审核职员需依据此规范开发和查对代码。此标准以C#为例,JAVA的童鞋一并参考,C++的童鞋自行脑补吧。

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

引言:

笔者的名字叫Radoslaw
Sadowski,我前天是贰个微软技术开发人士。笔者从初阶工作时就直接接触的微软技术.

在劳作一年后,笔者来看的成色很差的代码的数据基本上都得以写成一整本书了。

这几个经历让小编变成了多个想要清洁代码的人格障碍病者。

写那篇文章的目标是为着通过展现品质很差的类的例证来证实怎么样下笔出到底的、可延长的和可保证的代码。作者会通过好的书写模式和设计格局来解释坏的代码带来的题材,以及替换他的好的化解措施。

首先有些是本着那几个具有C#基础知识的开发人士——作者会议及展览示一些广阔的失实,然后再彰显一些让代码变得可读性的章程与技能。高级部分重点针对那二个至少存有设计情势概念的开发职员——小编将会显得完全绝望的、单元可测试的代码。

为了可以领悟那篇小说你必要至少掌握以下多个部分的基本知识:

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

正文中所涉及的事例都将会是具体中如实的具体的特色,而不是用装饰方式来做披萨可能用政策方式来做总计器那样的示范。

(ps解释:看过设计形式相关的书本的人应当会精晓许多那上边的书籍都以用那种例子,只是为了协理读者知道设计形式)

                           
  4858.com 1 
     
  4858.com 2

因为自个儿意识那系列型的制品不好用来诠释,相反那几个理论性的事例却是格外适合用来在本文中做表达的。

我们日常会听到说不用用这些,要用那1个,不过却不通晓那种替换的说辞。明日自家将会竭力解释和申明那一个好的书写习惯以及设计格局是当真是在援救大家的开销生活!


此文为译文,原来的小说地址请点击。
本文通过重构三个废物代码,演说了什么样写出能够的代码。开发人士及代码审核职员需依据此规范支出和审核代码。此标准以C#为例,JAVA的童鞋一并参考,C++的童鞋自行脑补吧。

  前言:自从上篇发表之后,大家反映了过多难点,因为前篇讲的事物不是很深,大概我们看完之后并未什么感觉.本章(前篇,中篇,后篇)的要害指标其实首先是提议不好的筹划,然后相比的提议叁个周旋相比合理的支行架构,同时本篇也为后续讲述架构形式和设计方式等的小说做个铺垫。

 提示:

  •  在本文中自个儿不会花时间来讲解C#的性状和关系方式等等(小编也说明不完),网上有广大有关那地点的好的申辩的事例。笔者将聚齐讲述怎么着在我们平常工作中应用这个东西。
  • 事例是一种相比较简单的崛起大家要表明的难题的点子,可是仅限于描述的题材——因为本人发觉当自家在就学如何包蕴注重要代码的例牛时,笔者意识在领略文章的一体化考虑方面会有困难。
  •  小编不是说小编文中说的法子是惟一的缓解措施,我只是能有限辅助那些方法将会是让您的代码变得更高品质的门道。
  • 自作者并不关切上面这个代码的怎么错误处理,日志记录等等。小编要抒发的只是用来缓解一般编码一些问题的方法。

这就从头吧….

简介

那篇小说的指标是突显如何将一段垃圾代码重构成1个完完全全的、可扩充性和可保证的代码。作者将表明怎么着通过一流实践和更好的设计情势来改写它。

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

  • 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.取名形式-从源代码中我们能够连蒙带猜推断出来这几个总结方法和出口结果是哪些。而且大家想要从这些类中领到总括算法将会是一件11分狼狈的工作。

如此带来的危机是:

最严重的难点是:浪费时间,

4858.com 3

 

要是大家必要满足客户的小购销咨询,要像她们出示算法细节,只怕大家需求修改这段代码,那将消费大家非常短的时光去领略大家的计算方式的逻辑。即便我们不记录她或重构代码,下次大家/其余开发人士再看那段代码的时候,依然必要费用一样的岁月来切磋那一个代码是干嘛的。而且在改动的还要还简单出错,导致原先的测算全部弄错。

 2.魔法数字

 4858.com 4

在那一个例子中type是变量,你能猜到它代表着客户账户的等级么?If-else
if
言语是用来达成怎么样抉择总括出产品价格折扣的方法。

未来大家不清楚怎么着的账户是1,2,3或4。今后想像一下,当你只可以为了那么些有价值的VIP客户改变她们的折扣计算办法的时候,你试着从那一个代码中找出修改的方法—那几个历程只怕会花费你十分长的时刻不说,还很有大概犯错以至于修改那个基础的一般的客户的账户,毕竟像2要么3那个用语毫无描述性的。不过在大家犯错未来,那一个一般的客户却很高兴,因为他俩赢得了VIP客户的折扣。:)

3.从没掌握的bug

因为我们的代码质量很差,而且可读性非常差,所以我们大概随便就忽略掉很多杰出重庆大学的业务。想象一下,将来黑马在系统中加进一种新的客户类型-金卡用户,而在我们的系统中此外一种新的账户类型最终获得的标价将是0元。为何吧?因为在我们的if-else
if
语句中一向不任何动静是满意新的图景的,所以假诺是未处理过的账户类型,最终重临值都将变成0。一旦大家的老总娘发现那件事,他将会老羞成怒-究竟她已经免费卖给那样用户很多过多东西了!

4858.com 5

4.支行设计,拿什么拯救你。不曾可读性

小编们不可能不认同上边那段代码的可读性是真的不得了。

她让我们费用了太多的岁月去领会那段代码,同时期码隐藏不当的可能率太大了,而那便是尚未可读性的最根本的概念。

 5.魔法数字(再次)

您从代码中能知道好像0.1,0.7,0.5那几个数字的趣味么?好的,作者承认本人不亮堂。唯有大家和好编辑那么些代码大家才清楚那是何许意思,旁人是力不从心知晓的。

你尝试想想假设让您改改下边那句代码,你会什么:

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

因为那一个方法完全不可读,所以你改改的进度中不得不尝试着把第一个0.5改成0.4而保持首个0.5不懂。那说不定会是二个bug,可是却是最棒的最合适的修改章程。因为这几个0.5如何都没有告知大家。

一致的事也设有将years变量转换成disc变量的变换进程中:

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

那是用来计算折扣率的,会经过账户在咱们系统的时间的百分比来获取。好的,那么今后难点来了,借使时光刚刚好就是5吗?

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

虽说第二当即的时候不易于看出来,可是仔细研商一下就会发现:大家的代码里有司空见惯双重的地点。例如:disc *
(amount – (0.1m * amount));

而与之有同一效果的还有(只是变了贰个参数而已):disc * (amount –
(0.5m * amount))

在那三个算术中,唯一的界别就只是2个静态参数,而我们全然能够用3个可变的参数来顶替。

借使大家不试着在写代码的时候从第③手ctri+c,ctrl+v中解脱出来,那大家将遇到的标题就是大家只好修改代码中的部分功效,因为我们不精晓某些许地方需求修改。下面的逻辑是测算出在大家系统中种种客户对应年限获得的折扣,所以倘使我们只是贸然修改两到三处,很简单导致任哪儿方的左右不均等。

7.各种类具有太多的复杂性的权责区域

我们写的类至少背负了四个权利:

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

其一背离了单纯权利原则。那么那会推动什么损伤吗?假若大家想要改变上诉壹特个性中的四个,那就表示恐怕会碰触到部分别的的大家并不想修改的表征。所以在修改的时候大家只能再度测试全部的类,那么那就招致了很重的日子的浪费。

一段垃圾代码

在大家实在的出品中有如此八个类:

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

那是一段万分糟糕的代码(二胡:要是你没以为这段代码很不佳,那您眼下气象大概很不好了),大家不太驾驭这么些类的目标是怎么着。它或然是二个网上商城的折扣管理类,负责为客户总括折扣。

以此类完全具备了不足读、不可维护、不可扩张的特征,它利用了诸多坏的进行和反方式的筹划。

上边我们稳步分析那里究竟有稍许难点?

  • 取名难点 –
    大家不得不通过推断那么些类到底是为着总计什么。那实则是在浪费时间。
    设若大家一贯不相关文书档案或然重构那段代码,那大家下一次恐怕供给花大批量的光阴才能精通这段代码的具体意思。

  • 魔数 –
    在那么些事例中,你能猜到变量type是指客户账户的意况吧。通过if-else来挑选总括优惠后的产品价格。
    现今,大家压根不清楚账户状态1,2,3,4独家是何许看头。
    此外,你精晓0.1,0.7,0.5都是什么样意思啊?
    让大家想象一下,就算您要修改上面那行代码:
    result = (amount - (0.5m * amount)) - disc * (amount - (0.5m * amount));

  • 隐形的BUG –
    因为代码非常不好,大家恐怕会失掉万分重要的业务。试想一下,要是我们的系统中新增了一类账户状态,而新的账户等级不满足任何三个if-else条件。这时,再次回到值会固定为0。

  • 不可读 –
    大家只能认可,那是一段不可读的代码。不可读=越多的精晓时间+扩大发生错误的高风险

  • DLacrosseY – 不要发生重复的代码
    咱俩可能不能够一眼就找出它们,但它们确实存在。
    例如:disc *(amount - (0.1m * amount));
    相同的逻辑:
    disc *(amount - (0.5m * amount));
    那里只有3个数值不平等。借使大家不能够抽身再度的代码,大家会遇见许多标题。比如某段代码在三个地点有重复,当咱们须求修改那有的逻辑时,你很或许只修改到了2至3处。

  • 单一任务规范
    本条类至少存有八个职务:
    1 选用总括算法
    2 遵照账户状态计算折扣
    3 依照账户网龄总结折扣
    它违反了单纯职分规范。那会带来什么样危机?如若大家即将修改第多少个功效的话,会影响到别的第1个成效。那就象征,大家每便修改都会转移大家本不想修改的代码。因而,我们只可以对任何类举办测试,那其实很浪费时间。

简介

那篇小说的目标是呈现怎么样将一段垃圾代码重构成3个完完全全的、可扩张性和可尊敬的代码。小编将表达怎么着通过顶尖实践和更好的设计格局来改写它。

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

  • c# 基础
  • 依靠注入,工厂格局,策略格局

此文中的例子源于实际项目,那里不会有如何使用装饰情势营造的披萨,也不会利用政策方式的总结器,这么些事例是老大好的辨证,可是它们很难被在实际项目中动用。

本篇的议题如下:

1. style=”font-family: 小篆; color: red”>注脚示例供给

2. style=”font-family: 小篆; color: red”>业务层设计

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

4. style=”font-family: 草书; color: red”>数据访问层设计

5. style=”font-family: 大篆; color: red”>展现层设计

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

那就从头重构吧…

在接下去的柒个步骤中笔者将向你显得大家怎样制止上诉问题来营造三个到底的易维护,同时又便于单元测试的看起来一目驾驭的代码。

 

重构

经过以下9布,作者会告诉你们怎么着制止上述风险并促成3个到底的、可保险的、可测试的代码。

  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,2,3到底是怎么样意思。

  1. 魔数
    在C#中幸免魔数大家一般选用枚举来替换它们。那里运用AccountStatus
    枚举来替换if-else中的魔数。
    public enum AccountStatus { NotRegistered = 1, SimpleCustomer = 2, ValuableCustomer = 3, MostValuableCustomer = 4 }
    现在大家来看正视构后的类,大家能够很不难的表露哪1个账户状态应当用哪些算法来总计折扣。混合账户状态的高危害飞快的消沉了。

     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
    来增长代码可读性。
    再正是,小编还将有个别长度相当长的语句才分成两行。比如:**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。
    当大家的办法取得账户状态作为输入参数,可是参数值也许带有大家未规划到的未知情状。那时,我们不能怎样也不做,抛出10分是此时最佳的做法。

        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.1m,0.2m,0.3m,作者m,我们并不知道它们是哪些意思。
    此外decimal discountForLoyaltyInPercentage =
    (timeOfHavingAccountInYears > 5) ? (decimal)5/100 :
    (decimal)timeOfHavingAccountInYears/100;
    中,数字5也不行神秘。
    大家不可能不让它们更有着描述性,那时使用常量会是3个比较好的主意。
    大家来定义二个静态类:

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

9.结尾,获得彻底的代码
终极,让我们通过引入依赖注入和工厂方法情势来博取终极的版本吧。
先来看卡最后结果:

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

第2,大家摆脱了扩展方法(静态类),若是大家想对ApplyDiscount方法进行单元测试是相比费力的,打消大家对PriceExtensions增加类也拓展测试。
为了制止这几个题目,大家成立了DefaultLoyaltyDiscountCalculator类来替换ApplyDiscountForTimeOfHavingAccount那几个扩充方法,此类还落到实处了ILoyaltyDiscountCalculator接口。今后,当大家要测试DiscountManager类时,大家只构造函数注入ILoyaltyDiscountCalculator接口的兑现即可。那里运用了依靠注入。
通过如此做,大家将网龄折扣的算法迁移到接近DefaultLoyaltyDiscountCalculator的不一样类中,那样当大家修改某一个算法不会覆盖到别的业务。
对于依据账户状态来估测计算折扣的业务,大家需求在DiscountManager中删去三个职务:

  • 根据账户状态选择总括的算法

  • 贯彻总结算法
    那里大家透过DefaultAccountDiscountCalculatorFactory工厂类来缓解那一个题材,DefaultAccountDiscountCalculatorFactory工厂类完毕了IAccountDiscountCalculatorFactory接口。
    小编们的工厂将控制取舍哪3个倒扣算法。接着,工厂类被通过构造函数注入到DiscountManager类中。
    上边作者只要求在DiscountManager 中使用工厂:
    priceAfterDiscount = _factory.GetAccountDiscountCalculator(accountStatus).ApplyDiscount(price);
    以上,我们解决了第三个难点,上边咱们须求贯彻总结算法。依据账户状态,提供分裂的算法,那刚好适合政策情势。大家须要营造三个政策:NotRegisteredDiscountCalculator,SimpleCustomerDiscountCalculator,MostValuableCustomerDiscountCalculator早已室如悬磬出来的接口IAccountDiscountCalculator
    好了,未来我们有可一段干净可读的代码了,那段代码中全体的类都只有一个职分:

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

一段垃圾代码

在大家实际的制品中有如此叁个类:

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

那是一段乌烟瘴气的代码(二胡:假若您没觉得那段代码很不好,那你日前事态恐怕很倒霉了),我们不太精晓这么些类的指标是何许。它恐怕是三个网上商城的折扣管理类,负责为客户总计折扣。

其一类完全拥有了不可读、不可维护、不可扩充的表征,它利用了许多坏的实践和反情势的安顿性。

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

  • 命名难题 –
    大家不得不通过估摸那几个类到底是为了总计什么。那实则是在浪费时间。
    要是我们从未相关文书档案大概重构那段代码,那咱们下一回或许须要花多量的年华才能掌握那段代码的切切实实意思。

  • 魔数 –
    在这些事例中,你能猜到变量type是指客户账户的图景吧。通过if-else来抉择总括减价后的产品价格。
    以后,大家压根不驾驭账户状态1,2,3,陆分头是怎么着意思。
    此外,你领会0.1,0.7,0.5都是怎么意思啊?
    让大家想象一下,若是您要修改上边那行代码:
    result = (amount - (0.5m * amount)) - disc * (amount - (0.5m * amount));

  • 隐藏的BUG –
    因为代码分外不好,大家或者会失掉非常重庆大学的事体。试想一下,若是大家的系列中新增了一类账户状态,而新的账户等级不满足任何多少个if-else条件。那时,重返值会固定为0。

  • 不行读 –
    大家只可以认可,那是一段不可读的代码。不可读=越多的接头时间+扩张发生错误的高风险

  • D本田CR-VY – 不要发生重复的代码
    大家只怕无法一眼就找出它们,但它们确实存在。
    例如:disc *(amount - (0.1m * amount)); 同样的逻辑:
    disc *(amount - (0.5m * amount));
    这里唯有贰个数值不雷同。假诺大家无能为力脱身再度的代码,咱们会遇见不少难点。比如某段代码在多少个地点有重复,当我们供给修改那有些逻辑时,你相当的大概只修改到了2至3处。

  • 单一义务规范 这几个类至少存有四个职务: 1 选用总括算法 2
    依照账户状态总结折扣 3 依照账户网龄总括折扣
    它违反了单一职务规范。那会拉动什么危机?倘若大家即将修改第多个效益的话,会影响到其余第一个成效。那就象征,大家每一趟修改都会改变大家本不想修改的代码。由此,我们只好对全部类举办测试,那实在很浪费时间。

 ** ** **

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 }

虽说那样,大家照旧不晓得1,2,3,4象征着什么,那就持续往下啊!

总结

正文通过不难易懂的法门重构了一段难点代码,它显得了何等在事实上情形中央银行使最棒实践和设计格局来补助咱们写出干净的代码。
就小编的干活经历来说,本文中出现的不善做法是时常发生的。编写那种代码的人连续认为他俩能够保险那种规则,但不幸的是系统和工作往往都会更为复杂,每一趟修改那类代码时都会带来巨大的风险。

重构

经过以下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,2,3到底是怎么看头。

  2. 魔数
    在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;
        }
    }
    
  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.2m,0.3m,作者m,大家并不知道它们是什么样意思。
    其它decimal discountForLoyaltyInPercentage =
    (timeOfHavingAccountInYears > 5) ? (decimal)5/100 :
    (decimal)timeOfHavingAccountInYears/100;
    中,数字5也要命神秘。
    大家亟须让它们更享有描述性,那时使用常量会是三个比较好的措施。
    大家来定义三个静态类:

    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. 扫除重复的代码
    为了清除重复的代码,这里将一部分算法提取出来。首先,我们建立八个扩展方法:

    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产生的机率,并且也让我们减少精通事情逻辑的时光。
    大家发现,那里二种情景的客户调用了扳平的措施:
    .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;
    }
    }
    

    9.最后,获得彻底的代码
    最终,让我们经过引入注重注入和工厂方法情势来博取最后的本子吧。
    先来看卡最后结果:

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

    第2,我们摆脱了扩充方法(静态类),要是大家想对ApplyDiscount艺术开始展览单元测试是相比不方便的,撤除大家对PriceExtensions扩张类也展开测试。
    为了制止这些标题,我们创建了DefaultLoyaltyDiscountCalculator 类来替换ApplyDiscountForTimeOfHavingAccount那几个扩充方法,此类还落到实处了ILoyaltyDiscountCalculator接口。今后,当我们要测试DiscountManager类时,大家只构造函数注入ILoyaltyDiscountCalculator接口的贯彻即可。那里运用了借助注入。
    通过如此做,我们将网龄折扣的算法迁移到近似DefaultLoyaltyDiscountCalculator 的不一致类中,那样当大家修改某一个算法不会覆盖到其余作业。
    对于依据账户状态来计算折扣的事体,大家必要在DiscountManager中删去五个义务:

  • 据他们说账户状态选拔计算的算法
  • 达成总结算法
    那里大家通过DefaultAccountDiscountCalculatorFactory工厂类来消除这么些标题,DefaultAccountDiscountCalculatorFactory工厂类完成了IAccountDiscountCalculatorFactory接口。
    我们的工厂将决定选拔哪1个倒扣算法。接着,工厂类被通过构造函数注入到DiscountManager类中。
    上面小编只需求在DiscountManager 中选择工厂:
    priceAfterDiscount = _factory.GetAccountDiscountCalculator(accountStatus).ApplyDiscount(price);
    以上,大家缓解了第⑤个难点,上面大家供给达成总括算法。遵照账户状态,提供不一样的算法,那刚刚符合政策方式。大家须要创设多个政策:NotRegisteredDiscountCalculator,SimpleCustomerDiscountCalculator,MostValuableCustomerDiscountCalculator早就一贫如洗出来的接口IAccountDiscountCalculator
    好了,未来大家有可一段干净可读的代码了,那段代码中保有的类都唯有一个任务:
  • 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 }

总结

正文通过简单易懂的艺术重构了一段难题代码,它显得了怎么着在事实上处境中运用最棒实践和设计格局来扶助大家写出干净的代码。
就自笔者的行事经历来说,本文中冒出的涂鸦做法是不时发生的。编写那种代码的人三番4次认为她们力所能及保证那种规则,但不幸的是系统和事务往往都会越发复杂,每回修改那类代码时都会带动巨大的风险。

  本篇依然用事先的电子商务网站中的多个归纳的景色来叙述:在页面上急需出示产品的列表音讯。并且依照产品的品类不一致,总计出相应的折扣。 

III:更加多的可读性

在这一步中大家将由此将if-else
if
 语句改为switch-case 语句,来扩充文章的可读性。

同时,作者也将1个不长的乘除办法拆分为两句话来写。今后大家将“
通过账户状态来估测计算折扣率”与“通过账户定期来总括折扣率”那二者分别来计量。

例如: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。纵然很不佳,但却是真的。

那我们该如何修复那几个标题吧?这就只有经过没有不当提示了。

4858.com 6

你是否会想,那几个会不会是支付的两样,应该不会被交付到不当提醒中去?不,他会的!

当大家的办法通过取得账户状态作为参数的时候,大家并不想程序让我们不足预见的样子提升,造成不可预测的失误。

 那种景色是纯属不容许出现的,所以大家务必透过抛出极度来严防那种情况。

下边包车型地铁代码正是经过抛出格外后修改的以制止出现不满意条件的状态-修章是将抛出十二分制止 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)

4858.com 7

VI:通过其余措施再摆脱魔法数

接下去让我们的目光放在通过账户状态计算折扣率的计量办法中的静态变量:(static_discount_in_percentages/100)

下一场带入下边数字距离试试:0.1m,0.3m,0.5m

那几个数字其实也是一种档次的魔法数-他们也从未直接告诉大家他们表示着哪些。

作者们也有同样的情景,比如将“有账户的时光”折价为“忠诚折扣”。

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

数字5让大家的代码变得神秘了四起。

大家务必做些什么让这一个变得更具表现性。

自笔者会用别的一种办法来制止魔法数的表述的面世-相当于C#中的常量(关键词是const),作者强烈提议在大家的应用程序中等专业高校门定义多少个静态类来储存这么些常量。

在大家的事例中,笔者是创设了上边的类:

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 }

自家梦想您也认同本身这一个格局会进一步使代码自个儿变得更富有说明性:)

只怕有个别朋友认为从斯Matt UI马上跳到这种分层设计,仿佛快了些。其实也终于三个探讨的弹跳吧。下边就来看看那种分层是何许缓解此前斯马特UI的难题的。 

VII:不要再另行啦!

4858.com 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 }

正如方法的名字一般,作者不再须求独自解释3回他们的作用是如何。将来就初始在大家的事例中运用那么些代码吧:

 

 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:移除那么些多余的代码

在写代码的时候条件上是我们的代码越是精简越好。精简的代码的表示,越少的荒谬的也许性,在读书精通代码逻辑的时候开支的时光越少。

之所以今后初步精简咱们的代码吧。

我们得以无限制发现大家二种客户账户下具有相同的办法:

.ApplyDiscountForTimeOfHavingAccount(timeOfHavingAccountInYears);

咱俩可不得以只写三遍啊?大家事先将未注册的用户放在了抛出非凡中,因为我们的折扣率只会预计注册用户的定期,并从未给未注册用户留有效用设定。所以,我们相应给未注册用户设定的时间为多少吗?
-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语句外面。好处正是:更少的代码量!

  2.  业务层设计

IX:进步-最终的得到根本清洁的代码

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

4858.com 9

好的,那就起来做一些变动来贯彻那一个指标吗。大家得以应用重视注入和利用政策格局那二种情势。

那正是大家今天最终整理出来的代码了:

 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类中经过构造函数显示测试效用。那里我们运用的就叫依赖注入情势。

4858.com 10

在做那一个的同时,大家也将计算折扣率这些作用安全的移交到另三个不等的类中,借使我们想要修改这一段的逻辑,这大家就只要求修改DefaultLoyaltyDiscountCalculator** **类就好了,而不须要转移别的的地点,那样收缩了在改变他的时候发生破坏其余地点的高风险,同时也不供给再追加单独测试的时光了。

上边是我们在DiscountManager类中选择分别的逻辑类:

priceAfterDiscount =
_loyaltyDiscountCalculator.ApplyDiscount(priceAfterDiscount,
timeOfHavingAccountInYears);

为了针对账户状态的逻辑来计量折扣率,笔者创建了部分相比复杂的东西。我们在DiscountManager类中有多个职分要求解释出去。

  1. 依据账户状态怎样接纳相应的盘算格局。
  2. 出奇总计办法的底细

为了将首先个职分移交出去,笔者创制了工厂类(DefaultAccountDiscountCalculatorFactory),为了促成工厂情势,然后再把那个隐蔽到虚幻IAccountDiscountCalculatorFactory里面去。

4858.com 11

我们的厂子会操纵取舍哪类总括方式。最终大家透过重视注册格局构造函数将工厂情势注射到DiscountManager类中

上面就是选取了工厂的DiscountManager类:

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

 以上会针对分化的账户状态重回曾几何时的国策,然后调用ApplyDiscount 方法。

首先个职分已经被接入出去了,接下去正是第三个了。

 接下来大家就从头商量策略了…..

4858.com 12

因为差异的账户状态会有永不的折扣总括办法,所以我们需求区别的贯彻政策。座椅非凡适用于政策方式。

在大家的例证中,大家有二种政策:

NotRegisteredDiscountCalculator SimpleCustomerDiscountCalculator MostValuableCustomerDiscountCalculator**

他们带有了实际的折扣总结方法的兑现并被藏在了抽象IAccountDiscountCalculator里。

那就允许我们的类DiscountManager利用方便的国策,而不须要知道具体的落实。我们的类只须要明白与ApplyDiscount方法相关的IAccountDiscountCalculator 接口再次回到的对象的门类。

NotRegisteredDiscountCalculator, SimpleCustomerDiscountCalculator,
MostValuableCustomerDiscountCalculator
这一个类富含了实际的通过账户状态选择符合计算的盘算办法的兑现。因为我们的那七个政策看起来相似,我们唯一能做的基本上就只有针对那三种计算策略成立贰个艺术然后各个策略类通过二个并非的参数来调用她。因为那会让大家的代码变得尤其多,所以本人后天控制不这么做了。

好了,到最近停止大家的代码变得可读了,而且每一个类都惟有三个职分了-那样修改他的时候会独自一一对应了:

  1. DiscountManager-管理代码流
  2. DefaultLoyaltyDiscountCalculator4858.com ,-可相信的乘除折扣率的章程
  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代码中的。今后,选用分段的方法,大家使用了世界模型来协会来电子商务中的业务逻辑。

总结

在本文中,代码被无限简化了,使得全部的技艺和形式的诠释更易于了。它显示了何等缓解广大的编制程序难点,以及利用优良的执行和设计格局以十分、干净的方法解决这一个难题的便宜。

在本人的工作经验中,小编再三在那篇小说中强调了蹩脚的做法。它们显明存在于广大采取场馆,而不是在3个类中,如在本身的例子中那样,那使得发现它们更是困难,因为它们隐藏在方便的代码之间。写那种代码的人连连争论说,他们依据的是简约鸠拙的条条框框。不幸的是,大概拥有的种类都在成人,变得格外复杂。然后,那一个简单的、不可增加的代码中的每二个改动都是很是主要的,并且推动了远大的危机。

请记住,您的代码将长期存在于生产条件中,并将在各样事情须要变动上进展改动。因而编写过于简短、不可扩充的代码非常快就会发出严重的结局。最终一点是对开发人士有利,越发是那些在你协调从此维护您的代码。

若果你有一对标题依据小说不要犹豫联系小编!

  有关领域模型的有的事物,我们在继承的作品中会讲解的。

4858.com 13PS:

依照作品敲的源代码:

链接: 密码:9spz

  注:领域模型模式被规划用来组织复杂的业务逻辑和事关。

 

  下边的类图就展示了大家事先的电子商务的要求中所用到的政工模型。

4858.com 14

  Product类就意味着了电子商务中的每三个出品。

  Price类将会含有可算折扣的事体逻辑,并且用政策形式来具体落到实处折扣的算法-。

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

  

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

 

 

 

  那一个接口就用来促成差异优惠的策略,那是政策情势的一种选用。那几个形式允许大家在运转的时候改变不一样的算法达成。在本例子中,Price类将会基于分化的出品来落到实处区别的优惠策略。在咱们事先的百般SmartUI例子中,其实那些打折的算法大家早就写了,可是没有分离出来,导致了每一次加3个降价的算法的策略,程序就要求转移,重新编写翻译,布置。也正是说打折的一些是个变化点,大家应有分离出来的。 

注:策略方式:用四个类来封装3个算法的贯彻,并且通过切换算法的贯彻允许在运作时修改一个目的的一颦一笑。

 

在电子商务中,不是各样商品都会降价的,其实我们要实现的优惠策略唯有一种。可是若是如此,我们在写代码的时候将要if-else判断是不是是优惠的商品,其实那里如故暴光了变化点的:假诺国庆那天,全部的货品都降价了,那么我们就得修改代码。其实大家能够那样考虑:不优惠的情形也究竟一种降价,其余的货色降价可能是7折,不降价的状态正是10折。 

 

4858.com 154858.com 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;
        }
}

 

 

上面大家来看看Price类的落实。

 

4858.com 174858.com 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类在筹划中便是用了“注重倒置原则”,因为它没有应用某叁个现实的降价达成算法,而且重视于接口抽象,至于事后究竟会哪一类的打折算法,其实是由商品的花色来决定的。 

  我们照旧持续的看,以后看望Product类。

  

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

 

        

         未来全数的工作实体就曾经创制了。至于对商品是不是降价,其实那是由客户代码来支配:依照客户代码传入的货品的种类分歧,然后调用区别的政策,选取了差异的减价算法总括折扣。所以大家那里来添加三个意味商品品种的枚举:  

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

 

 

  大家将会把挑选哪类降价的策略的逻辑写在多少个独立的地点,也正是说:只要客户代码传入相应的参数消息,我们就自行的创办三个适用的促销策略对象。很分明,这里能够行使工厂方法来贯彻,如下:  

 

4858.com 194858.com 20代码

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

 

 

  在下面的逻辑分层中,我们创建了二个Repository的类库,其实大家就算想接纳Repository格局来兑现”持久化非亲非故性”—–业务类完全不用管什么保存和获取数据。而且由Repository决定数据的来源和保留的地点,大概是数据库,也或然正是内部存款和储蓄器,可是不管怎么,业务类是并非管那几个的。所以上面用一个接口来贯彻灵活性:  

 

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

 

 

  若是前天有不可枚举的货品,我们想精晓他们的折扣价格,最简便的不二法门便是遍历他们,判断项目,然后使用分化的降价策略。为了特别的可读,我们得以为商品列表建立扩充方法,如下:

  

4858.com 214858.com 22代码

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

 

 

  为了简化客户代码的调用工作,大家提供一个近似门户(gateway),恐怕是Façade的定义:把纷纭的操作逻辑隐藏,留给客户代码2个归纳易用的API。我们这里创办3个Service类,如下:

  

4858.com 234858.com 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;
    }    
}

 

 

  只要客户代码(如出示层中的代码)直接调用上面包车型地铁主意就能够了,而且商品的折扣也根据传入的货品体系分化来总计。

 

  3.       服务层设计

  服务层就出任应用程序的入口的剧中人物。有时候,能够被认为是façade.不仅如此,因为service分为领域逻辑的service和门户的service。门户的service平常为展现层提供强类型的View Model(有时也号称Presentation Model)。 二个View Model便是给3个专程的View来使用的。在本例中,大家将会建立Product的View Model来呈现商品的音信。一般景色下,大家决不把业务类直接揭发给呈现层,那样很简单紧耦合,所以在中等就上贰个View
Model,其实View Model和业务类的构造基本上,只是View
Model做了部分调整,便于最后的来得。关于View
Model详细的,后文讲述。

  注:Façade形式:为当中负责的子系统提供贰个简易的接口供外部访问。

** 

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

 

4858.com 254858.com 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中显示用的。我们从前的Smart UI的点子中,还树立了模版列来展现Product类中平昔不的字段,其实就一定于在UI中作了一定的展现逻辑的拍卖。那里大家一贯体现ViewModel.

 

  大家应该很纯熟Web
Service:在客户端和劳动应用请求/响应的音信机制实行通讯的。咱们那边的客户代码和Service也选拔那种措施,因为很有也许我们在陈设的时候Service的代码和客户代码(展现层)在区别机器上。

  请求的新闻的布局如下:  

 

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

 

 

  服务在响应请求的时候也要定义格式,而且我们能够在响应中插足越多的质量来判定那么些请求是不是成功。所以在上边包车型地铁代码中,我们进入了Message属性,用来在伸手战败的时候显得错误音讯,还添加了3个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>添加扩充方法,直接调用,如下:

  

 

4858.com 274858.com 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;
        }
    }

 

 

  最终,大家插足三个ProductService来与业务层的Service 类进行交互,业务层的Service会再次回到商品列表,然后大家明天丰硕的那些ProductService会把列表转为ProductViewModels。

 

世家大概认为意外:为何那边添加了四个ProductService,从前在作业层加三个,未来又加三个,是或不是命名非常可能成效重新?其实在上一篇已经提过:有时在工作层类添加三个service层,首假如用来公司业务流程的,经常要几个工作类组合在联合利用,那样重点是为了简化客户程序(也正是调用那几个业务层的代码)的调用,达成类似Façade的作用。

 

大家前几天丰硕的ProductService就是事务层中**service层**的客户程序,因为大家调用了业务层的service,往往有时,大家不想把本身系统的业务类的布局一贯暴光给外界,如展现层,而且也可望提供越发契合呈现层所需的数据结构,那么大家就添加了那个Product瑟维斯,提供从业务类到ViewModel的转移。而且在这几个ProductSevice中,我们也得以完毕部分不行处理体制,假若波及到了分布式调用,那么大家还是能用这一个ProductService类向显示层和UI那边隐藏分布式的新闻:达成代理形式。

前日就写在到那边,在写的历程中窥见那篇有点长了,所以分为3篇(前,中,后)公布!不明了的地点大家多探讨一下,也足以告知小编!下篇后天公布!见谅!

 

发表评论

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

网站地图xml地图
Copyright @ 2010-2019 美高梅手机版4858 版权所有