团结的序文表明:

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

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

 

此文为译文,原版的书文地址请点击。
本文通过重构多少个放弃物代码,演讲了什么样写出理想的代码。开发职员及代码审核人士需遵守此标准开发和核查代码。此规范以C#为例,JAVA的童鞋一并参考,C++的童鞋自行脑补吧。

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

引言:

本人的名字叫Radoslaw
Sadowski,作者前日是五个微软技术开发职员。小编从初始工作时就直接接触的微软技术.

在做事一年后,小编见到的材料很差的代码的多寡基本上都足以写成1整本书了。

那些经验让自家变成了多个想要清洁代码的人格障碍病人。

写那篇文章的指标是为了通过体现质量很差的类的例子来注解什么下笔出干净的、可延伸的和可保证的代码。小编会通过好的书写格局和设计格局来诠释坏的代码带来的标题,以及替换他的好的缓解方法。

首先有的是本着那么些拥有C#基础知识的开发人士——我会议及展览示1些常见的失实,然后再呈现1些让代码变得可读性的措施与技术。高级部分重点针对那一个至少存有设计格局概念的开发职员——小编将会突显完全彻底的、单元可测试的代码。

为了能够了然那篇小说你必要至少领会以下三个部分的基本知识:

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

正文中所涉及的事例都将会是具体中如实的切切实实的风味,而不是用装饰格局来做披萨恐怕用政策情势来做计算器那样的示范。

(ps解释:看过设计方式相关的图书的人应当会分晓许多那上头的书籍都以用那种例子,只是为了支持读者精通设计形式)

                           
  金沙注册送58 1 
     
  金沙注册送58 2

因为笔者发觉那系列型的产品不佳用来分解,相反那一个理论性的例子却是格外适合用来在本文中做解释的。

作者们平日会听到说并非用那么些,要用那些,不过却不亮堂那种替换的理由。明日自笔者将会竭尽全力解释和验证那多少个好的书写习惯以及设计形式是真正是在解救大家的费用生活!


此文为译文,原著地址请点击。
本文通过重构二个破烂代码,演说了何等写出卓越的代码。开发人士及代码审核人士需依照此规范开发和稽核代码。此标准以C#为例,JAVA的童鞋一并参考,C++的童鞋自行脑补吧。

  前言:自从上篇公布之后,我们反映了很多问题,因为前篇讲的事物不是很深,恐怕大家看完将来未有何感觉.本章(前篇,中篇,后篇)的重中之重指标其实首先是提议倒霉的宏图,然后比较的提出三个周旋比较客观的分支架构,同时本篇也为再三再四讲述架构方式和设计形式等的文章做个铺垫。

 提示:

  •  在本文中本身不会花时间来讲解C#的特色和关联格局等等(笔者也表明不完),网上有为数不少有关那上头的好的辩论的例证。笔者将聚齐讲述怎样在我们常见工作中运用这么些事物。
  • 事例是1种相比较简单的出色咱们要表明的难点的艺术,不过仅限于描述的题材——因为本人发觉当自家在读书怎么包罗着至关心珍视要代码的例证时,作者意识在精通小说的总体构思方面会有困难。
  •  小编不是说本人文中说的措施是无可比拟的消除措施,笔者只是能确认保证这个艺术将会是让您的代码变得更加高品质的途径。
  • 自身并不关怀下边这一个代码的什么样错误处理,日志记录等等。笔者要发布的只是用来化解1般编码壹些题指标章程。

那就起来吧….

简介

那篇小说的指标是展示什么将1段垃圾代码重构成2个根本的、可扩充性和可爱戴的代码。作者将分解什么通过一级实践和越来越好的设计格局来改写它。

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

  • c# 基础
  • 借助于注入,工厂形式,策略形式

此文中的例子源于实际项目,那里不会有哪些使用装饰形式营造的披萨,也不会选用政策情势的总括器,那个事例是万分好的求证,不过它们很难被在实际项目中使用。


 

那个不好透了的类…

下边的例子是我们现实中的类:

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

地方这几个例子真的是1种尤其差的书写方式。你能清楚那一个类是用来干嘛的么?这些事物是用来做1些奇怪的演算的么?大家小说就从她起来入手来讲学吧…

今昔自笔者来报告您,刚刚这一个类是用来当顾客在网上买东西的时候为她们计算对应折扣的折扣计算和管制的类。

-难以置信吧!

-可是那是真的!

那种写法真的是将难以阅读、难以保证和不便扩张那三种集合在1齐了,而且具备着太差的书写习惯和谬误的形式。

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

1.命名格局-从源代码中我们得以连蒙带猜猜测出来那一个总括情势和输出结果是如何。而且大家想要从这几个类中提取总计算法将会是1件1二分难堪的政工。

如此带来的加害是:

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

金沙注册送58 3

 

假如大家要求满意客户的小购买销售咨询,要像他们来得算法细节,或许大家需求修改那段代码,那将消费大家不短的日子去精通我们的盘算方法的逻辑。即便大家不记录她或重构代码,下次我们/其余开发人士再看这段代码的时候,依旧须求成本相同的小运来研商那几个代码是干嘛的。而且在修改的同时还易于失误,导致原先的总计全体失误。

 2.魔法数字

金沙注册送58, 金沙注册送58 4

在这些例子中type是变量,你能猜到它代表着客户账户的等级么?If-else
if
话语是用来促成怎么样选取计算出产品价格折扣的格局。

当今大家不知底怎样的账户是一,二,③或四。今后设想一下,当你不得不为了那几个有价值的VIP客户改变他们的折扣计算方法的时候,你试着从那3个代码中找出修改的格局—那个历程恐怕会费用你非常长的时刻不说,还很有望犯错以至于修改这一个基础的相似的客户的账户,毕竟像二要么三那些用语毫无描述性的。可是在我们犯错以往,那叁个一般的客户却很开心,因为他们获取了VIP客户的折扣。:)

3.未曾显著的bug

因为我们的代码品质很差,而且可读性相当差,所以大家或然随便就忽略掉很多不胜重大的作业。想象一下,未来黑马在系统中加进一种新的客户类型-金卡用户,而在咱们的类别中别的一种新的账户类型最后得到的标价将是0元。为何吗?因为在大家的if-else
if
语句中绝非任何情况是知足新的情形的,所以只即便未处理过的账户类型,末了重临值都将变成0。1旦大家的业主发现那件事,他将会老羞成怒-究竟她早已免费卖给那样用户很多过多东西了!

金沙注册送58 5

4.并未可读性

大家必须认同上面那段代码的可读性是真的不佳。

她让大家开销了太多的年华去了然那段代码,同时期码隐藏不当的可能率太大了,而那正是平素不可读性的最器重的概念。

 5.魔法数字(再度)

您从代码中能知道好像0.一,0.柒,0.伍这个数字的意思么?好的,作者承认自身不领悟。唯有大家自个儿编写那几个代码大家才晓得那是怎么样意思,别人是无力回天清楚的。

你尝试想想如果让您改改上面那句代码,你会怎么着:

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

因为那几个格局完全不行读,所以您改改的进度中只好尝试着把第三个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));

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

在那多个算术中,唯壹的区别就只是贰个静态参数,而大家全然能够用二个可变的参数来取代。

假设大家不试着在写代码的时候从一直ctri+c,ctrl+v中抽身出来,那大家将遭受的题材正是我们只可以修改代码中的部分功效,因为大家不知晓有多少地方必要修改。上边的逻辑是总计出在大家系统中每一个客户对应年限获得的折扣,所以一旦大家只是贸然修改两到叁处,很不难造成任什么地方方的内外分裂。

7.种种类具有太多的纷纭的权力和权利区域

小编们写的类至少背负了多少个任务:

  1. 选用总计的运算法则
  2. 为每一个分歧情状的账户总括折扣率
  3. 基于种种客人的限期计算出相应的折扣率

这一个背离了单一权利标准。那么那会带来怎样危机呢?如若大家想要改变上诉1个特色中的三个,那就意味着恐怕会碰触到壹些任何的大家并不想修改的性状。所以在改动的时候大家不得不重新测试全数的类,那么那就导致了很重的年华的荒废。

一段垃圾代码

在大家真正的出品中有那般三个类:

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

那是一段相当不佳的代码(二胡:假设你没以为那段代码很倒霉,这您近来境况大概很不佳了),我们不太明白那些类的指标是何等。它可能是2个网上商城的折扣管理类,负责为客户总括折扣。

本条类完全具备了不足读、不可维护、不可增添的特性,它选拔了广大坏的执行和反方式的统一筹划。

下边大家稳步分析那里毕竟有微微难点?

  • 取名难点 –
    大家只好通过估摸那么些类到底是为着总计什么。那实际上是在浪费时间。
    比方大家未有有关文书档案大概重构那段代码,那我们下3次恐怕需求花大批量的岁月才能清楚那段代码的切切实实意思。

  • 魔数 –
    在这几个例子中,你能猜到变量type是指客户账户的情景呢。通过if-else来选用计算促销后的产品价格。
    现行反革命,我们压根不通晓账户状态壹,2,三,陆分头是怎么着意思。
    除此以外,你明白0.1,0.七,0.5都以何等看头吧?
    让大家想象一下,若是您要修改上面这行代码:
    result = (amount - (0.5m * amount)) - disc * (amount - (0.5m * amount));

  • NET架构设计,怎么样将坏的代码重新编写翻译为好的代码。隐身的BUG –
    因为代码卓殊倒霉,大家大概会失掉非常首要的工作。试想一下,要是大家的种类中新增了一类账户状态,而新的账户等级不满意任何3个if-else条件。那时,再次回到值会固定为0。

  • 不可读 –
    我们不得不认可,那是1段不可读的代码。不可读=更加多的知情时间+增添发生错误的高危害

  • DLANDY – 不要产生重复的代码
    我们也许不可能壹眼就找出它们,但它们确实存在。
    例如:disc *(amount - (0.1m * amount));
    相同的逻辑:
    disc *(amount - (0.5m * amount));
    此间唯有一个数值差异。假设大家不恐怕摆脱再度的代码,大家会境遇很多标题。比如某段代码在四个地点有再次,当大家须要修改这部分逻辑时,你很只怕只修改到了二至三处。

  • 单纯任务规范
    以此类至少存有四个职务:
    一 选用总计算法
    贰 遵照账户状态计算折扣
    叁 依据账户网龄计算折扣
    它违反了单纯性任务规范。那会推动如何危害?假若大家就要修改第5个效益的话,会影响到此外第一个成效。那就表示,大家每便修改都会改变我们本不想修改的代码。因而,大家只好对一切类举行测试,那实在很浪费时间。

简介

那篇小说的目标是展示怎样将一段垃圾代码重构成三个彻底的、可扩张性和可保证的代码。作者将分解怎么着通过一流实践和更加好的设计情势来改写它。

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

  • c# 基础
  • 正视注入,工厂格局,策略方式

此文中的例子源于实际项目,这里不会有怎么着使用装饰格局创设的披萨,也不会选择政策形式的计算器,那一个事例是不行好的验证,然而它们很难被在事实上项目中选拔。

本篇的议题如下:

一. style=”font-family: 燕体; color: red”>证明示例供给

2. style=”font-family: 石籀文; color: red”>业务层设计

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

四. style=”font-family: 大篆; color: red”>数据访问层设计

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

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

这就起先重构吧…

在接下去的7个步骤中自笔者将向你显示大家什么样制止上诉难点来创设2个彻底的易维护,同时又方便单元测试的看起来一目了解的代码。

 

重构

通过以下9布,作者会告诉你们怎么制止上述危害并落到实处一个彻底的、可保险的、可测试的代码。

  1. 命名,命名,命名
    那是理想代码的最主要方面之壹。我们只需求改变方法,参数和变量的命名。未来,我们能够方便的知道上边包车型大巴类是负责什么的了。

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

但是大家任然不晓得账户状态一,二,3到底是什么样意思。

  1. 魔数
    在C#中防止魔数大家一般采纳枚举来替换它们。那里运用AccountStatus
    枚举来替换if-else中的魔数。
    public enum AccountStatus { NotRegistered = 1, SimpleCustomer = 2, ValuableCustomer = 3, MostValuableCustomer = 4 }
    今昔我们来探视重构后的类,大家得以很简单的表露哪3个账户状态应该用如何算法来测算折扣。混合账户状态的高风险急速的降低了。

     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.2m,0.三m,作者m,大家并不知道它们是什么样意思。
    此外decimal discountForLoyaltyInPercentage =
    (timeOfHavingAccountInYears > 5) ? (decimal)5/100 :
    (decimal)timeOfHavingAccountInYears/100;
    中,数字伍也格外神秘。
    大家无法不让它们更享有描述性,那时使用常量会是3个比较好的点子。
    作者们来定义三个静态类:

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

随着修改DiscountManager类:

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

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

透过措施名称,大家就足以明白它的天职是何许,未来修改大家的例证:

    public class DiscountManager
    {
    public decimal ApplyDiscount(decimal price, AccountStatus accountStatus, int timeOfHavingAccountInYears)
    {
        decimal priceAfterDiscount = 0;
        switch (accountStatus)
        {
            case AccountStatus.NotRegistered:
                priceAfterDiscount = price;
                break;
            case AccountStatus.SimpleCustomer:
                priceAfterDiscount = price.ApplyDiscountForAccountStatus(Constants.DISCOUNT_FOR_SIMPLE_CUSTOMERS)
                  .ApplyDiscountForTimeOfHavingAccount(timeOfHavingAccountInYears);
                break;
            case AccountStatus.ValuableCustomer:
                priceAfterDiscount = price.ApplyDiscountForAccountStatus(Constants.DISCOUNT_FOR_VALUABLE_CUSTOMERS)
                  .ApplyDiscountForTimeOfHavingAccount(timeOfHavingAccountInYears);
                break;
            case AccountStatus.MostValuableCustomer:
                priceAfterDiscount = price.ApplyDiscountForAccountStatus(Constants.DISCOUNT_FOR_MOST_VALUABLE_CUSTOMERS)
                  .ApplyDiscountForTimeOfHavingAccount(timeOfHavingAccountInYears);
                break;
            default:
                throw new NotImplementedException();
        }
        return priceAfterDiscount;
    }
    }
  1. 去除没用的代码
    我们应该写出简约的代码,因为简短的代码=减弱BUG发生的机率,并且也让我们缩小精通事情逻辑的小运。
    咱俩发现,那里二种情形的客户调用了一样的方法:
    .ApplyDiscountForTimeOfHavingAccount(timeOfHavingAccountInYears);
    那里能够统一代码:

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

九.结尾,获得彻底的代码
说起底,让大家经过引进依赖注入和工厂方法形式来取得最后的版本吧。
先来看卡最终结出:

    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类来替换ApplyDiscountFor提姆eOfHavingAccount这几个扩张方法,此类还落到实处了ILoyaltyDiscountCalculator接口。未来,当大家要测试DiscountManager类时,我们只构造函数注入ILoyaltyDiscountCalculator接口的达成即可。这里运用了借助注入。
通过那样做,大家将网龄折扣的算法迁移到接近DefaultLoyaltyDiscountCalculator的不一样类中,那样当我们修改某二个算法不会覆盖到其它业务。
对此依据账户状态来总计折扣的作业,大家须要在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.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;
}
}

那是一段乌烟瘴气的代码(贰胡:如若你没觉得那段代码很倒霉,那您最近景观只怕很不佳了),我们不太精通那些类的指标是怎么。它大概是四个网上商城的折扣管理类,负责为客户总结折扣。

这么些类完全拥有了不可读、不可维护、不可扩充的特性,它选拔了过多坏的执行和反情势的统一筹划。

下边咱们逐步分析这里终归某个许难题?

  • 取名难题 –
    我们只能通过估计那几个类到底是为着总结什么。这事实上是在浪费时间。
    假设大家没有相关文书档案或然重构那段代码,那大家下一遍只怕需求花多量的小时才能明了那段代码的有血有肉意思。

  • 魔数 –
    在那个例子中,你能猜到变量type是指客户账户的事态吧。通过if-else来选取总计减价后的产品价格。
    今后,大家压根不知情账户状态1,贰,三,四分别是什么样意思。
    其它,你知道0.一,0.柒,0.5都是如何看头吧?
    让大家想象一下,借使您要修改下边那行代码:
    result = (amount - (0.5m * amount)) - disc * (amount - (0.5m * amount));

  • 潜伏的BUG –
    因为代码13分倒霉,大家可能会失去格外重大的事体。试想一下,若是大家的种类中新增了一类账户状态,而新的账户等级不满足任何八个if-else条件。那时,重返值会固定为0。

  • 不足读 –
    我们只可以认可,那是一段不可读的代码。不可读=越来越多的领会时间+扩充发生错误的风险

  • DKugaY – 不要发生重复的代码
    我们只怕不能够1眼就找出它们,但它们确实存在。
    例如:disc *(amount - (0.1m * amount)); 同样的逻辑:
    disc *(amount - (0.5m * amount));
    那里唯有一个数值不均等。纵然大家无能为力解脱再一次的代码,我们会遇见许多题材。比如某段代码在多个地点有双重,当大家需求修改那1部分逻辑时,你很或然只修改到了二至3处。

  • 单纯性职责规范 这几个类至少存有多少个职分: 壹 选用总结算法 2依照账户状态总计折扣 三 依据账户网龄计算折扣
    它违反了纯粹职分规范。这会带来什么样风险?假诺大家即将修改第一个作用的话,会潜移默化到其余第3个职能。那就代表,大家每一回修改都会改变大家本不想修改的代码。由此,大家只能对全部类举行测试,那实在很浪费时间。

 ** ** **

I:命名,命名,命名

恕作者直言,那是代码中最重大的一步。大家只是修章/参数/变量那么些的名字,而近来大家得以直观的掌握到上边这几个类代表如何看头。

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

固然这么,大家照旧不知道1,二,三,四代表着什么,那就连任往下呢!

总结

正文通过简单易懂的法子重构了一段难点代码,它显得了什么在实质上意况中接纳最好实践和设计情势来救助我们写出干净的代码。
就自笔者的干活经验来说,本文中冒出的涂鸦做法是日常发生的。编写那种代码的人连连觉得她们能够保证那种规则,但不幸的是系统和业务往往都会越加复杂,每一回修改那类代码时都会带来巨大的高风险。

重构

经过以下九布,笔者会告诉你们怎么制止上述危机并促成一个彻底的、可体贴的、可测试的代码。

  1. 取名,命名,命名
    那是能够代码的最关键方面之一。大家只必要改变方法,参数和变量的命名。以后,大家能够适当的接头上面包车型大巴类是负责什么的了。

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

    只是大家任然不清楚账户状态1,贰,三到底是何许意思。

  2. 魔数
    在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;
    中,数字5也要命神秘。
    大家不可能不让它们更有着描述性,那时使用常量会是3个相比较好的主意。
    大家来定义四个静态类:

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

    随之修改DiscountManager类:

    public class DiscountManager
    {
    public decimal ApplyDiscount(decimal price, AccountStatus accountStatus, int timeOfHavingAccountInYears)
    {
        decimal priceAfterDiscount = 0;
        decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > Constants.MAXIMUM_DISCOUNT_FOR_LOYALTY) ? (decimal)Constants.MAXIMUM_DISCOUNT_FOR_LOYALTY / 100 : (decimal)timeOfHavingAccountInYears / 100;
        switch (accountStatus)
        {
            case AccountStatus.NotRegistered:
                priceAfterDiscount = price;
                break;
            case AccountStatus.SimpleCustomer:
                priceAfterDiscount = (price - (Constants.DISCOUNT_FOR_SIMPLE_CUSTOMERS * price));
                priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
                break;
            case AccountStatus.ValuableCustomer:
                priceAfterDiscount = (price - (Constants.DISCOUNT_FOR_VALUABLE_CUSTOMERS * price));
                priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
                break;
            case AccountStatus.MostValuableCustomer:
                priceAfterDiscount = (price - (Constants.DISCOUNT_FOR_MOST_VALUABLE_CUSTOMERS * price));
                priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
                break;
            default:
                throw new NotImplementedException();
        }
        return priceAfterDiscount;
    }
    }
    
  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);
    }
    }
    

    第2,大家摆脱了扩充方法(静态类),假使我们想对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;
    }
    }
    

  一. 表明示例须求

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 语句,来充实小说的可读性。

与此同时,笔者也将3个非常短的测算格局拆分为两句话来写。今后大家将“
通过账户状态来计量折扣率”与“通过账户定期来估测计算折扣率”那三头分别来测算。

例如: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。固然很颓靡,但却是真的。

那大家该怎么样修复这一个题材吧?那就唯有经过没错误提醒了。

金沙注册送58 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)

金沙注册送58 7

VI:通过其它方法再摆脱魔法数

接下去让我们的眼神放在通过账户状态计算折扣率的一个钱打二十五个结格局中的静态变量:(static_discount_in_percentages/100)

然后带入上面数字距离试试:0.一m,0.叁m,0.⑤m

那一个数字其实也是壹类别型的魔法数-他们也一向不一贯告知大家他们意味着着怎么着。

咱俩也有同1的情事,比如将“有账户的时辰”折价为“忠诚折扣”。

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

数字伍让我们的代码变得神秘了四起。

咱俩亟须做些什么让那些变得更具表现性。

自个儿会用其余一种情势来制止魔法数的表明的出现-相当于C#中的常量(关键词是const),笔者强烈提议在大家的应用程序中特地定义2个静态类来储存那个常量。

在大家的事例中,笔者是创建了上面包车型大巴类:

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

因此一定的修改,我们的DiscountManager类就改成了如此了:

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

本身愿意您也承认自身这几个办法会进一步使代码本身变得更拥有表达性:)

恐怕部分朋友认为从斯马特 UI马上跳到那种分层设计,就像快了些。其实也终归贰个思虑的跃进吧。上边就来看看那种分层是怎样消除在此之前斯马特UI的标题标。 

VII:不要再重复啦!

金沙注册送58 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 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:移除那2个多余的代码

在写代码的时候条件上是我们的代码越是精简越好。精简的代码的象征,越少的谬误的恐怕,在读书明白代码逻辑的时候成本的日子越少。

据此今后起先精简大家的代码吧。

我们得以自由发现大家二种客户账户下拥有同样的方法:

.ApplyDiscountForTimeOfHavingAccount(timeOfHavingAccountInYears);

大家可不得以只写1遍啊?大家事先将未注册的用户放在了抛出尤在那之中,因为我们的折扣率只会一个钱打二十七个结注册用户的期限,并未给未注册用户留有功用设定。所以,大家理应给未注册用户设定的小时为多少吧?
-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语句外面。好处就是:更加少的代码量!

  二.  业务层设计

IX:升高-最终的获得彻底清洁的代码

好了,以往大家得以像阅读1本书一样方便来审视大家的代码了,可是这就够了么?我们得以将代码变得最棒简单的!

金沙注册送58 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的时候将变得不太不难,因为大家亟须统1测试与之严格关联的类PriceExtensions。

为了制止那几个,作者创立了DefaultLoyaltyDiscountCalculator 类,那里面含有了ApplyDiscountForTimeOfHavingAccount扩充方法,同事自身通过架空中接力口ILoyaltyDiscountCalculator隐身了她的求实完结。今后,当小编想测试大家的类DiscountManager的时候,作者就能够透过 ILoyaltyDiscountCalculator仿照注入虚构对象到DiscountManager类中通过构造函数字展现示测试作用。这里我们选取的就叫重视注入情势。

金沙注册送58 10

在做那一个的同时,我们也将总括折扣率这么些意义安全的移交到另3个分裂的类中,假使大家想要修改这一段的逻辑,那我们就只须求修改DefaultLoyaltyDiscountCalculator** **类就好了,而不须要改变别的的地方,那样裁减了在变更他的时候发出破坏其余地点的风险,同时也不须求再扩大单独测试的流年了。

上边是大家在DiscountManager类中采取分其他逻辑类:

priceAfterDiscount =
_loyaltyDiscountCalculator.ApplyDiscount(priceAfterDiscount,
timeOfHavingAccountInYears);

为了针对账户状态的逻辑来测算折扣率,小编创设了有的相比复杂的事物。我们在DiscountManager类中有多个权利需求表达出去。

  1. 根据账户状态如何挑选相应的乘除方法。
  2. 优秀总计格局的细节

为了将首个职责移交出去,小编创制了工厂类(DefaultAccountDiscountCalculatorFactory),为了兑现工厂情势,然后再把那个隐形到虚幻IAccountDiscountCalculatorFactory里面去。

金沙注册送58 11

大家的工厂会决定选择哪一类总结办法。最终大家因此正视注册格局构造函数将工厂形式注射到DiscountManager类中

上边便是使用了工厂的DiscountManager类:

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

 以上会指向不一样的账户状态重回什么时候的国策,然后调用ApplyDiscount 方法。

率先个职务已经被接入出去了,接下去正是第壹个了。

 接下来我们就起初谈论策略了…..

金沙注册送58 12

因为差别的账户状态会有永不的折扣总计办法,所以大家须求差别的贯彻政策。座椅分外适用于政策形式。

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

NotRegisteredDiscountCalculator SimpleCustomerDiscountCalculator MostValuableCustomerDiscountCalculator**

他们带有了切实的折扣计算方法的贯彻并被藏在了抽象IAccountDiscountCalculator里。

那就允许我们的类DiscountManager采纳非常的方针,而不要求精通具体的完成。大家的类只供给理解与ApplyDiscount方法相关的IAccountDiscountCalculator 接口重临的靶子的品类。

NotRegisteredDiscountCalculator, SimpleCustomerDiscountCalculator,
MostValuableCustomerDiscountCalculator
那些类富含了切实可行的通过账户状态选择符合总计的一个钱打二拾伍个结方法的落到实处。因为大家的那四个政策看起来相似,我们唯1能做的几近就唯有针对那二种总括策略创立1个办法然后每一个策略类通过二个毫无的参数来调用她。因为那会让大家的代码变得进一步多,所以笔者前日决定不那样做了。

好了,到近来截至大家的代码变得可读了,而且每一种类都唯有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 }

  记得在在此之前的Smart UI的例证中,程序的事体逻辑是向来写在了ASPX页前边面包车型地铁cs代码中的。未来,接纳分段的措施,大家选用了世界模型来组织来电子商务中的业务逻辑。

总结

在本文中,代码被Infiniti简化了,使得全体的技能和方式的分解更易于了。它突显了什么消除周围的编制程序难点,以及选用非凡的实施和设计形式以适合、干净的措施缓解那一个难题的功利。

在自家的做事经历中,作者屡屡在那篇文章中强调了不佳的做法。它们显然存在于广大接纳场所,而不是在多少个类中,如在小编的例证中那样,那使得发现它们进一步不方便,因为它们隐藏在适用的代码之间。写这种代码的人接二连三争执说,他们依据的是总结古板的规则。不幸的是,大约拥有的系统都在成人,变得十分复杂。然后,那些不难的、不可扩张的代码中的每2个改动都以充裕首要的,并且推动了光辉的风险。

请记住,您的代码将长时间存在于生产条件中,并将在各样事情供给变动上进展修改。因而编写过于简短、不可扩充的代码不慢就会发出严重的结局。最终一点是对开发人员有利,特别是那一个在您本人将来维护你的代码。

若果你有壹些难点依据小说不要犹豫联系自己!

  有关领域模型的片段事物,大家在延续的篇章中会讲解的。

金沙注册送58 13PS:

听说作品敲的源代码:

链接: 密码:9spz

  注:领域模型格局被规划用来集团复杂的工作逻辑和涉及。

 

  下边包车型客车类图就突显了我们前边的电子商务的急需中所用到的事人体模型型。

金沙注册送58 14

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

  Price类将会包涵可算折扣的事情逻辑,并且用政策格局来具体贯彻折扣的算法-。

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

  

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

 

 

 

  这几个接口就用来贯彻不一样降价的策略,那是政策格局的1种选择。这一个格局允许大家在运维的时候改变不一样的算法完毕。在本例子中,Price类将会基于分歧的出品来兑现不一致的减价策略。在我们事先的百般斯马特UI例子中,其实这一个减价的算法我们早就写了,可是未有分离出来,导致了历次加二个降价的算法的策略,程序就供给转移,重新编写翻译,铺排。也正是说优惠的局地是个变化点,大家相应分离出来的。 

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

 

在电子商务中,不是种种商品都会优惠的,其实大家要兑现的打折策略唯有一种。可是借使如此,我们在写代码的时候就要if-else判断是不是是降价的商品,其实那里依旧揭破了变化点的:若是国庆那天,全数的货色都降价了,那么大家就得修改代码。其实大家能够这么思虑:不减价的场馆也好不简单一种优惠,其余的货色优惠或许是柒折,不优惠的气象正是十折。 

 

金沙注册送58 15金沙注册送58 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类的完成。

 

金沙注册送58 17金沙注册送58 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个独自的地点,也正是说:只要客户代码传入相应的参数音讯,大家就机关的创办三个体面的促销策略对象。很分明,那里能够利用工厂方法来促成,如下:  

 

金沙注册送58 19金沙注册送58 20代码

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

 

 

  在上头的逻辑分层中,大家树立了3个Repository的类库,其实咱们就算想使用Repository方式来落到实处”持久化非亲非故性”—–业务类完全不用管怎么样保存和获取数据。而且由Repository决定数据的根源和保留的地方,也许是数据库,也或许正是内部存款和储蓄器,可是不管怎么,业务类是无须管这几个的。所以上边用3个接口来促成灵活性:  

 

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

 

 

  假如今后有为数不少的商品,大家想知道她们的折扣价格,最简便易行的不二等秘书诀正是遍历他们,判断项目,然后利用分裂的降价策略。为了特别的可读,大家得以为货品列表建立扩张方法,如下:

  

金沙注册送58 21金沙注册送58 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。我们那里开创3个Service类,如下:

  

金沙注册送58 23金沙注册送58 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)。 3个View Model正是给三个特意的View来使用的。在本例中,我们将会树立Product的View Model来展现商品的新闻。一般情况下,我们绝不把工作类间接暴光给彰显层,那样很简单紧耦合,所以在中游就上四个View
Model,其实View Model和业务类的协会基本上,只是View
Model做了有的调整,便于最终的展现。关于View
Model详细的,后文讲述。

  注:Façade形式:为内部负责的子系统提供二个粗略的接口供外部访问。

** 

  上边我们就来看看Product的View Model是什么写的:

 

金沙注册送58 25金沙注册送58 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结构区别的,而Service再次回到的又是ViewModel的响应,那么就须求把得到到的Product转换为View Model的协会。可以把转换的代码写在二个特定的地点(可以认为是个Mapping的历程),为了阅读的便利,大家能够为List<Product>添加扩充方法,直接调用,如下:

  

 

金沙注册送58 27金沙注册送58 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,从前在工作层加3个,现在又加3个,是否命名有失常态依旧功用重新?其实在上1篇已经提过:有时在事情层类添加几个service层,首假如用来组织业务流程的,平常要几个业务类组合在同步行使,这样重点是为了简化客户程序(约等于调用那一个业务层的代码)的调用,完毕类似Façade的效应。

 

咱俩明天添加的ProductService便是作业层中**service层**的客户程序,因为我们调用了业务层的service,往往有时,大家不想把团结系统的业务类的构造一向揭穿给外界,如显示层,而且也盼望提供更为符合呈现层所需的数据结构,那么大家就添加了这一个ProductService,提供从作业类到ViewModel的变换。而且在这些ProductSevice中,我们也足以完结部分丰硕处理机制,若是提到到了分布式调用,那么我们还是能用那些ProductService类向展现层和UI那边隐藏分布式的新闻:实现代理情势。

后天就写在到这边,在写的进度中窥见那篇有点长了,所以分为3篇(前,中,后)公布!不理解的地点大家多切磋一下,也足以告知作者!下篇明天公布!见谅!

 

相关文章

网站地图xml地图