自个儿的代码,如何将坏的代码重新编写翻译为好的代码

友善的题词表明:

 本文原版的书文者:Radoslaw
Sadowski自个儿的代码,如何将坏的代码重新编写翻译为好的代码。,原版的书文链接为:C#
BAD PRACTICES: Learn how to make a good code by bad
example。

本体系还有其余小说,后续将日益翻译。

 

此文为译文,原来的小说地址请点击。
正文通过重构三个杂质代码,解说了什么写出卓越的代码。开发职员及代码审核职员需遵守此标准开发和审批代码。此规范以C#为例,JAVA的童鞋1并参考,C++的童鞋自行脑补吧。

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

引言:

本人的名字叫Radoslaw
Sadowski,笔者以往是3个微软技术开发人士。小编从起头工作时就径直接触的微软技术.

在做事一年后,作者看来的质量很差的代码的数量基本上都能够写成壹整本书了。

那些经验让自个儿成为了三个想要清洁代码的人格障碍病人。

写那篇小说的指标是为了通过展现品质很差的类的例子来申明怎么着下笔出干净的、可延伸的和可爱抚的代码。小编会通过好的书写形式和设计情势来诠释坏的代码带来的标题,以及替换他的好的缓解方法。

先是片段是指向那八个负有C#基础知识的开发职员——作者会议及展览示1些普遍的失实,然后再展现一些让代码变得可读性的不2秘诀与技能。高级部分重点针对那一个至少存有设计格局概念的开发职员——笔者将会议及展览示完全绝望的、单元可测试的代码。

为了能够知道这篇小说你需求至少明白以下多个部分的基本知识:

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

本文中所涉及的例子都将会是切实中的确的切实可行的特色,而不是用装饰方式来做披萨可能用政策形式来做计算器那样的以身作则。

(ps解释:看过设计格局相关的图书的人相应会清楚许多那地点的书籍都以用那种例子,只是为着扶持读者了然设计方式)

                           
  亚洲必赢官网 1 
     
  亚洲必赢官网 2

因为自己意识那体系型的产品不好用来解释,相反那么些理论性的事例却是格外适合用来在本文中做解释的。

笔者们平日会听到说毫不用这几个,要用这几个,然则却不知道那种替换的理由。明天自个儿将会竭力解释和验证那个好的书写习惯以及设计方式是实在是在挽救大家的支付生活!


此文为译文,原来的作品地址请点击。
本文通过重构二个废品代码,演讲了怎样写出了不起的代码。开发人士及代码审核人士需依照此标准开发和核对代码。此标准以C#为例,JAVA的童鞋一并参考,C++的童鞋自行脑补吧。

  前言:自从上篇发布之后,我们汇报了成都百货上千标题,因为前篇讲的事物不是很深,或者大家看完现在并未有怎么感觉.本章(前篇,中篇,后篇)的最重要指标其实首先是建议倒霉的宏图,然后相比较的提议贰个针锋相对相比较合理的道岔架构,同时本篇也为继续讲述架构情势和设计情势等的篇章做个铺垫。

 提示:

  •  在本文中本人不会花时间来讲解C#的个性和涉及形式等等(作者也表明不完),网上有广大有关那上面包车型地铁好的申辩的事例。我将聚齐讲述怎样在大家司空见惯工作中选用那一个东西。
  • 事例是一种比较易于的隆起大家要证实的标题标法子,不过仅限于描述的难点——因为笔者意识当笔者在学习怎么着包含着关键代码的例鼠时,作者发以往了然小说的完好构思方面会有不便。
  •  小编不是说自家文中说的情势是无比的化解格局,作者只是能担保这个办法将会是让你的代码变得更加高质量的路径。
  • 小编并不关切上边这一个代码的什么样错误处理,日志记录等等。小编要发布的只是用来消除1般编码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 }

地点那些例子真的是一种12分差的书写情势。你能精通那个类是用来干嘛的么?那些东西是用来做壹些奇怪的运算的么?大家小说就从他起来入手来上课吧…

前天作者来告诉你,刚刚这些类是用来当消费者在网上买东西的时候为她们计算对应折扣的折扣计算和管制的类。

-难以置信吧!

-但是这是真的!

那种写法真的是将难以阅读、难以保险和麻烦扩张那三种集合在一块儿了,而且装有着太差的书写习惯和错误的形式。

除开还有任何什么问题么?

1.取名格局-从源代码中大家能够连蒙带猜预计出来那么些总结方法和出口结果是怎么。而且大家想要从那些类中领到总结算法将会是壹件12分不便的事情。

如此带来的损伤是:

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

亚洲必赢官网 3

 

若是大家需求满意客户的小买卖咨询,要像她们来得算法细节,可能我们必要修改那段代码,这将消费大家不长的岁月去领略大家的乘除方式的逻辑。固然大家不记录她或重构代码,下次咱们/别的开发职员再看那段代码的时候,照旧需求开支壹样的时日来研讨那几个代码是干嘛的。而且在修改的同时还简单出错,导致原先的猜测全体失误。

 2.魔法数字

 亚洲必赢官网 4

在这么些例子中type是变量,你能猜到它代表着客户账户的等级么?If-else
if
言辞是用来兑现怎么样抉择总结出产品价格折扣的点子。

现今大家不知晓哪些的账户是一,2,3或四。今后设想一下,当你只可以为了那多少个有价值的VIP客户改变她们的折扣总结方法的时候,你试着从这几个代码中找出修改的点子—那一个历程大概会费用你不长的时光不说,还很有希望犯错以至于修改这多少个基础的壹般的客户的账户,终究像2要么叁那一个词语毫无描述性的。然则在我们犯错今后,那一个一般的客户却很欣喜,因为他俩获取了VIP客户的折扣。:)

3.不曾强烈的bug

因为大家的代码品质很差,而且可读性相当差,所以我们可能随便就忽略掉很多非凡重大的政工。想象一下,今后黑马在系统中加进一种新的客户类型-金卡用户,而在大家的系统中其余一种新的账户类型最终获得的价钱将是0元。为啥吧?因为在大家的if-else
if
语句中从未别的动静是知足新的情事的,所以要是是未处理过的账户类型,最终重回值都将变成0。1旦大家的老板娘发现那件事,他将会雷霆大发-毕竟她已经免费卖给那样用户很多浩大东西了!

亚洲必赢官网 5

4.从未有过可读性

大家无法不承认上边那段代码的可读性是真的不得了。

她让大家费用了太多的时日去明白那段代码,同时代码隐藏不当的概率太大了,而那正是从未有过可读性的最根本的概念。

 5.魔法数字(再度)

您从代码中能知道好像0.1,0.七,0.5这一个数字的趣味么?好的,小编承认自身不晓得。唯有大家通力合作编写那几个代码大家才清楚这是哪些看头,别人是心有余而力不足知晓的。

你尝试想想要是让您改改上面那句代码,你会什么:

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

因为这些方法完全不可读,所以你改改的历程中不得不尝试着把第3个0.5改成0.4而保持第二个0.5不懂。那说不定会是七个bug,不过却是最佳的最合适的修章。因为那几个0.伍怎么样都未曾告知咱们。

如出一辙的事也存在将years变量转换来disc变量的变换进程中:

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

那是用来总计折扣率的,会经过账户在大家系统的光阴的百分比来获取。好的,那么未来难点来了,要是时光刚刚好就是五吗?

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

虽说第三当即的时候不易于看出来,但是仔细钻探一下就会发现:大家的代码里有为数不少再度的地点。例如:disc *
(amount – (0.1m * amount));

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

在那八个算术中,唯一的界别就只是叁个静态参数,而大家全然能够用1个可变的参数来代替。

假诺大家不试着在写代码的时候从直接ctri+c,ctrl+v中解脱出来,那大家将赶上的标题正是大家只好修改代码中的部分功用,因为我们不领悟某个许地点必要修改。上边的逻辑是计算出在我们系统中各样客户对应年限获得的折扣,所以若是大家只是贸然修改两到③处,很简单导致任哪儿方的前后不均等。

7.各个类具有太多的复杂性的义务区域

作者们写的类至少背负了四个职务:

  1. 慎选总结的运算法则
  2. 为各样不一致景色的账户总括折扣率
  3. 根据每一个客人的时间限制总括出相应的折扣率

以此背离了单纯性义务原则。那么那会带来如何风险吗?若是我们想要改变上诉三个特点中的五个,那就象征恐怕会碰触到有的别样的大家并不想修改的风味。所以在改动的时候我们只好重新测试全部的类,那么那就导致了很重的年月的荒废。

一段垃圾代码

在大家实际的制品中有这么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;
  }
}

那是1段一无可取的代码(2胡:要是你没以为那段代码很不好,那您眼下状态只怕很不好了),大家不太领会那么些类的目标是什么。它或者是一个网上商城的折扣管理类,负责为客户总括折扣。

这些类完全具备了不足读、不可维护、不可扩充的性状,它应用了累累坏的实践和反形式的安排。

上面我们日益分析那里毕竟有个别许难点?

  • 命名难题 –
    大家只好通过猜度那一个类到底是为着计算什么。那其实是在浪费时间。
    设若大家并未相关文书档案只怕重构那段代码,那我们下二次恐怕须要花多量的岁月才能明了那段代码的切实可行意思。

  • 魔数 –
    在这么些事例中,你能猜到变量type是指客户账户的气象吧。通过if-else来选取总计打折后的产品价格。
    现行,大家压根不掌握账户状态壹,2,3,五分头是怎么着意思。
    除此以外,你明白0.一,0.七,0.5都是何等看头呢?
    让大家想象一下,若是你要修改下边那行代码:
    result = (amount - (0.5m * amount)) - disc * (amount - (0.5m * amount));

  • 躲藏的BUG –
    因为代码十二分倒霉,大家可能会失去十二分重大的工作。试想一下,假若大家的连串中新增了一类账户状态,而新的账户等级不满足任何七个if-else条件。那时,再次来到值会固定为0。

  • 不得读 –
    大家只好承认,那是1段不可读的代码。不可读=更加多的知道时间+增添发生错误的风险

  • DPAJEROY – 不要产生重复的代码
    我们或者还是不可能壹眼就找出它们,但它们确实存在。
    例如:disc *(amount - (0.1m * amount));
    一致的逻辑:
    disc *(amount - (0.5m * amount));
    此间只有三个数值分化。假设大家无能为力解脱再度的代码,我们会蒙受重重难题。比如某段代码在五个地方有再一次,当大家要求修改那壹部分逻辑时,你很只怕只修改到了2至三处。

  • 纯净职责规范
    这一个类至少存有三个职分:
    壹 选用总括算法
    二 依据账户状态计算折扣
    叁 依据账户网龄总括折扣
    它违反了10足义务规范。那会推动怎么着风险?借使大家就要修改第四个功能的话,会影响到别的第1个职能。那就代表,我们每一遍修改都会改变我们本不想修改的代码。因而,大家只好对整个类进行测试,那实质上很浪费时间。

简介

那篇小说的指标是显得什么将1段垃圾代码重构成二个绝望的、可扩展性和可保险的代码。笔者将解释什么通过一流实践和越来越好的设计格局来改写它。

阅读本文你须要有以下基础:

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

此文中的例子源于实际项目,那里不会有何使用装饰方式营造的披萨,也不会动用政策方式的计算器,这个事例是可怜好的辨证,但是它们很难被在实际上项目中动用。

本篇的议题如下:

一. style=”font-family: 大篆; color: red”>注脚示例须求

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

三. style=”font-family: 大篆; color: red”>服务层设计

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

伍. style=”font-family: 草书; color: red”>展现层设计

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

那就从头重构吧…

在接下去的捌个步骤中自作者将向你显得大家怎么防止上诉问题来营造多个到底的易维护,同时又便利单元测试的看起来一目明白的代码。

 

重构

透过以下九布,作者会告诉你们怎么防止上述危机并促成三个到底的、可爱惜的、可测试的代码。

  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,二,三到底是何许看头。

  1. 魔数
    在C#中制止魔数大家1般选用枚举来替换它们。那里运用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
    来拉长代码可读性。
    并且,作者还将或多或少长度不短的语句才分成两行。比如:**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. 搞定魔数的另1种艺术
    运用静态常量来替换魔数。0.一m,0.2m,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措施开始展览单元测试是相比较艰难的,撤销我们对普赖斯Extensions扩大类也展开测试。
为了防止那一个标题,大家创设了DefaultLoyaltyDiscountCalculator类来替换ApplyDiscountForTimeOfHavingAccount那么些扩大方法,此类还完毕了ILoyaltyDiscountCalculator接口。今后,当大家要测试DiscountManager类时,我们只构造函数注入ILoyaltyDiscountCalculator接口的兑现即可。这里运用了依靠注入。
通过那样做,大家将网龄折扣的算法迁移到接近DefaultLoyaltyDiscountCalculator的区别类中,那样当大家修改某3个算法不会覆盖到别的工作。
对此依据账户状态来计量折扣的事情,大家供给在DiscountManager中剔除七个职务:

  • 据说账户状态选择总计的算法

  • 福寿绵绵总结算法
    此地大家通过DefaultAccountDiscountCalculatorFactory工厂类来解决这么些难点,DefaultAccountDiscountCalculatorFactory工厂类完结了IAccountDiscountCalculatorFactory接口。
    大家的工厂将控制取舍哪二个倒扣算法。接着,工厂类被通过构造函数注入到DiscountManager类中。
    上面作者只需求在DiscountManager 中动用工厂:
    priceAfterDiscount = _factory.GetAccountDiscountCalculator(accountStatus).ApplyDiscount(price);
    上述,大家缓解了第3个难点,上面大家要求贯彻总括算法。依照账户状态,提供区别的算法,那恰恰契合政策情势。我们供给塑造四个政策:NotRegisteredDiscountCalculator,SimpleCustomerDiscountCalculator,MostValuableCustomerDiscountCalculator早已环堵萧然出来的接口IAccountDiscountCalculator
    好了,未来大家有可1段干净可读的代码了,那段代码中持有的类都只有二个职务:

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

那是1段一塌糊涂的代码(二胡:借使您没觉着那段代码很不佳,那你日前情形可能很倒霉了),我们不太通晓那些类的指标是怎样。它大概是三个网上商城的折扣管理类,负责为客户计算折扣。

本条类完全具备了不足读、不可维护、不可扩张的特点,它选用了重重坏的履行和反方式的规划。

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

  • 命名难题 –
    大家不得不通过猜想这几个类到底是为了总结什么。那实际上是在浪费时间。
    假设大家一贯不有关文书档案或许重构那段代码,这我们下贰次或然须求花大量的时光才能分晓那段代码的求实意思。

  • 魔数 –
    在这一个例子中,你能猜到变量type是指客户账户的情形呢。通过if-else来选择总计优惠后的产品价格。
    现在,大家压根不精晓账户状态①,二,三,五分别是何许看头。
    其它,你领悟0.一,0.柒,0.伍都以什么意思呢?
    让大家想像一下,借使您要修改上面那行代码:
    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个数值不一致等。尽管大家无能为力摆脱再度的代码,大家会遇上很多题材。比如某段代码在四个地点有双重,当我们要求修改那部分逻辑时,你十分的大概只修改到了二至3处。

  • 单纯职分规范 那一个类至少存有四个任务: 一 选拔总括算法 二依照账户状态总计折扣 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 }

虽说那样,大家照旧不知晓一,二,三,肆意味着什么,这就此起彼伏往下啊!

总结

本文通过不难易懂的章程重构了一段难题代码,它显得了怎么在骨子里意况中运用最好实践和设计情势来援救我们写出干净的代码。
就自作者的劳作经历来说,本文中冒出的2流做法是隔三差伍发出的。编写那种代码的人一而再认为他们力所能及维持这种规则,但不幸的是系统和作业往往都会尤其复杂,每回修改那类代码时都会带来巨大的风险。

重构

因此以下玖布,我会告诉你们怎么着幸免上述风险并贯彻一个根本的、可珍爱的、可测试的代码。

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

    可是我们任然不知底账户状态一,二,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. 破除魔数的另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;
    }
    }
    
  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;
    }
    }
    

    玖.最后,获得根本的代码
    最后,让我们经过引进正视注入和工厂方法情势来博取终极的本子吧。
    先来看卡最后结出:

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

    第3,大家摆脱了扩张方法(静态类),假若大家想对ApplyDiscount主意实行单元测试是比较艰难的,撤消大家对PriceExtensions扩大类也开始展览测试。
    为了防止这一个题目,大家创设了DefaultLoyaltyDiscountCalculator 类来替换ApplyDiscountForTimeOfHavingAccount那一个扩充方法,此类还完毕了ILoyaltyDiscountCalculator接口。未来,当大家要测试DiscountManager类时,大家只构造函数注入ILoyaltyDiscountCalculator接口的完毕即可。那里运用了注重注入。
    通过如此做,大家将网龄折扣的算法迁移到类似DefaultLoyaltyDiscountCalculator 的差异类中,这样当大家修改某2个算法不会覆盖到任何事情。
    对于依照账户状态来测算折扣的事务,大家须要在DiscountManager中去除多个职分:

  • 根据账户状态选择计算的算法
  • 落实计算算法
    那里大家经过DefaultAccountDiscountCalculatorFactory工厂类来消除那么些题材,DefaultAccountDiscountCalculatorFactory工厂类实现了IAccountDiscountCalculatorFactory接口。
    大家的厂子将控制取舍哪贰个折扣算法。接着,工厂类被通过构造函数注入到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;
    }
    }
    

  一. 注脚示例需要

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 }

总结

正文通过简单易懂的不2诀窍重构了一段难题代码,它展现了什么在事实上境况中选择最好实践和设计格局来援救大家写出到底的代码。
就本身的做事经验来说,本文中现身的二流做法是平日发出的。编写那种代码的人总是觉得他们能够维持那种规则,但不幸的是系统和事务往往都会尤其复杂,每便修改那类代码时都会拉动巨大的高风险。

  本篇依旧用事先的电子商务网址中的贰个简单的现象来讲述:在页面上急需出示产品的列表消息。并且依照产品的品类差异,总计出相应的折扣。 

III:越来越多的可读性

在这一步中我们将通过将if-else
if
 语句改为switch-case 语句,来增Gavin章的可读性。

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

例如: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

您是或不是会想,那些会不会是付出的两样,应该不会被交付到不当提醒中去?不,他会的!

当大家的艺术通过取得账户状态作为参数的时候,我们并不想程序让我们不得预言的样子发展,造成不可预测的失误。

 那种情景是纯属不允许现身的,所以大家无法不经过抛出尤其来预防那种气象。

上边包车型地铁代码就是通过抛出尤其后修改的以幸免出现不满足条件的场馆-修章是将抛出拾分幸免 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.3m,0.五m

这几个数字其实也是一种档次的魔法数-他们也从不直接告知我们他们代表着怎么着。

咱俩也有同一的意况,比如将“有账户的年华”折价为“忠诚折扣”。

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

数字伍让大家的代码变得神秘了起来。

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

本人会用别的一种办法来制止魔法数的表述的现身-约等于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 }

作者梦想您也确认自个儿这些方式会愈来愈使代码自己变得更具有表明性:)

兴许有个别朋友认为从Smart UI立时跳到那种分层设计,仿佛快了些。其实也好不不难三个研讨的跃进吧。上面就来看看那种分层是何许消除在此以前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:移除那个多余的代码

在写代码的时候条件上是我们的代码越是精简越好。精简的代码的意味,越少的谬误的可能性,在翻阅精晓代码逻辑的时候开销的时间越少。

由此未来开班精简大家的代码吧。

咱俩得以随意发现大家两种客户账户下拥有相同的章程:

.ApplyDiscountForTimeOfHavingAccount(timeOfHavingAccountInYears);

大家可不得以只写二次啊?大家在此之前将未注册的用户放在了抛出尤在那之中,因为我们的折扣率只会一个钱打二15个结注册用户的限期,并从未给未注册用户留有作用设定。所以,我们应当给未注册用户设定的光阴为多少呢?
-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 }

笔者们还是能将那1行移除到switch-case语句外面。好处正是:更少的代码量!

  2.  业务层设计

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 }

第2大家摆脱了扩张方法(也便是静态类),之所以要脱身那种是因为扩大方法与折扣总括情势之间存在了紧耦合的关联。假使大家想要单元测试我们的艺术ApplyDiscount的时候将变得不太不难,因为大家不可能不统1测试与之牢牢关联的类PriceExtensions。

为了制止那个,笔者成立了DefaultLoyaltyDiscountCalculator 类,那中间含有了ApplyDiscountForTimeOfHavingAccount推而广之方法,同事自个儿通过架空中接力口ILoyaltyDiscountCalculator躲藏了她的实际完成。以后,当自家想测试我们的类DiscountManager的时候,作者就足以由此 ILoyaltyDiscountCalculator依样画葫芦注入虚构对象到DiscountManager类中通过构造函数字彰显示测试效用。那里大家利用的就叫依赖注入情势。

亚洲必赢官网 10

在做这一个的还要,咱们也将总结折扣率那个作用安全的移交到另1个不1的类中,如若大家想要修改那一段的逻辑,那我们就只须要修改DefaultLoyaltyDiscountCalculator** **类就好了,而不供给转移别的的地点,那样收缩了在变更他的时候爆发破坏别的地方的危害,同时也不必要再追加单独测试的时刻了。

上边是大家在DiscountManager类中选择分其他逻辑类:

priceAfterDiscount =
_loyaltyDiscountCalculator.ApplyDiscount(priceAfterDiscount,
timeOfHavingAccountInYears);

为了针对账户状态的逻辑来计量折扣率,笔者创造了一些相比复杂的事物。我们在DiscountManager类中有五个义务要求表明出去。

  1. 依照账户状态怎么着抉择相应的一个钱打二十五个结格局。
  2. 越发计算办法的细节

为了将首先个任务移交出去,小编创制了工厂类(DefaultAccountDiscountCalculatorFactory),为了落实工厂方式,然后再把那几个隐形到虚幻IAccountDiscountCalculatorFactory里面去。

亚洲必赢官网 11

大家的厂子会操纵选拔哪个种类总括格局。最终大家由此信赖注册形式构造函数将工厂形式注射到DiscountManager类中

下边就是利用了工厂的DiscountManager类:

亚洲必赢官网,priceAfterDiscount =
_factory.GetAccountDiscountCalculator(accountStatus).ApplyDiscount(price);

 以上会针对差异的账户状态再次来到几时的策略,然后调用ApplyDiscount 方法。

首先个权利已经被连接出去了,接下去正是首个了。

 接下来大家就伊始商量策略了…..

亚洲必赢官网 12

因为不一样的账户状态会有永不的折扣总括格局,所以我们要求分裂的兑现政策。座椅万分适用于政策形式。

在大家的事例中,大家有三种政策:

NotRegisteredDiscountCalculator SimpleCustomerDiscountCalculator MostValuableCustomerDiscountCalculator**

她俩带有了切实的折扣总括办法的完结并被藏在了抽象IAccountDiscountCalculator里。

那就允许大家的类DiscountManager使用分外的策略,而不必要知道具体的贯彻。我们的类只需求理解与ApplyDiscount方法相关的IAccountDiscountCalculator 接口再次回到的目的的门类。

NotRegisteredDiscountCalculator, SimpleCustomerDiscountCalculator,
MostValuableCustomerDiscountCalculator
这么些类富含了切实的通过账户状态采用适合总结的估计划办公室法的落成。因为我们的这些政策看起来相似,大家唯1能做的基本上就只有针对那二种总计策略创建1个艺术然后每一个策略类通过四个不要的参数来调用她。因为那会让我们的代码变得更其多,所以笔者明天控制不这么做了。

好了,到近年来截至大家的代码变得可读了,而且每一种类都只有3个权力和权利了-那样修改他的时候会独自一1对应了:

  1. DiscountManager-管理代码流
  2. DefaultLoyaltyDiscountCalculator-可信赖的一个钱打二十七个结折扣率的不2秘籍
  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 }

  记得在事先的Smart UI的例证中,程序的政工逻辑是一贯写在了ASPX页前边面的cs代码中的。现在,接纳分段的点子,大家选用了世界模型来企业来电子商务中的业务逻辑。

总结

在本文中,代码被Infiniti简化了,使得全数的技艺和格局的诠释更易于了。它展现了怎么消除周围的编制程序难题,以及利用优质的执行和设计形式以方便、干净的办法缓解这几个题材的补益。

在自己的劳作经验中,作者反复在这篇小说中强调了不佳的做法。它们显然存在于广大使用场馆,而不是在一个类中,如在自家的例证中那样,那使得发现它们进一步不方便,因为它们隐藏在适用的代码之间。写那种代码的人三番五次争持说,他们遵照的是简简单单愚拙的条条框框。不幸的是,差不多拥有的系统都在成长,变得卓殊复杂。然后,这一个大约的、不可扩张的代码中的每二个修改都以十三分首要的,并且带来了大侠的高风险。

请牢记,您的代码将长时间存在于生产环境中,并将在每个事情须求变动上海展览中心开修改。由此编写过于简单、不可扩充的代码十分的快就会发生严重的后果。最后一点是对开发职员有利,特别是这几个在您本人随后维护您的代码。

万壹你有局地标题依据小说不要犹豫联系小编!

  有关领域模型的一部分东西,咱们在继续的稿子中会讲解的。

亚洲必赢官网 13PS:

遵照文章敲的源代码:

链接: 密码:9spz

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

 

  上边包车型地铁类图就突显了我们前边的电子商务的急需中所用到的事情模型。

亚洲必赢官网 14

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

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

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

  

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

 

 

 

  那个接口就用来贯彻分裂减价的策略,那是策略形式的一种选择。这一个形式允许大家在运转的时候改变差异的算法完成。在本例子中,Price类将会遵照分歧的产品来兑现区别的降价策略。在我们在此以前的不行斯马特UI例子中,其实这些降价的算法我们早已写了,然则并未有分离出来,导致了历次加2个降价的算法的政策,程序就须求变更,重新编写翻译,安插。约等于说优惠的某个是个变化点,我们理应分离出来的。 

注:策略格局:用二个类来封装二个算法的达成,并且通过切换算法的兑现允许在运转时修改1个指标的行事。

 

在电子商务中,不是每个商品都会降价的,其实大家要贯彻的降价策略唯有1种。不过1旦这么,大家在写代码的时候将要if-else判断是或不是是降价的货品,其实那里照旧暴光了变化点的:假使国庆那天,所有的商品都优惠了,那么大家就得修改代码。其实大家能够如此思量:不降价的场合也总算一种优惠,其余的货物减价也许是柒折,不促销的动静正是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;
        }
}

 

 

下边大家来看望Price类的兑现。

 

亚洲必赢官网 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类在统一筹划中便是用了“重视倒置原则”,因为它从未利用某三个切实的优惠完毕算法,而且正视于接口抽象,至于之后毕竟会哪个种类的减价算法,其实是由商品的品类来控制的。 

  我们依然继续的看,未来探视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
 }

 

 

  我们将会把挑选哪个种类减价的策略的逻辑写在三个独立的地点,也正是说:只要客户代码传入相应的参数音讯,大家就自行的创立二个方便的优惠策略对象。很显明,那里能够行使工厂方法来兑现,如下:  

 

亚洲必赢官网 19亚洲必赢官网 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();
 }

 

 

  假诺后天有广大的货品,大家想精晓他们的折扣价格,最简便的点子正是遍历他们,判断项目,然后选拔差异的降价策略。为了特别的可读,大家得以为货物列表建立扩大方法,如下:

  

亚洲必赢官网 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);
            }
      }
 }

 

 

  为了简化客户代码的调用工作,大家提供叁个看似门户(gateway),可能是Façade的定义:把复杂的操作逻辑隐藏,留给客户代码一个简约易用的API。大家那边开创1个瑟维斯类,如下:

  

亚洲必赢官网 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:在客户端和劳动使用请求/响应的音信机制进行通讯的。我们那边的客户代码和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结构不一的,而瑟维斯重临的又是ViewModel的响应,那么就必要把获得到的Product转换为View Model的结构。能够把转换的代码写在一个一定的地方(能够认为是个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;
        }
    }

 

 

  最终,我们投入1个ProductService来与业务层的瑟维斯 类举办互相,业务层的Service会再次来到商品列表,然后我们明天添加的那些ProductService会把列表转为ProductViewModels。

 

世家恐怕觉得意外:为啥那边添加了多少个ProductService,以前在事情层加多少个,未来又加3个,是或不是命名有标题依旧作用重新?其实在上一篇已经提过:有时在业务层类添加多少个service层,主假使用来协会业务流程的,日常要多少个业务类组合在1块儿行使,那样重大是为着简化客户程序(也正是调用这几个业务层的代码)的调用,实现类似Façade的功效。

 

大家今日加上的ProductService就是事务层中**service层**的客户程序,因为大家调用了业务层的service,往往有时,我们不想把温馨系统的业务类的布局一直揭发给外界,如呈现层,而且也冀望提供越来越切合展现层所需的数据结构,那么我们就添加了这一个Product瑟维斯,提供从事情类到ViewModel的转移。而且在这几个ProductSevice中,我们也得以完结部分分外处理机制,要是波及到了分布式调用,那么我们还是可以用这几个ProductService类向突显层和UI那边隐藏分布式的新闻:完成代理方式。

后日就写在到那边,在写的进度中窥见那篇有点长了,所以分为叁篇(前,中,后)揭橥!不明了的地点大家多钻探一下,也得以告诉自身!下篇后天透露!见谅!

 

网站地图xml地图