威尼斯人线上娱乐

自己的代码,怎么着将坏的代码重新编写翻译为好的代码

10 4月 , 2019  

友好的题词表达:

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

本体系还有别的小说,后续将稳步翻译。

 

此文为译文,原来的小说地址请点击。
本文通过重构一个垃圾堆代码,阐述了哪些写出了不起的代码。开发人士及代码审核人士需遵循此标准开发和审查批准代码。此规范以C#为例,JAVA的童鞋一并参考,C++的童鞋自行脑补吧。

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

引言:

自身的名字叫Radoslaw
Sadowski,作者昨天是2个微软技术开发职员。笔者从伊始工作时就一贯接触的微软技术.

在干活一年后,笔者见状的品质很差的代码的数码基本上都得以写成一整本书了。

这么些经验让自家变成了一个想要清洁代码的自闭症伤者。

写那篇文章的目标是为了通过显示质量很差的类的例证来表明什么下笔出到底的、可延伸的和可保险的代码。笔者会通过好的书写情势和设计情势来表达坏的代码带来的难题,以及替换他的好的消除方法。

第二局部是针对性这一个拥有C#基础知识的开发职员——小编会议及展览示壹些科学普及的谬误,然后再突显1些让代码变得可读性的章程与技能。高级部分至关心珍视要针对那多少个至少存有设计方式概念的开发人士——作者将会议及展览示完全绝望的、单元可测试的代码。

为了能够清楚这篇文章你需求至少明白以下三个部分的基本知识:

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

下边这么些事例真的是一种13分差的书写格局。你能分晓那么些类是用来干嘛的么?那个事物是用来做1些出人意料的运算的么?大家小说就从他起来出手来讲学吧…

后东瀛身来报告您,刚刚那3个类是用来当顾客在网上买东西的时候为她们计算对应折扣的折扣总计和管理的类。

-难以置信吧!

-不过这是真的!

那种写法真的是将难以阅读、难以维护和难以扩张那三种集合在共同了,而且拥有着太差的书写习惯和不当的格局。

除却还有任何什么难点么?

1.取名格局-从源代码中大家得以连蒙带猜推断出来那些总括办法和输出结果是何许。而且大家想要从这一个类中领到总计算法将会是一件万分狼狈的思想政治工作。

如此那般推动的侵蚀是:

最惨重的难题是:浪费时间,

威尼斯人线上娱乐 3

 

若是大家必要满足客户的商业咨询,要像他们来得算法细节,只怕大家要求修改那段代码,那将开销大家不短的时光去领略大家的总计方法的逻辑。固然大家不记录她或重构代码,下次我们/别的开发人士再看那段代码的时候,依旧供给开支相同的岁月来钻探那些代码是干嘛的。而且在改动的还要还易于失误,导致原本的一个钱打二十五个结全部失误。

 2.魔法数字

 威尼斯人线上娱乐 4

在这些事例中type是变量,你能猜到它代表着客户账户的等级么?If-else
if
言辞是用来兑现如何挑选计算出产品价格折扣的格局。

现行反革命大家不明白怎样的账户是1,二,3或四。未来想象一下,当您只可以为了那几个有价值的VIP客户改变他们的折扣计算情势的时候,你试着从那个代码中找出修改的方法—这些过程恐怕会费用你十分长的时日不说,还很有极大希望犯错以至于修改那个基础的形似的客户的账户,毕竟像二依旧叁这么些词语毫无描述性的。不过在我们犯错现在,那个一般的客户却很欢跃,因为他们取得了VIP客户的折扣。:)

3.从没通晓的bug

因为大家的代码品质很差,而且可读性卓殊差,所以大家可能无限制就忽略掉很多那二个关键的事务。想象一下,未来突然在系统中追加一种新的客户类型-金卡用户,而在大家的系统中任何壹种新的账户类型最终收获的价位将是0元。为啥呢?因为在大家的if-else
if
语句中未有此外动静是满足新的情景的,所以即使是未处理过的账户类型,最后重返值都将变成0。一旦大家的业主发现这件事,他将会怒发冲冠-终究她早就免费卖给这么用户很多众多事物了!

威尼斯人线上娱乐 5

4.从不可读性

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

他让我们费用了太多的时间去领略那段代码,同时期码隐藏不当的可能率太大了,而那正是未有可读性的最主要的定义。

 5.魔法数字(再一次)

您从代码中能知道好像0.壹,0.柒,0.5那一个数字的情致么?好的,小编肯定自个儿不知晓。唯有我们温馨编排那几个代码我们才掌握那是什么看头,别人是力不从心清楚的。

你尝试想想假如让您改改上面那句代码,你会如何:

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

因为那一个主意完全不行读,所以您改改的进度中不得不尝试着把第3个0.5改成0.四而保持第壹个0.5不懂。那只怕会是多少个bug,然而却是最棒的最合适的改动章程。因为这几个0.五怎么都未曾告诉我们。

无差别于的事也存在将years变量转换成disc变量的转移进度中:

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

那是用来总结折扣率的,会由此账户在我们系统的时光的百分比来获取。好的,那么今后难点来了,借使时光正好好正是5吧?

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

就算如此第一立即的时候不易于看出来,可是多加商量一下就会意识:大家的代码里有不少再度的地方。例如:disc *
(amount – (0.1m *自己的代码,怎么着将坏的代码重新编写翻译为好的代码。 amount));

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

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

一经大家不试着在写代码的时候从第二手ctri+c,ctrl+v中解脱出来,那我们将赶上的题材正是大家只可以修改代码中的部分功用,因为大家不晓得有多少地点必要修改。上边的逻辑是总结出在我们系统中各类客户对应年限获得的折扣,所以一旦大家只是贸然修改两到叁处,很容易造成任哪个地方方的上下分化。

7.各个类具有太多的纷纭的权力和义务区域

笔者们写的类至少背负了多少个职务:

  1. 选拔计算的运算法则
  2. 为每一个不一致景观的账户总括折扣率
  3. 依照种种客人的期限计算出相应的折扣率

本条背离了单一权利原则。那么那会拉动怎么着加害吗?假设大家想要改变上诉二个特色中的八个,那就代表或然会碰触到一些任何的大家并不想修改的特点。所以在修改的时候大家不得不重新测试全部的类,那么那就导致了很重的时刻的浪费。

壹段垃圾代码

在我们实事求是的产品中有那般2个类:

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来挑选总计降价后的产品价格。
    现今,我们压根不知道账户状态壹,二,叁,四独家是何许看头。
    其它,你知道0.一,0.七,0.5都以什么样意思啊?
    让大家想像一下,借使您要修改上边那行代码:
    result = (amount - (0.5m * amount)) - disc * (amount - (0.5m * amount));

  • 隐蔽的BUG –
    因为代码分外倒霉,大家或然会错过分外重大的工作。试想一下,假诺大家的类别中新增了1类账户状态,而新的账户等级不满意任何叁个if-else条件。那时,再次来到值会固定为0。

  • 不得读 –
    大家只可以承认,这是壹段不可读的代码。不可读=越多的知道时间+扩充发生错误的风险

  • D奥迪Q5Y – 不要爆发重复的代码
    我们大概不能够1眼就找出它们,但它们确实存在。
    例如:disc *(amount - (0.1m * amount));
    同一的逻辑:
    disc *(amount - (0.5m * amount));
    那里只有二个数值分裂等。即便我们不恐怕脱身再一次的代码,大家会遇见不少标题。比如某段代码在四个地点有双重,当大家必要修改那有个别逻辑时,你很或许只修改到了贰至叁处。

  • 单纯职务规范
    以此类至少存有多个任务:
    1 选用总括算法
    贰 依照账户状态计算折扣
    3 根据账户网龄总结折扣
    它违反了纯粹任务规范。那会带来如何风险?假诺咱们即将修改第多个功效的话,会影响到其余第1个作用。那就象征,大家每一遍修改都会改变我们本不想修改的代码。由此,大家只好对全部类实行测试,那实则很浪费时间。

简介

那篇小说的目标是显得怎样将一段垃圾代码重构成三个干净的、可增加性和可爱惜的代码。小编将解释什么通过一流实践和更好的设计形式来改写它。

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

  • c# 基础
  • 凭借注入,工厂方式,策略情势

此文中的例子源于实际项目,那里不会有哪些使用装饰格局塑造的披萨,也不会选拔政策方式的总括器,这么些事例是十分好的求证,不过它们很难被在实际项目中采纳。

本篇的议题如下:

一. style=”font-family: 大篆; color: red”>表明示例需要

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

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

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

⑤. style=”font-family: 小篆; color: red”>呈现层设计

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

那就起来重构吧…

在接下去的8个步骤中本身将向您来得大家怎样幸免上诉难点来营造三个到底的易维护,同时又有益于单元测试的看起来一目了解的代码。

 

重构

由此以下九布,小编会告诉你们怎么着制止上述危机并贯彻二个到底的、可珍视的、可测试的代码。

  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.1m,0.二m,0.三m,小编m,大家并不知道它们是怎么看头。
    此外decimal discountForLoyaltyInPercentage =
    (timeOfHavingAccountInYears > 5) ? (decimal)5/100 :
    (decimal)timeOfHavingAccountInYears/100;
    中,数字5也丰硕神秘。
    大家必须让它们更有着描述性,那时使用常量会是二个比较好的措施。
    我们来定义1个静态类:

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

随之修改DiscountManager类:

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

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

透过措施名称,大家就足以通晓它的职分是何许,未来修改大家的例子:

    public class DiscountManager
    {
    public decimal ApplyDiscount(decimal price, AccountStatus accountStatus, int timeOfHavingAccountInYears)
    {
        decimal priceAfterDiscount = 0;
        switch (accountStatus)
        {
            case AccountStatus.NotRegistered:
                priceAfterDiscount = price;
                break;
            case AccountStatus.SimpleCustomer:
                priceAfterDiscount = price.ApplyDiscountForAccountStatus(Constants.DISCOUNT_FOR_SIMPLE_CUSTOMERS)
                  .ApplyDiscountForTimeOfHavingAccount(timeOfHavingAccountInYears);
                break;
            case AccountStatus.ValuableCustomer:
                priceAfterDiscount = price.ApplyDiscountForAccountStatus(Constants.DISCOUNT_FOR_VALUABLE_CUSTOMERS)
                  .ApplyDiscountForTimeOfHavingAccount(timeOfHavingAccountInYears);
                break;
            case AccountStatus.MostValuableCustomer:
                priceAfterDiscount = price.ApplyDiscountForAccountStatus(Constants.DISCOUNT_FOR_MOST_VALUABLE_CUSTOMERS)
                  .ApplyDiscountForTimeOfHavingAccount(timeOfHavingAccountInYears);
                break;
            default:
                throw new NotImplementedException();
        }
        return priceAfterDiscount;
    }
    }
  1. 去除没用的代码
    我们应该写出大致的代码,因为简短的代码=减少BUG发生的机率,并且也让我们裁减通晓事情逻辑的流年。
    笔者们发现,那里两种情况的客户调用了同样的方法:
    .ApplyDiscountForTimeOfHavingAccount(timeOfHavingAccountInYears);
    此间可以统一代码:

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

九.末段,获得彻底的代码
最后,让我们由此引进信赖注入和工厂方法方式来博取最后的版本吧。
先来看卡最后结果:

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

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

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

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

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

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

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

        return calculator;
        }
    }

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

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

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

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

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

首先,大家摆脱了扩展方法(静态类),假若我们想对ApplyDiscount措施开始展览单元测试是比较艰苦的,撤除我们对PriceExtensions扩张类也进行测试。
为了防止这么些题目,大家创造了DefaultLoyaltyDiscountCalculator类来替换ApplyDiscountForTimeOfHavingAccount那些扩充方法,此类还达成了ILoyaltyDiscountCalculator接口。现在,当我们要测试DiscountManager类时,大家只构造函数注入ILoyaltyDiscountCalculator接口的兑现即可。那里运用了依靠注入。
通过那样做,我们将网龄折扣的算法迁移到接近DefaultLoyaltyDiscountCalculator的分化类中,那样当大家修改某二个算法不会覆盖到其他作业。
对此依照账户状态来计量折扣的业务,我们要求在DiscountManager中删去七个职责:

  • 依照账户状态选拔总计的算法

  • 落实总括算法
    那里大家通过DefaultAccountDiscountCalculatorFactory工厂类来缓解这些难点,DefaultAccountDiscountCalculatorFactory工厂类实现了IAccountDiscountCalculatorFactory接口。
    我们的厂子将控制取舍哪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.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;
    }
    }

一段垃圾代码

在大家实事求是的制品中有那般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.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;
}
}

那是一段一无可取的代码(2胡:如果您没觉得那段代码很倒霉,那您眼下事态可能很不好了),我们不太知道这一个类的指标是何等。它恐怕是叁个网上商城的折扣管理类,负责为客户总括折扣。

那么些类完全具备了不足读、不可维护、不可扩充的特点,它选拔了诸多坏的履行和反方式的规划。

下边大家慢慢分析那里究竟某些许难题?

  • 命名难点 –
    我们只可以通过测度这一个类到底是为着总括什么。那实际上是在浪费时间。
    如若大家平昔不有关文书档案或然重构那段代码,那大家下三次恐怕要求花多量的时光才能知晓那段代码的有血有肉意思。

  • 魔数 –
    在这一个事例中,你能猜到变量type是指客户账户的情状吧。通过if-else来摘取总计减价后的产品价格。
    未来,大家压根不精晓账户状态1,二,三,4各自是怎样意思。
    其它,你精通0.一,0.七,0.5都以什么样看头啊?
    让大家想像一下,假若你要修改下边那行代码:
    result = (amount - (0.5m * amount)) - disc * (amount - (0.5m * amount));

  • 隐藏的BUG –
    因为代码一塌糊涂,大家可能会失掉卓殊关键的思想政治工作。试想一下,借使大家的系统中新增了一类账户状态,而新的账户等级不满意任何1个if-else条件。那时,再次来到值会固定为0。

  • 不行读 –
    大家不得不承认,那是一段不可读的代码。不可读=更加多的了解时间+增添发生错误的高风险

  • D途睿欧Y – 不要发生重复的代码
    大家或者不可能壹眼就找出它们,但它们确实存在。
    例如:disc *(amount - (0.1m * amount)); 同样的逻辑:
    disc *(amount - (0.5m * amount));
    那里唯有一个数值不一致。假如大家鞭长莫及摆脱再度的代码,大家会遭遇很多题材。比如某段代码在多个地点有再次,当大家要求修改那部分逻辑时,你很恐怕只修改到了二至3处。

  • 单纯性职分规范 这些类至少存有两个职责: 一 采取总结算法 2依据账户状态计算折扣 3 依照账户网龄总括折扣
    它违反了10足职分规范。那会带来什么危害?假设大家将要修改第四个功效的话,会潜移默化到其余第1个职能。那就代表,我们每一回修改都会转移大家本不想修改的代码。因而,大家只可以对总体类进行测试,这事实上很浪费时间。

 ** ** **

I:命名,命名,命名

恕作者直言,那是代码中最重大的一步。大家只是修章/参数/变量那个的名字,方今日大家得以直观的询问到上面那几个类代表怎样看头。

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

即便这样,大家依旧不明了一,2,三,四意味着着如何,那就此起彼伏往下啊!

总结

本文通过简单易懂的格局重构了壹段难点代码,它展现了什么样在骨子里情况中接纳最棒实践和设计情势来提携大家写出到底的代码。
就自个儿的干活经验来说,本文中出现的涂鸦做法是平时产生的。编写那种代码的人总是觉得她们能够有限支撑那种规则,但不幸的是系统和作业往往都会尤其复杂,每回修改这类代码时都会拉动巨大的高风险。

重构

透过以下九布,笔者会告诉你们怎么防止上述风险并完毕3个完完全全的、可爱慕的、可测试的代码。

  1. 取名,命名,命名
    这是地道代码的最要害方面之1。大家只须求改变方法,参数和变量的命名。现在,大家能够适度的理解上面包车型地铁类是承受什么的了。

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

    只是大家任然不领会账户状态一,二,三到底是怎样看头。

  2. 魔数
    在C#中幸免魔数大家1般选取枚举来替换它们。那里运用AccountStatus 枚举来替换if-else中的魔数。
    public enum AccountStatus {   NotRegistered = 1,   SimpleCustomer = 2,   ValuableCustomer = 3,   MostValuableCustomer = 4 }
    未来我们来探视重构后的类,大家得以很不难的表露哪二个账户状态应该用怎么样算法来测算折扣。混合账户状态的高风险急速的下落了。

    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.一m,0.2m,0.叁m,小编m,大家并不知道它们是什么看头。
    其它decimal discountForLoyaltyInPercentage =
    (timeOfHavingAccountInYears > 5) ? (decimal)5/100 :
    (decimal)timeOfHavingAccountInYears/100;
    中,数字伍也要命神秘。
    我们亟须让它们更享有描述性,那时使用常量会是1个相比较好的措施。
    大家来定义贰个静态类:

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

    第一,我们摆脱了扩张方法(静态类),假设大家想对ApplyDiscount艺术进行单元测试是比较不方便的,撤销我们对PriceExtensions扩张类也拓展测试。
    为了防止那么些题材,我们创造了DefaultLoyaltyDiscountCalculator 类来替换ApplyDiscountForTimeOfHavingAccount那些扩张方法,此类还完成了ILoyaltyDiscountCalculator接口。今后,当大家要测试DiscountManager类时,大家只构造函数注入ILoyaltyDiscountCalculator接口的兑现即可。那里运用了依靠注入。
    通过那样做,大家将网龄折扣的算法迁移到类似DefaultLoyaltyDiscountCalculator 的差别类中,那样当我们修改某三个算法不会覆盖到其他工作。
    对于依照账户状态来测算折扣的事务,我们需求在DiscountManager中删去五个职分:

  • 基于账户状态接纳总计的算法
  • 兑现总括算法
    那里大家通过DefaultAccountDiscountCalculatorFactory工厂类来化解这么些标题,DefaultAccountDiscountCalculatorFactory工厂类达成了IAccountDiscountCalculatorFactory接口。
    我们的厂子将控制选取哪三个折扣算法。接着,工厂类被通过构造函数注入到DiscountManager类中。
    上边笔者只需求在DiscountManager 中采取工厂:
    priceAfterDiscount = _factory.GetAccountDiscountCalculator(accountStatus).ApplyDiscount(price);
    以上,我们解决了第二个难题,上面大家需求贯彻总结算法。依照账户状态,提供不一样的算法,那恰好符合政策形式。大家须求塑造四个政策: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.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 }

总结

本文通过简单易懂的章程重构了一段难题代码,它显得了哪些在骨子里情状中利用最棒实践和设计格局来提携大家写出干净的代码。
就本人的工作经历来说,本文中冒出的不良做法是时常发出的。编写那种代码的人再三再四觉得他们力所能及保持那种规则,但不幸的是系统和事务往往都会愈来愈复杂,每一趟修改这类代码时都会带动巨大的高风险。

  本篇依然用事先的电子商务网址中的二个简单的情景来讲述:在页面上急需出示产品的列表消息。并且依照产品的品种差别,计算出相应的折扣。 

III:更多的可读性

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

并且,笔者也将二个十分长的总计形式拆分为两句话来写。以后大家将“
通过账户状态来计算折扣率”与“通过账户定期来计量折扣率”那两者分别来测算。

例如: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.三m,0.5m

这几个数字其实也是一种档次的魔法数-他们也绝非直接告知大家他们代表着如何。

我们也有一致的动静,比如将“有账户的小时”折价为“忠诚折扣”。

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

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

大家必须做些什么让那么些变得更具表现性。

本人会用其余1种方法来幸免魔法数的抒发的产出-也正是C#中的常量(关键词是const),作者强烈提出在大家的应用程序中等专业高校门定义3个静态类来囤积那一个常量。

在大家的例子中,笔者是开创了上边包车型地铁类:

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

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

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

自个儿希望你也肯定自个儿这一个点子会更为使代码自身变得更有着表明性:)

恐怕部分朋友认为从斯马特 UI立时跳到那种分层设计,就像快了些。其实也终归贰个思考的跃进吧。上面就来探视那种分层是如何化解从前斯马特UI的题材的。 

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

我们行还是不行只写三次啊?我们前边将未注册的用户放在了抛出十分中,因为大家的折扣率只会盘算注册用户的为期,并未给未注册用户留有作用设定。所以,我们应当给未注册用户设定的时刻为多少吗?
-0年

那就是说相应的折扣率也将变成0了,那样我们就可以安全的将折扣率交付给未注册用户选取了,那就起来吧!

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

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

  二.  业务层设计

IX:提升-最后的收获彻底清爽的代码

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

威尼斯人线上娱乐 9

好的,那就开始做一些转移来落实这些目的呢。咱们可以采用正视注入和选用政策情势这三种方法。

那便是大家明天最后整理出来的代码了:

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

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

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

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

第一大家摆脱了增加方法(也正是静态类),之所以要摆脱这种是因为扩充方法与折扣计算方式之间存在了紧耦合的关联。假设大家想要单元测试我们的措施ApplyDiscount的时候将变得不太容易,因为大家无法不统一测试与之牢牢关系的类PriceExtensions。

为了制止那些,小编创设了DefaultLoyaltyDiscountCalculator 类,那中间含有了ApplyDiscountForTimeOfHavingAccount扩张方法,同事本身经过架空切口ILoyaltyDiscountCalculator躲藏了她的切切实实贯彻。今后,当自家想测试大家的类DiscountManager的时候,小编就能够通过 ILoyaltyDiscountCalculator如法泡制注入虚构对象到DiscountManager类中通过构造函数字显示示测试作用。那里大家选拔的就叫重视注入方式。

威尼斯人线上娱乐 10

在做这几个的还要,大家也将计算折扣率这些成效安全的移交到另三个不等的类中,若是大家想要修改那一段的逻辑,那我们就只须要修改DefaultLoyaltyDiscountCalculator** **类就好了,而不须求改变其他的地点,那样缩小了在改动他的时候发生破坏其余地方的高风险,同时也不供给再扩展单独测试的时刻了。

下边是我们在DiscountManager类中运用分别的逻辑类:

priceAfterDiscount =
_loyaltyDiscountCalculator.ApplyDiscount(priceAfterDiscount,
timeOfHavingAccountInYears);

为了针对账户状态的逻辑来测算折扣率,笔者创造了有个别比较复杂的东西。咱们在DiscountManager类中有五个任务须要解释出去。

  1. 基于账户状态怎样抉择相应的计算方法。
  2. 独特计算格局的底细

为了将率先个权利移交出去,作者创建了工厂类(DefaultAccountDiscountCalculatorFactory),为了兑现工厂格局,然后再把这一个隐蔽到虚幻IAccountDiscountCalculatorFactory里面去。

威尼斯人线上娱乐 11

笔者们的工厂会决定取舍哪个种类总结方法。最终大家通过注重注册形式构造函数将工厂方式注射到威尼斯人线上娱乐,DiscountManager类中

上边正是选择了工厂的DiscountManager类:

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

 以上会指向差别的账户状态再次回到哪天的国策,然后调用ApplyDiscount 方法。

率先个职务已经被接通出去了,接下去便是第二个了。

 接下来大家就从头谈论策略了…..

威尼斯人线上娱乐 12

因为差别的账户状态会有永不的折扣计算方法,所以咱们必要分裂的落到实处政策。座椅卓殊适用于政策方式。

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

NotRegisteredDiscountCalculator SimpleCustomerDiscountCalculator MostValuableCustomerDiscountCalculator**

她们带有了现实的折扣总结方法的贯彻并被藏在了聊以自慰IAccountDiscountCalculator里。

这就允许我们的类DiscountManager接纳方便的方针,而不须求了然具体的完毕。大家的类只须求明白与ApplyDiscount方法相关的IAccountDiscountCalculator 接口重返的对象的门类。

NotRegisteredDiscountCalculator, SimpleCustomerDiscountCalculator,
MostValuableCustomerDiscountCalculator
这个类富含了切实的经过账户状态选拔适合总计的测算办法的落到实处。因为大家的那八个政策看起来相似,大家唯一能做的大多就只有针对这二种总括策略创立3个办法然后各样策略类通过一个毫不的参数来调用她。因为那会让我们的代码变得越来越多,所以自个儿今后控制不这么做了。

好了,到最近停止大家的代码变得可读了,而且每种类都唯有1个权力和权利了-这样修改他的时候会独自一1对应了:

  1. DiscountManager-管理代码流
  2. DefaultLoyaltyDiscountCalculator-可相信的计量折扣率的法子
  3. DefaultAccountDiscountCalculatorFactory-决定依照账户状态选用哪个策略来计量
  4. **NotRegisteredDiscountCalculator, SimpleCustomerDiscountCalculatorMostValuableCustomerDiscountCalculator **
    依据账户状态总计折扣率

现行反革命起来相比较未来与后面的艺术:

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

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

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

  记得在头里的斯马特 UI的例证中,程序的事务逻辑是直接写在了ASPX页前面面包车型客车cs代码中的。现在,接纳分段的章程,大家利用了世界模型来公司来电子商务中的业务逻辑。

总结

在本文中,代码被Infiniti简化了,使得全数的技艺和模式的诠释更便于了。它呈现了什么样解决广大的编制程序难点,以及利用能够的实行和设计格局以非凡、干净的法门化解那些题材的利益。

在本身的办事经验中,笔者屡屡在那篇文章中强调了不良的做法。它们分明存在于广大利用场地,而不是在二个类中,如在本身的事例中这样,那使得发现它们更是困难,因为它们隐藏在合适的代码之间。写这种代码的人三番五次冲突说,他们依照的是回顾戆直的平整。不幸的是,大约拥有的系统都在成人,变得格外复杂。然后,这一个大约的、不可扩充的代码中的每2个修改都以十分主要的,并且带来了高大的危机。

请记住,您的代码将短时间存在于生产条件中,并将在每个工作必要变动上实行修改。由此编写过于简短、不可扩展的代码十分的快就会发出严重的结果。最终一点是对开发人士有利,更加是那多少个在您协调从此维护你的代码。

若果您有一部分题材依据小说不要犹豫联系自身!

  有关世界模型的有的事物,大家在后续的篇章中会讲解的。

威尼斯人线上娱乐 13PS:

依据小说敲的源代码:

链接: 密码:9spz

  注:领域模型格局被设计用来公司复杂的事体逻辑和涉嫌。

 

  下边包车型地铁类图就反映了我们后面的电子商务的须求中所用到的业务模型。

威尼斯人线上娱乐 14

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

  Price类将会蕴藏可算折扣的作业逻辑,并且用政策方式来具体达成折扣的算法-。

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

  

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

 

 

 

  这几个接口就用来促成不一致减价的策略,那是策略情势的1种采取。那么些情势允许咱们在运维的时候改变不相同的算法实现。在本例子中,普赖斯类将会遵照不相同的产品来落实不相同的减价策略。在大家事先的要命斯马特UI例子中,其实那么些打折的算法我们早就写了,可是并未有分离出来,导致了每便加3个减价的算法的策略,程序就要求改变,重新编写翻译,计划。相当于说优惠的壹对是个变化点,我们应有分离出来的。 

注:策略格局:用四个类来封装贰个算法的兑现,并且通过切换算法的贯彻允许在运营时修改一个目的的作为。

 

在电子商务中,不是每一个商品都会减价的,其实我们要促成的优惠策略唯有1种。可是要是那样,我们在写代码的时候就要if-else判断是不是是打折的商品,其实那里仍旧暴露了变化点的:倘诺国庆那天,全数的货色都减价了,那么大家就得修改代码。其实大家能够如此思索:不促销的情景也总算一种优惠,别的的商品降价恐怕是柒折,不打折的动静正是⑩折。 

 

威尼斯人线上娱乐 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
 }

 

 

  我们将会把挑选哪一种减价的方针的逻辑写在二个独自的地点,也便是说:只要客户代码传入相应的参数新闻,大家就机关的创制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决定数据的发源和封存的地方,大概是数据库,也说不定正是内部存款和储蓄器,可是不管怎么,业务类是绝不管这一个的。所以下边用3个接口来落到实处灵活性:  

 

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

 

 

  假设今后有过多的货品,大家想驾驭她们的折扣价格,最简易的不2秘诀便是遍历他们,判断项目,然后使用区别的减价策略。为了特别的可读,我们能够为货品列表建立增添方法,如下:

  

威尼斯人线上娱乐 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);
            }
      }
 }

 

 

  为了简化客户代码的调用工作,大家提供1个像样门户(gateway),可能是Façade的概念:把纷纷的操作逻辑隐藏,留给客户代码三个简约易用的API。大家那边创办二个Service类,如下:

  

威尼斯人线上娱乐 23威尼斯人线上娱乐 24代码

public class ProductService
{
   private IProductRepository _productRepository;

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

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

 

 

  只要客户代码(如出示层中的代码)直接调用下边包车型地铁点子就足以了,而且商品的折扣也依据传入的货色品种不一致来测算。

 

  三.       服务层设计

  服务层就担任应用程序的输入的剧中人物。有时候,能够被认为是façade.不仅如此,因为service分为领域逻辑的service和派别的service。门户的service平常为展现层提供强类型的View Model(有时也称之为Presentation Model)。 1个View Model正是给八个越发的View来使用的。在本例中,我们将会创造Product的View Model来呈现商品的音讯。1般情状下,大家不要把事情类直接揭露给显示层,那样很简单紧耦合,所以在中等就上四个View
Model,其实View Model和业务类的构造基本上,只是View
Model做了有的调动,便于最终的来得。关于View
Model详细的,后文讲述。

  注:Façade形式:为在那之中负责的子系统提供3个简便的接口供外部访问。

** 

  上边大家就来探望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中显得用的。我们事先的Smart UI的方法中,还创制了模版列来展现Product类中未有的字段,其实就一定于在UI中作了迟早的展现逻辑的拍卖。那里大家直接显示ViewModel.

 

  大家应该很熟稔Web
Service:在客户端和劳务应用请求/响应的消息机制进行通信的。我们那里的客户代码和Service也采用那种格局,因为很有一点都不小希望我们在陈设的时候Service的代码和客户代码(展现层)在不一样机器上。

  请求的新闻的布局如下:  

 

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

 

 

  服务在响应请求的时候也要定义格式,而且我们能够在响应中参与越多的习性来判定那么些请求是不是成功。所以在底下的代码中,大家进入了Message属性,用来在伸手失败的时候显得错误消息,还添加了一个Success属性用来判定请求的图景:  

 

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

 

 

  还有一些决不忘记了:因为Product和它对应的View Model结构不一的,而瑟维斯重临的又是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;
        }
    }

 

 

  最终,我们投入贰个ProductService来与业务层的Service 类实行相互,业务层的Service会重返商品列表,然后我们今日添加的这些ProductService会把列表转为ProductViewModels。

 

世家莫不认为奇怪:为啥那边添加了多个ProductService,从前在工作层加二个,以往又加三个,是或不是命名非常要么功效重新?其实在上壹篇已经提过:有时在事情层类添加多个service层,主借使用来公司业务流程的,平时要多少个事情类组合在共同使用,那样重大是为了简化客户程序(约等于调用这一个业务层的代码)的调用,达成类似Façade的作用。

 

我们后天添加的ProductService就是政工层中**service层**的客户程序,因为大家调用了业务层的service,往往有时,大家不想把团结系统的业务类的构造平素揭示给外界,如呈现层,而且也意在提供进一步切合突显层所需的数据结构,那么大家就添加了这些ProductService,提供从工作类到ViewModel的变换。而且在这几个ProductSevice中,我们也足以达成部分要命处理体制,假设涉嫌到了分布式调用,那么我们还足以用那几个ProductService类向彰显层和UI那边隐藏分布式的音讯:完成代理情势。

前几日就写在到此处,在写的进度中发觉那篇有点长了,所以分为三篇(前,中,后)发表!不知情的地点大家多讨论一下,也能够告诉本身!下篇前天公布!见谅!

 


相关文章

发表评论

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

网站地图xml地图