澳门新萄京 5

如何将坏的代码重新编译为好的代码,编码规范实战篇【澳门新萄京】

友好的前言表明:

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

本系列还恐怕有别的小说,后续将渐次翻译。

 

引言:

自家的名字叫Radoslaw
Sadowski,作者前日是五个微软技术开采人士。笔者从初始工作时就一贯接触的微软工夫.

在办事一年后,小编看看的成色相当不佳的代码的数量基本上都足以写成一整本书了。

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

写那篇小说的指标是为着通过展现品质相当差的类的例证来表明什么下笔出到底的、可延伸的和可尊崇的代码。小编会通过好的书写方式和设计情势来解释坏的代码带来的难题,以及替换他的好的解决形式。

首先有个别是本着那个具有C#基础知识的开垦人士——笔者会议及展览示一些周围的荒谬,然后再展现一些让代码变得可读性的格局与技术。高等部分重大针对那四个至少存有设计格局概念的开荒人士——作者将会议及展览示完全深透的、单元可测验的代码。

为了可以驾驭这篇小说你要求至少通晓以下多个部分的基本知识:

  • C#语言
  • 依赖注入、工厂设计形式、战略设计格局

正文中所涉及的例证都将会是切实可行中实实在在的现实性的特点,并不是用装饰模式来做披萨可能用政策形式来做总结器那样的演示。

(ps解释:看过设计方式相关的图书的人应有会理解多数那方面包车型客车书本都以用这种例子,只是为着协助读者知道设计形式)

                           
  澳门新萄京 1 
     
  澳门新萄京 2

因为自个儿开采那种类型的成品倒霉用来讲解,相反这一个理论性的例子却是极度适合用来在本文中做表达的。

大家平常会听到说毫不用那个,要用那多少个,然而却不亮堂这种替换的说辞。明天自身将会大力解释和认证那三个好的书写习于旧贯以及设计格局是当真是在拯救大家的开支生活!

此文为译文,最先的作品地址请点击。
本文通过重构一个污源代码,演讲了怎么着写出理想的代码。开垦人士及代码核实职员需遵照此标准开垦和核对代码。此标准以C#为例,JAVA的童鞋一并参考,C++的童鞋自行脑补吧。

 提示:

  •  在本文中自己不会花时间来说解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.命有名的模特式-从源代码中我们得以连蒙带猜推断出来那些总括方法和出口结果是哪些。并且我们想要从那些类中领取计算算法将会是一件非常艰辛的业务。

与此相类似带来的有毒是:

最严重的主题材料是:浪费时间,

澳门新萄京 3

 

只要我们供给满意顾客的购销咨询,要像她们来得算法细节,可能大家须求修改这段代码,这将成本我们非常长的时刻去了解咱们的企图方式的逻辑。就算大家不记录她或重构代码,下一次大家/其余开荒职员再看这段代码的时候,依旧必要开销同样的年月来研商这个代码是干嘛的。而且在修改的同期还轻便出错,导致原来的乘除全体失误。

 2.法力数字

 澳门新萄京 4

在那几个事例中type是变量,你能猜到它表示着顾客账户的等第么?If-else
if
言辞是用来兑现怎么样挑选总结出产品价格折扣的不二诀窍。

明日大家不明了怎么样的账户是1,2,3或4。现在虚拟一下,当你不得不为了那么些有价值的VIP顾客退换她们的折扣总括情势的时候,你试着从这个代码中寻觅修改的艺术—那么些历程恐怕会耗费你相当短的小时不说,还很有十分的大恐怕犯错乃至于修改那多少个基础的形似的客商的账户,毕竟像2依旧3这么些用语毫无描述性的。然而在大家犯错今后,那多少个一般的客商却很喜欢,因为他俩获得了VIP客商的折扣。:)

3.从未显著的bug

因为我们的代码品质相当不佳,何况可读性比较不好,所以大家兴许轻松就忽略掉很多格外主要的政工。想象一下,今后猛然在系统中加进一种新的顾客类型-金卡客户,而在我们的体系中任何一种新的账户类型最后收获的价位将是0元。为何吧?因为在我们的if-else
if
语句中尚无别的情状是满意新的景观的,所以假若是未管理过的账户类型,最终重临值都将形成0。一旦大家的老总娘开采这事,他将会怒发冲冠-毕竟她早就无偿卖给这么客商非常多居多事物了!

澳门新萄京 5

4.尚未可读性

我们必需认同下边这段代码的可读性是真的不好。

她让我们花费了太多的岁月去领略这段代码,同有时候代码掩盖不当的可能率太大了,而那正是未曾可读性的最要害的定义。

 5.法力数字(再度)

您从代码中能知道好像0.1,0.7,0.5那个数字的情致么?好的,我分明本身不驾驭。独有我们自个儿编辑这么些代码大家才清楚那是怎么看头,外人是爱莫能助领悟的。

您试试想想假使令你改改上边那句代码,你会怎样:

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

因为那么些方法完全不可读,所以您改改的进程中只好尝试着把第一个0.5改成0.4而保持第一个0.5不懂。那恐怕会是一个bug,可是却是最佳的最合适的改变章程。因为那些0.5哪些都尚未报告大家。

同样的事也存在将years变量调换成disc变量的转换进程中:

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

那是用来计量折扣率的,会经过账户在大家系统的时间的百分比来获取。好的,那么以后主题材料来了,如若时光正好好正是5吧?

6.轻松-不要一再做无用功

即使第一眼看的时候不易于看出来,不过留神切磋一下就能发觉:咱们的代码里有为数十分多双重的地点。举个例子:disc *
(amount – (0.1m * amount));

而与之有同样服从的还或许有(只是变了贰个参数而已):disc * (amount –
(0.5m * amount))

在那一个算术中,独一的区分就只是二个静态参数,而大家完全能够用三个可变的参数来顶替。

万一大家不试着在写代码的时候从第一手ctri+c,ctrl+v中解脱出来,这我们将境遇的标题便是大家不得不修改代码中的部分功效,因为大家不知底有微微地点供给修改。下边包车型地铁逻辑是估测计算出在大家系统中各样客商对应年限得到的折扣,所以一旦大家只是贸然修改两到三处,很轻易变成任什么地点方的光景不相同。

7.各个类具备太多的复杂的任务区域

咱俩写的类至少背负了八个义务:

  1. 慎选总括的运算法规
  2. 为各样不一样意况的账户计算折扣率
  3. 依据每一种客人的限制期限总括出相应的折扣率

以此背离了单纯权利原则。那么那会推动怎么样伤害呢?若是大家想要改动上诉3个特征中的五个,那就表示或者会碰触到部分其余的大家并不想修改的风味。所以在退换的时候大家不得不重新测量试验全部的类,那么那就招致了比较重的时日的疏落。

简介

那篇文章的指标是浮现什么将一段垃圾代码重构成一个完完全全的、可增加性和可保证的代码。笔者将解释什么通过一级实行和更加好的设计格局来改写它。

翻阅本文你供给有以下基础:

  • c# 基础
  • 依附注入,工厂格局,战略方式

此文中的例子源于实际项目,这里不会有哪些使用装饰情势创设的披萨,也不会使用政策形式的总括器,那么些事例是相当好的验证,可是它们很难被在实质上项目中运用。

那就开首重构吧…

在接下去的9个步骤中本身将向你来得大家什么样制止上诉难点来创设一个到底的易维护,同有时间又平价单元测量检验的看起来胸有成竹的代码。

 

一段垃圾代码

在我们真实的成品中有这么三个类:

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

那是一段非常不好的代码(二胡:假诺您没觉着这段代码比较不好,那你近些日子状态大概比比较差了),大家不老子@楚那么些类的目标是哪些。它或然是八个英特网商铺的折扣管理类,负担为顾客总计折扣。

那些类完全具有了不足读、不可维护、不可扩充的特征,它选取了成千上万坏的试行和反情势的规划。

下边大家稳步深入分析这里究竟有多少难题?

  • 取名难题 –
    我们只可以通过揣摸这么些类到底是为了总括什么。那实质上是在浪费时间。
    假设大家从没相关文书档案大概重构这段代码,那我们下三回只怕需求花多量的时日本事领会这段代码的实际意思。

  • 魔数 –
    在这些事例中,你能猜到变量type是指客商账户的事态呢。通过if-else来摘取计算巨惠后的产品价格。
    以后,大家压根不亮堂账户状态1,2,3,4分别是怎么意思。
    别的,你掌握0.1,0.7,0.5都以何许意思吧?
    让我们想像一下,若是你要修改下边那行代码:
    result = (amount - (0.5m * amount)) - disc * (amount - (0.5m * amount));

  • 隐身的BUG –
    因为代码特别倒霉,大家或然会失去特别首要的政工。试想一下,倘诺大家的体系中新扩大了一类账户状态,而新的账户品级不知足任何两个if-else条件。那时,再次来到值会固定为0。

  • 不行读 –
    大家只能承认,那是一段不可读的代码。不可读=越多的明亮时间+扩大产生错误的高危害

  • DLacrosseY – 不要发生重复的代码
    我们或然无法一眼就找寻它们,但它们确实存在。
    比如:disc *(amount - (0.1m * amount)); 一样的逻辑:
    disc *(amount - (0.5m * amount));
    这里独有贰个数值差异样。假如大家无计可施抽身再次的代码,大家会遭遇重重主题素材。比方某段代码在八个地方有重新,当大家供给修改那有些逻辑时,你很只怕只修改到了2至3处。

  • 纯净职务规范 那么些类至少存有八个职务: 1 接纳计算算法 2
    依照账户状态计算折扣 3 依据账户网龄总结折扣
    它违反了单纯性职分标准。那会拉动哪些风险?若是我们将要修改第八个功效的话,会影响到别的第4个效果与利益。那就意味着,大家每一趟修改都会转移我们本不想修改的代码。由此,大家只可以对全体类举行测量检验,那实在很浪费时间。

I:命名,命名,命名

恕小编直言,那是代码中最重大的一步。大家只是修改章程/参数/变量那几个的名字,而现行我们得以直观的刺探到上边那一个类代表如何看头。

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

即使如此这么,我们还是不知道1,2,3,4意味着怎么着,那就继续往下呢!

重构

经过以下9布,作者会告诉你们怎么幸免上述危害并完毕二个根本的、可尊崇的、可测量检验的代码。

  1. 取名,命名,命名
    那是地道代码的最珍视方面之一。我们只必要改造方法,参数和变量的命名。今后,大家得以方便的知晓上面的类是承担什么的了。

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

    然而大家任然不知晓账户状态1,2,3到底是什么意思。

  2. 魔数
    在C#中幸免魔数大家一般选拔枚举来替换它们。这里运用AccountStatus 枚举来替换if-else中的魔数。
    public enum AccountStatus {   NotRegistered = 1,   SimpleCustomer = 2,   ValuableCustomer = 3,   MostValuableCustomer = 4 }
    今后大家来探视重构后的类,大家得以很轻巧的揭露哪三个账户状态应当用什么算法来计算折扣。混合账户状态的高风险飞速的下降了。

    public class DiscountManager
    {
        public decimal ApplyDiscount(decimal price, AccountStatus accountStatus, int timeOfHavingAccountInYears)
        {
            decimal priceAfterDiscount = 0;
            decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > 5) ? (decimal)5 / 100 : (decimal)timeOfHavingAccountInYears / 100;
    
            if (accountStatus == AccountStatus.NotRegistered)
            {
                priceAfterDiscount = price;
            }
            else if (accountStatus == AccountStatus.SimpleCustomer)
            {
                priceAfterDiscount = (price - (0.1m * price)) - (discountForLoyaltyInPercentage * (price - (0.1m * price)));
            }
            else if (accountStatus == AccountStatus.ValuableCustomer)
            {
                priceAfterDiscount = (0.7m * price) - (discountForLoyaltyInPercentage * (0.7m * price));
            }
            else if (accountStatus == AccountStatus.MostValuableCustomer)
            {
                priceAfterDiscount = (price - (0.5m * price)) - (discountForLoyaltyInPercentage * (price - (0.5m * price)));
            }
            return priceAfterDiscount;
        }
    }
    
  3. 更加多的代码可读性
    在这一步中,大家接纳switch-case 来替换 if-else
    if
    来抓牢代码可读性。
    同一时候,笔者还将一些长度十分短的语句才分成两行。比方:**priceAfterDiscount = (price - (0.5m * price)) - (discountForLoyaltyInPercentage * (price - (0.5m * price)));**
    被修改为:
    **priceAfterDiscount = (price - (0.5m * price));priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);**
    以下是完好的改造:

    public class DiscountManager
    {
        public decimal ApplyDiscount(decimal price, AccountStatus accountStatus, int timeOfHavingAccountInYears)
        {
            decimal priceAfterDiscount = 0;
            decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > 5) ? (decimal)5 / 100 : (decimal)timeOfHavingAccountInYears / 100;
            switch (accountStatus)
            {
                case AccountStatus.NotRegistered:
                    priceAfterDiscount = price;
                    break;
                case AccountStatus.SimpleCustomer:
                    priceAfterDiscount = (price - (0.1m * price));
                    priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
                    break;
                case AccountStatus.ValuableCustomer:
                    priceAfterDiscount = (0.7m * price);
                    priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
                    break;
                case AccountStatus.MostValuableCustomer:
                    priceAfterDiscount = (price - (0.5m * price));
                    priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
                    break;
            }
            return priceAfterDiscount;
        }
    }
    
  4. 撤销隐形的BUG
    正如大家事先提到的,大家的ApplyDiscount方法恐怕将为新的客商情形重回0。
    大家怎么着技术化解那几个主题材料?答案正是抛出NotImplementedException。
    当大家的诀窍获得账户状态作为输入参数,不过参数值恐怕富含我们未规划到的无人问津景况。那时,大家不能够怎么着也不做,抛出特别是此时最棒的做法。

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

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

         public class DiscountManager
        {
        public decimal ApplyDiscount(decimal price, AccountStatus     accountStatus, int timeOfHavingAccountInYears)
        {
        decimal priceAfterDiscount = 0;
        decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > 5) ? (decimal)5 / 100 : (decimal)timeOfHavingAccountInYears / 100;
        switch (accountStatus)
        {
            case AccountStatus.NotRegistered:
                priceAfterDiscount = price;
                break;
            case AccountStatus.SimpleCustomer:
                priceAfterDiscount = (price - (0.1m * price));
                priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
                break;
            case AccountStatus.ValuableCustomer:
                priceAfterDiscount = (price - (0.3m * price));
                priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
                break;
            case AccountStatus.MostValuableCustomer:
                priceAfterDiscount = (price - (0.5m * price));
                priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
                break;
            default:
                throw new NotImplementedException();
        }
        return priceAfterDiscount;
        }
        }
    
  1. 免去魔数的另一种艺术
    使用静态常量来替换魔数。0.1m,0.2m,0.3m,笔者m,大家并不知道它们是怎么看头。
    别的decimal discountForLoyaltyInPercentage =
    (timeOfHavingAccountInYears > 5) ? (decimal)5/100 :
    (decimal)timeOfHavingAccountInYears/100;
    中,数字5也要命神秘。
    大家亟须让它们更享有描述性,那时使用常量会是叁个比较好的措施。
    大家来定义二个静态类:

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

    随即修改DiscountManager类:

    public class DiscountManager
    {
    public decimal ApplyDiscount(decimal price, AccountStatus accountStatus, int timeOfHavingAccountInYears)
    {
        decimal priceAfterDiscount = 0;
        decimal discountForLoyaltyInPercentage = (timeOfHavingAccountInYears > Constants.MAXIMUM_DISCOUNT_FOR_LOYALTY) ? (decimal)Constants.MAXIMUM_DISCOUNT_FOR_LOYALTY / 100 : (decimal)timeOfHavingAccountInYears / 100;
        switch (accountStatus)
        {
            case AccountStatus.NotRegistered:
                priceAfterDiscount = price;
                break;
            case AccountStatus.SimpleCustomer:
                priceAfterDiscount = (price - (Constants.DISCOUNT_FOR_SIMPLE_CUSTOMERS * price));
                priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
                break;
            case AccountStatus.ValuableCustomer:
                priceAfterDiscount = (price - (Constants.DISCOUNT_FOR_VALUABLE_CUSTOMERS * price));
                priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
                break;
            case AccountStatus.MostValuableCustomer:
                priceAfterDiscount = (price - (Constants.DISCOUNT_FOR_MOST_VALUABLE_CUSTOMERS * price));
                priceAfterDiscount = priceAfterDiscount - (discountForLoyaltyInPercentage * priceAfterDiscount);
                break;
            default:
                throw new NotImplementedException();
        }
        return priceAfterDiscount;
    }
    }
    
  2. 铲除重复的代码
    为了消除重复的代码,这里将一部分算法提收取来。首先,我们建设构造多个增加方法:

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

    经过措施名称,大家就足以精晓它的任务是怎么样,现在修改我们的例子:

    public class DiscountManager
    {
    public decimal ApplyDiscount(decimal price, AccountStatus accountStatus, int timeOfHavingAccountInYears)
    {
        decimal priceAfterDiscount = 0;
        switch (accountStatus)
        {
            case AccountStatus.NotRegistered:
                priceAfterDiscount = price;
                break;
            case AccountStatus.SimpleCustomer:
                priceAfterDiscount = price.ApplyDiscountForAccountStatus(Constants.DISCOUNT_FOR_SIMPLE_CUSTOMERS)
                  .ApplyDiscountForTimeOfHavingAccount(timeOfHavingAccountInYears);
                break;
            case AccountStatus.ValuableCustomer:
                priceAfterDiscount = price.ApplyDiscountForAccountStatus(Constants.DISCOUNT_FOR_VALUABLE_CUSTOMERS)
                  .ApplyDiscountForTimeOfHavingAccount(timeOfHavingAccountInYears);
                break;
            case AccountStatus.MostValuableCustomer:
                priceAfterDiscount = price.ApplyDiscountForAccountStatus(Constants.DISCOUNT_FOR_MOST_VALUABLE_CUSTOMERS)
                  .ApplyDiscountForTimeOfHavingAccount(timeOfHavingAccountInYears);
                break;
            default:
                throw new NotImplementedException();
        }
        return priceAfterDiscount;
    }
    }
    
  3. 剔除没用的代码
    大家理应写出简约的代码,因为简短的代码=减少BUG爆发的机率,并且也让我们缩小精通事情逻辑的时间。
    大家开掘,这里三种情景的客户调用了同等的点子:
    .ApplyDiscountForTimeOfHavingAccount(timeOfHavingAccountInYears);
    这里能够统一代码:

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

    9.最后,获得根本的代码
    最终,让大家透过引进依赖注入和工厂方法情势来赢得最后的本子吧。
    先来看卡最后结果:

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

    先是,大家摆脱了扩展方法(静态类),倘使大家想对ApplyDiscount方式开展单元测量检验是比较困难的,取消大家对普赖斯Extensions扩充类也展开测量检验。
    为了幸免这么些主题材料,大家成立了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.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 语句,来增Gavin章的可读性。

同期,笔者也将一个相当短的一个钱打二十四个结办法拆分为两句话来写。未来我们将“
通过账户状态来计量折扣率”与“通过账户定时来总计折扣率”这两边分别来计量。

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

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

那正是修改后的代码:

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

发表评论

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