[翻译]C# BAD PRACTICES: Learn how to make a good code by bad example—C#:如何将坏的代码重新编写翻译为好的代码

友善的题词表达:

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

本连串还会有此外小说,后续将慢慢翻译。

 

引言:

本人的名字叫Radoslaw
Sadowski,小编今天是一个微软技能开垦人士。作者从起头专业时就意气风发直接触的微软本领.

在做事一年后,作者看见的材质相当糟糕的代码的数额基本上都足以写成一整本书了。

那几个资历让自身成为了二个想要清洁代码的自闭症伤者。

写那篇文章的目的是为着通过呈现品质相当差的类的事例来表明怎样下笔出到底的、可延长的和可敬性格很顽强在荆棘载途或巨大压力面前不屈的代码。小编会通过好的书写格局和设计格局来证明坏的代码带给的主题素材,以至替换他的好的解决情势。

第生龙活虎有个别是照准这一个负有C#幼功知识的开垦职员——我博览会示一些不认为奇的大错特错,然后再呈现一些让代码变得可读性的方法与手艺。高档部分重要针对这个最少存有设计情势概念的开采职员——作者将展会示完全通透到底的、单元可测量试验的代码。

为了能够领悟那篇小说你必要最少驾驭以下多个部分的基本知识:

  • C#语言
  • 借助注入、工厂设计形式、战略设计情势

本文中所涉及的例证都将会是切实中实实在在的现实性的天性,并不是用装饰情势来做披萨大概用政策方式来做总计器那样的演示。

(ps解释:看过设计格局相关的书籍的人应该会了然多数那方面的图书都以用这种例子,只是为了扶植读者了然设计形式卡塔 尔(阿拉伯语:قطر‎

                           
  图片 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.取名格局-从源代码中我们得以连蒙带猜估算出来这几个总括办法和输出结果是什么。何况大家想要从这几个类中领取总括算法将会是风华正茂件特别狼狈的政工。

这么推动的侵蚀是:

最沉痛的主题素材是:浪费时间,

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

而与之有相像效劳的还应该有(只是变了二个参数而已卡塔 尔(英语:State of Qatar):disc * (amount –
(0.5m * amount))

在这里多少个算术中,唯生龙活虎的界别就只是叁个静态参数,而笔者辈全然能够用二个可变的参数来顶替。

假如大家不试着在写代码的时候从第一手ctri+c,ctrl+v中蝉衣出来,那大家将碰着的标题就是我们只可以改良代码中的部分机能,因为我们不明了有些许地方需求改善。下面的逻辑是测算出在大家系统中各样客商对应年限获得的折扣,所以假诺大家只是贸然改进两到三处,比较轻巧以致别的地点的左右不均等。

7.每一种类具备太多的复杂的权利区域

作者们写的类最少背负了四个义务:

  1. 选料计算的运算法则
  2. 为每种分化景观的账户总计折扣率
  3. 基于各种客人的年限总结出相应的折扣率

本条背离了十足义务标准。那么那会带给什么样损害呢?假设大家想要改造上诉3个特征中的多少个,那就代表大概会碰触到一些任何的我们并不想修正的特征。所以在修正的时候我们只能再度测验全部的类,那么那就导致了十分重的岁月的浪费。

那就从头重构吧…

在接下去的9个步骤中自小编将向你出示大家怎么幸免上诉难点来构建三个干净的易维护,同期又有扶持单元测量检验的看起来一句话来说的代码。

 

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代表着哪些,那就持续往下啊!

II:魔法数

在C#中制止现身不了然的魔法数的主意是因而枚举来顶替。小编通过枚举方法来代替在if-else if 语句中现身的代替账户状态的法力数。

1 public enum AccountStatus
2 {
3   NotRegistered = 1,
4   SimpleCustomer = 2,
5   ValuableCustomer = 3,
6   MostValuableCustomer = 4
7 }

当今在看大家重构了的类,大家得以相当轻便的揭露那三个总结准绳是用来依据不用状态来计量折扣率的。将账户状态弄混的可能率就小幅裁减了。

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

III:更加的多的可读性

在此一步中大家将通过将if-else
if
 语句改为switch-case 语句,来扩充任品的可读性。

并且,作者也将二个相当短的计算格局拆分为两句话来写。今后我们将“
通过账户状态来计算折扣率”与“通过账户定时来计量折扣率”这两个分别来测算。

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

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

那正是纠正后的代码:

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

IV:未有明显的bug

大家好不轻巧找到大家掩盖的bug了!

因为自身刚巧提到的我们的主意中对于不合乎的账户状态会在形成对于有着商品最终都重回0。纵然特不好,但却是真的。

那大家该怎么修复那几个标题吗?那就唯有由此未有不当提醒了。

图片 6

您是否会想,那个会不会是付出的不等,应该不会被交付到不当提醒中去?不,他会的!

当我们的方法通过获得账户状态作为参数的时候,大家并不想程序让我们不足预感的大势前行,形成不可预测的失误。

 这种境况是相对不容许现身的,所以大家亟须透过抛出十三分来防护这种情景。

下边包车型大巴代码正是经过抛出特别后改进的以幸免现身不满意条件的情况-修改章程是将抛出格外防止 switch-case语句中的default 句中。

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

V:解析总计办法

在大家的例子中大家有四个概念给客户的折扣率的正规:

  1. 账户状态;
  2. 账户在我们系统中存在的期限

对此年限的推断折扣率的措施,全部的计量办法都有一点肖似:

(discountForLoyaltyInPercentage * priceAfterDiscount)

当然,也照旧存在分歧的:0.7m * price

由此大家把这几个改成那样:price – (0.3m * price)

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

到现在大家将整合治理全数通过账户状态的精兵简政办法改为相近种格式:price –
((static_discount_in_percentages/100) * price)

VI:通过此外措施再脱位法力数

接下去让大家的眼神放在通过账户状态总括折扣率的测算方法中的静态变量:(static_discount_in_percentages/100)

下一场带入上边数字间隔试试:0.1m,0.3m,0.5m

那些数字其实也是风流倜傥种类型的法力数-他们也未尝平昔报告大家他们意味着着怎么。

咱俩也许有平等的气象,举例将“有账户的时光”折价为“忠诚折扣”。

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

数字5让我们的代码变得神秘了起来。

小编们不得不做些什么让这些变得更具表现性。

自家会用别的生龙活虎种艺术来制止法力数的表明的现身-也正是C#中的常量(关键词是const卡塔尔国,小编刚强建议在大家的应用程序中特意定义一个静态类来囤积那么些常量。

在大家的例子中,作者是创造了上面包车型客车类:

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

通过一定的改过,大家的DiscountManager类就改成了那般了:

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

作者期望您也承认本人那么些艺术会越加使代码本身变得更具备表明性:)

VII:不要再另行啦!

图片 7

 

我们得以经过分拆算法的不二等秘书籍来运动我们的酌量方法,而不是单纯简单的复制代码。

大家会透过增添方法。

首先我们会成立三个扩充方法。

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

在写代码的时候条件上是大家的代码越是简练越好。精简的代码的表示,越少的荒诞的或许性,在翻阅精晓代码逻辑的时候花销的时间越少。

故此未来领头精练大家的代码吧。

我们得以专断开掘大家两种顾客账户下具备相近的艺术:

.ApplyDiscountForTimeOfHavingAccount(timeOfHavingAccountInYears);

咱俩好还是不佳只写贰遍啊?大家早先将未注册的顾客放在了抛出至极中,因为大家的折扣率只会估计注册顾客的限制期限,并不曾给未注册客商留有功效设定。所以,我们应有给未注册客户设定的年BlackBerry多少呢?
-0年

那即是说相应的折扣率也将造成0了,那样大家就能够优哉游哉的将折扣率交付给未注册顾客选拔了,这就起来吧!

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

咱俩还足以将那风流浪漫行移除到switch-case语句外面。好处就是:越来越少的代码量!

IX:进步-最终的拿走根本清洁的代码

好了,今后我们得以像阅读一本书同样方便来审视大家的代码了,不过这就够了么?大家能够将代码变得最棒轻松的!

图片 8

好的,那就起来做一些改观来兑现那一个目的吧。大家能够使用信任注入和使用政策形式这两种办法。

那正是我们明天最后收拾出来的代码了:

 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 }

首先大家脱位了扩大方法(约等于静态类卡塔 尔(英语:State of Qatar),之所以要解脱这种是因为扩充方法与折扣计算办法之间存在了紧耦合的涉嫌。如若大家想要单元测量试验大家的办法ApplyDiscount的时候将变得不太轻便,因为大家必得统生机勃勃测量试验与之牢牢关系的类PriceExtensions。

为了制止这么些,小编创造了DefaultLoyaltyDiscountCalculator 类,那此中含有了ApplyDiscountForTimeOfHavingAccount增添方法,同事本身经过架空中接力口ILoyaltyDiscountCalculator藏匿了他的现实贯彻。以往,当本人想测验大家的类DiscountManager的时候,作者就足以因而 ILoyaltyDiscountCalculator宪章注入伪造对象到DiscountManager类中通过构造函数呈现测验成效。这里大家应用的就叫信任注入格局。

图片 9

在做这么些的还要,大家也将总计折扣率这几个作用安全的移交到另一个分化的类中,假使我们想要修正那豆蔻梢头段的逻辑,那我们就只供给修改DefaultLoyaltyDiscountCalculator** **类就好了,而没有须要退换别的的地点,那样降低了在退换他的时候产生破坏其余地点的高危害,同期也无需再充实单独测量检验的岁月了。

上面是大家在DiscountManager类中应用分其余逻辑类:

priceAfterDiscount =
_loyaltyDiscountCalculator.ApplyDiscount(priceAfterDiscount,
timeOfHavingAccountInYears);

为了针对账户状态的逻辑来计量折扣率,笔者创立了风姿洒脱部分比较复杂的东西。大家在DiscountManager类中有多少个义务需求表明出去。

  1. 轶事账户状态如何抉择相应的精兵简政方式。
  2. 破例总结办法的内部情形

为了将率先个职责移交出去,小编成立了工厂类(DefaultAccountDiscountCalculatorFactory卡塔尔国,为了促成工厂情势,然后再把那些掩盖到虚幻IAccountDiscountCalculatorFactory里面去。

图片 10

大家的厂子会决定取舍哪一种总结方法。最终大家透过重视注册格局构造函数将工厂方式注射到DiscountManager类中

上边便是接纳了工厂的DiscountManager类:

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

 以上会针对区别的账户状态重临曾几何时的战术,然后调用ApplyDiscount 方法。

首先个权利已经被接入出去了,接下去就是第四个了。

 接下来大家就初叶研讨攻略了…..

图片 11

因为区别的账户状态会有永不的折扣总括办法,所以我们需求分裂的兑现政策。座椅非常适用于政策方式。

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

NotRegisteredDiscountCalculator SimpleCustomerDiscountCalculator MostValuableCustomerDiscountCalculator**

他们带有了实际的折扣总结办法的兑现并被藏在了说梅止渴IAccountDiscountCalculator里。

那就同意大家的类DiscountManager利用优良的国策,而无需通晓具体的贯彻。我们的类只要求明白与ApplyDiscount方法相关的IAccountDiscountCalculator 接口重临的目的的品类。

NotRegisteredDiscountCalculator, SimpleCustomerDiscountCalculator,
MostValuableCustomerDiscountCalculator
这一个类饱含了现实的通过账户状态接受契合总计的测算格局的兑现。因为大家的那多少个政策看起来相近,我们唯黄金时代能做的大半就唯有针对那三种计算战略创设三个主意然后各类计谋类通过三个并不是的参数来调用她。因为那会让大家的代码变得非常多,所以作者今天调控不这么做了。

好了,到近来停止我们的代码变得可读了,并且每个类都唯有一个职分了-那样校订他的时候会单唯风度翩翩后生可畏对应了:

  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 }

总结

在本文中,代码被Infiniti简化了,使得全部的技艺和形式的分解更易于了。它显得了怎样减轻相近的编制程序难点,以致选拔优越的实践和设计方式以合适、干净的格局解决那么些题指标裨益。

在自己的劳作经历中,小编频仍在这里篇文章中强调了蹩脚的做法。它们分明存在于广大接受地方,并非在贰个类中,如在自家的例子中那么,那使得开掘它们进一层不便,因为它们隐蔽在适龄的代码之间。写这种代码的人连连争论说,他们根据的是大约愚钝的平整。不幸的是,差十分少全数的系列都在成年人,变得特别复杂。然后,这几个轻便的、不可扩展的代码中的各种改动都甚特别重大的,并且拉动了宏伟的风险。

请记住,您的代码将长期存在于临蓐条件中,并就要各类事业供给变动上拓宽修正。因而编写过于简短、不可扩展的代码异常快就能够产生严重的结局。最终一点是对开垦人士有利,特别是那多少个在您和谐事后维护你的代码。

若是你有后生可畏部分难题依据作品不要犹豫联系本人!

图片 12PS:

据书上说随笔敲的源代码:

链接: 密码:9spz

相关文章

发表评论

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

*
*
Website