四虎免费最新在线永久4HU,中文字幕无码AV激情不卡,国产精品成人免费视频一区,天天躁狠狠躁狠狠躁性色AV

南京北大青鳥(niǎo)

全國(guó)咨詢(xún)電話:15195455103

三分鐘了解北大青鳥(niǎo)
當(dāng)前位置:南京北大青鳥(niǎo) > 學(xué)習(xí)園地 > 編程技巧

程序員須知的4個(gè)寫(xiě)代碼技巧

來(lái)源:南京北大青鳥(niǎo)? ? ? 作者:南京北大青鳥(niǎo) ? ??

如果現(xiàn)在要求對(duì)你寫(xiě)的Java代碼進(jìn)行優(yōu)化,那你會(huì)怎么做呢?作者在本文介紹了可以提高系統(tǒng)性能以及代碼可讀性的四種方法,如果你對(duì)此感興趣,就讓我們一起來(lái)看看吧。 我們平時(shí)的

如果現(xiàn)在要求對(duì)你寫(xiě)的Java代碼進(jìn)行優(yōu)化,那你會(huì)怎么做呢?作者在本文介紹了可以提高系統(tǒng)性能以及代碼可讀性的四種方法,如果你對(duì)此感興趣,就讓我們一起來(lái)看看吧。

我們平時(shí)的編程任務(wù)不外乎就是將相同的技術(shù)套件應(yīng)用到不同的項(xiàng)目中去,對(duì)于大多數(shù)情況來(lái)說(shuō),這些技術(shù)都是可以滿(mǎn)足目標(biāo)的。然而,有的項(xiàng)目可能需要用到一些特別的技術(shù),因此工程師們得深入研究,去尋找那些簡(jiǎn)單但有效的方法。在以前一篇文章中,我們討論了必要時(shí)可以使用的四種特殊技術(shù),這些特殊技術(shù)可以創(chuàng)建更好的Java軟件;而本文我們將介紹一些有助于解決常見(jiàn)問(wèn)題的通用設(shè)計(jì)策略和目標(biāo)實(shí)現(xiàn)技術(shù),即:

只做有目的性的優(yōu)化

常量盡量使用枚舉

重新定義類(lèi)里面的equals()方法

盡量多使用多態(tài)性

值得注意的是,本文中描述的技術(shù)并不是適用于所有情況。另外這些技術(shù)應(yīng)該什么時(shí)候使用以及在什么地方使用,都是需要使用者經(jīng)過(guò)深思熟慮的。

1 .只做有目的性的優(yōu)化

大型軟件系統(tǒng)肯定非常關(guān)注性能問(wèn)題。雖然我們希望能夠?qū)懗龈咝У拇a,但很多時(shí)候,如果想對(duì)代碼進(jìn)行優(yōu)化,我們卻無(wú)從下手。例如,下面的這段代碼會(huì)影響到性能嗎?

?
1
2
3
4
5
6
7
8
public void processIntegers(List<Integer> integers) {
 
  for (Integer value: integers) {
    for (int i = integers.size() - 1; i >= 0; i--) {
      value += integers.get(i);
    }
  }
}

這就得視情況而定了。上面這段代碼可以看出它的處理算法是O(n³)(使用大O符號(hào)),其中n是list集合的大小。如果n只有5,那么就不會(huì)有問(wèn)題,只會(huì)執(zhí)行25次迭代。但如果n是10萬(wàn),那可能會(huì)影響性能了。請(qǐng)注意,即使這樣我們也不能判定肯定會(huì)有問(wèn)題。盡管此方法需要執(zhí)行10億次邏輯迭代,但會(huì)不會(huì)對(duì)性能產(chǎn)生影響仍然有待討論。

例如,假設(shè)客戶(hù)端是在它自己的線程中執(zhí)行這段代碼,并且異步等待計(jì)算完成,那么它的執(zhí)行時(shí)間有可能是可以接受的。同樣,如果系統(tǒng)部署在了生產(chǎn)環(huán)境上,但是沒(méi)有客戶(hù)端進(jìn)行調(diào)用,那我們根本沒(méi)必要去對(duì)這段代碼進(jìn)行優(yōu)化,因?yàn)閴焊筒粫?huì)消耗系統(tǒng)的整體性能。事實(shí)上,優(yōu)化性能以后系統(tǒng)會(huì)變得更加復(fù)雜,悲劇的是系統(tǒng)的性能卻沒(méi)有因此而提高。

重要的是天下沒(méi)有mf的午餐,因此為了降低代價(jià),我們通常會(huì)通過(guò)類(lèi)似于緩存、循環(huán)展開(kāi)或預(yù)計(jì)算值這類(lèi)技術(shù)去實(shí)現(xiàn)優(yōu)化,這樣反而增加了系統(tǒng)的復(fù)雜性,也降低了代碼的可讀性。如果這種優(yōu)化可以提高系統(tǒng)的性能,那么即使變得復(fù)雜,那也是值得的,但是做決定之前,必須首先知道這兩條信息:

性能要求是什么

性能瓶頸在哪里

首先我們需要清楚地知道性能要求是什么。如果終是在要求以?xún)?nèi),并且終用戶(hù)也沒(méi)有提出什么異議,那么就沒(méi)有必要進(jìn)行性能優(yōu)化。但是,當(dāng)添加了新功能或者系統(tǒng)的數(shù)據(jù)量達(dá)到一定規(guī)模以后就必須進(jìn)行優(yōu)化了,否則可能會(huì)出現(xiàn)問(wèn)題。

在這種情況下,不應(yīng)該靠直覺(jué),也不應(yīng)該依靠檢查。因?yàn)榧词故窍馦artin Fowler這樣有經(jīng)驗(yàn)的開(kāi)發(fā)人員也容易做一些錯(cuò)誤的優(yōu)化,正如在重構(gòu)(第70頁(yè))一文中解釋的那樣:

如果分析了足夠多的程序以后,你會(huì)發(fā)現(xiàn)關(guān)于性能的有趣之處在于,大部分時(shí)間都浪費(fèi)在了系統(tǒng)中的一小部分代碼中里面。如果對(duì)所有代碼進(jìn)行了同樣的優(yōu)化,那么終結(jié)果就是浪費(fèi)了90%的優(yōu)化,因?yàn)閮?yōu)化過(guò)以后的代碼運(yùn)行得頻率并不多。因?yàn)闆](méi)有目標(biāo)而做的優(yōu)化所耗費(fèi)的時(shí)間,都是在浪費(fèi)時(shí)間。

作為一名身經(jīng)百戰(zhàn)的開(kāi)發(fā)人員,我們應(yīng)該認(rèn)真對(duì)待這一觀點(diǎn)。第一次猜測(cè)不僅沒(méi)有提高系統(tǒng)的性能,而且90%的開(kāi)發(fā)時(shí)間完全是浪費(fèi)了。相反,我們應(yīng)該在生產(chǎn)環(huán)境(或者預(yù)生產(chǎn)環(huán)境中)執(zhí)行常見(jiàn)用例,并找出在執(zhí)行過(guò)程中是哪部分在消耗系統(tǒng)資源,然后對(duì)系統(tǒng)進(jìn)行配置。例如消耗大部分資源的代碼只占了10%,那么優(yōu)化其余90%的代碼就是浪費(fèi)時(shí)間。

根據(jù)分析結(jié)果,要想使用這些知識(shí),我們應(yīng)該從常見(jiàn)的情況入手。因?yàn)檫@將確保實(shí)際付出的努力終是可以提高系統(tǒng)的性能。每次優(yōu)化后,都應(yīng)該重復(fù)分析步驟。因?yàn)檫@不僅可以確保系統(tǒng)的性能真的得到了改善,也可以看出再對(duì)系統(tǒng)進(jìn)行優(yōu)化后,性能瓶頸是在哪個(gè)部分(因?yàn)榻鉀Q完一個(gè)瓶頸以后,其它瓶頸可能消耗系統(tǒng)更多的整體資源)。需要注意的是,在現(xiàn)有瓶頸中花費(fèi)的時(shí)間百分比很可能會(huì)增加,因?yàn)槭O碌钠款i是暫時(shí)不變的,而且隨著目標(biāo)瓶頸的消除,整個(gè)執(zhí)行時(shí)間應(yīng)該會(huì)減少。

盡管在Java系統(tǒng)中想要對(duì)概要文件進(jìn)行全面檢查需要很大的容量,但是還是有一些很常見(jiàn)的工具可以幫助發(fā)現(xiàn)系統(tǒng)的性能熱點(diǎn),這些工具包括JMeter、AppDynamics和YourKit。另外,還可以參見(jiàn)DZone的性能監(jiān)測(cè)指南,獲取更多關(guān)于Java程序性能優(yōu)化的信息。

雖然性能是許多大型軟件系統(tǒng)一個(gè)非常重要的組成部分,也成為產(chǎn)品交付管道中自動(dòng)化測(cè)試套件的一部分,但是還是不能夠盲目的且沒(méi)有目的的進(jìn)行優(yōu)化。相反,應(yīng)該對(duì)已經(jīng)掌握的性能瓶頸進(jìn)行特定的優(yōu)化。這不僅可以幫助我們避免增加了系統(tǒng)的復(fù)雜性,而且還讓我們少走彎路,不去做那些浪費(fèi)時(shí)間的優(yōu)化。

2.常量盡量使用枚舉

需要用戶(hù)列出一組預(yù)定義或常量值的場(chǎng)景有很多,例如在web應(yīng)用程序中可能遇到的HTTP響應(yīng)代碼。常見(jiàn)的實(shí)現(xiàn)技術(shù)之一是新建類(lèi),該類(lèi)里面有很多靜態(tài)的final類(lèi)型的值,每個(gè)值都應(yīng)該有一句注釋?zhuān)枋鲈撝档暮x是什么:

?
1
2
3
4
5
6
7
8
public class HttpResponseCodes {
  public static final int OK = 200;
  public static final int NOT_FOUND = 404;
  public static final int FORBIDDEN = 403;
}
if (getHttpResponse().getStatusCode() == HttpResponseCodes.OK) {
  // Do something if the response code is OK
}

能夠有這種思路就已經(jīng)非常好了,但這還是有一些缺點(diǎn):

沒(méi)有對(duì)傳入的整數(shù)值進(jìn)行嚴(yán)格的校驗(yàn)

由于是基本數(shù)據(jù)類(lèi)型,因此不能調(diào)用狀態(tài)代碼上的方法

在第一種情況下只是簡(jiǎn)單的創(chuàng)建了一個(gè)特定的常量來(lái)表示特殊的整數(shù)值,但并沒(méi)有對(duì)方法或變量進(jìn)行限制,因此使用的值可能會(huì)超出定義的范圍。例如:

?
1
2
3
4
5
6
public class HttpResponseHandler {
  public static void printMessage(int statusCode) {
    System.out.println("Recieved status of " + statusCode);
  }
}
HttpResponseHandler.printMessage(15000);

盡管15000并不是有效的HTTP響應(yīng)代碼,但是由于服務(wù)器端也沒(méi)有限制客戶(hù)端必須提供有效的整數(shù)。在第二種情況下,我們沒(méi)有辦法為狀態(tài)代碼定義方法。例如,如果想要檢查給定的狀態(tài)代碼是否是一個(gè)成功的代碼,那就必須定義一個(gè)單獨(dú)的函數(shù):

?
1
2
3
4
5
6
7
8
9
10
11
public class HttpResponseCodes {
  public static final int OK = 200;
  public static final int NOT_FOUND = 404;
  public static final int FORBIDDEN = 403;
  public static boolean isSuccess(int statusCode) {
    return statusCode >= 200 && statusCode < 300;
  }
}
if (HttpResponseCodes.isSuccess(getHttpResponse().getStatusCode())) {
  // Do something if the response code is a success code
}

為了解決這些問(wèn)題,我們需要將常量類(lèi)型從基本數(shù)據(jù)類(lèi)型改為自定義類(lèi)型,并只允許自定義類(lèi)的特定對(duì)象。這正是Java枚舉(enum)的用途。使用enum,我們可以一次性解決這兩個(gè)問(wèn)題:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public enum HttpResponseCodes {
  OK(200),
  FORBIDDEN(403),
  NOT_FOUND(404);
  private final int code;
  HttpResponseCodes(int code) {
    this.code = code;
  }
  public int getCode() {
    return code;
  }
  public boolean isSuccess() {
    return code >= 200 && code < 300;
  }
}
if (getHttpResponse().getStatusCode().isSuccess()) {
  // Do something if the response code is a success code
}

同樣,現(xiàn)在還可以要求在調(diào)用方法的時(shí)候提供必須有效的狀態(tài)代碼:

?
1
2
3
4
5
6
public class HttpResponseHandler {
  public static void printMessage(HttpResponseCode statusCode) {
    System.out.println("Recieved status of " + statusCode.getCode());
  }
}
HttpResponseHandler.printMessage(HttpResponseCode.OK);

值得注意的是,舉這個(gè)例子事項(xiàng)說(shuō)明如果是常量,則應(yīng)該盡量使用枚舉,但并不是說(shuō)什么情況下都應(yīng)該使用枚舉。在某些情況下,可能希望使用一個(gè)常量來(lái)表示某個(gè)特殊值,但是也允許提供其它的值。例如,大家可能都知道圓周率,我們可以用一個(gè)常量來(lái)捕獲這個(gè)值(并重用它):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class NumericConstants {
  public static final double PI = 3.14;
  public static final double UNIT_CIRCLE_AREA = PI * PI;
}
public class Rug {
  private final double area;
  public class Run(double area) {
    this.area = area;
  }
  public double getCost() {
    return area * 2;
  }
}
// Create a carpet that is 4 feet in diameter (radius of 2 feet)
Rug fourFootRug = new Rug(2 * NumericConstants.UNIT_CIRCLE_AREA);

因此,使用枚舉的規(guī)則可以歸納為:

當(dāng)所有可能的離散值都已經(jīng)提前知道了,那么就可以使用枚舉

再拿上文中所提到的HTTP響應(yīng)代碼為例,我們可能知道HTTP狀態(tài)代碼的所有值(可以在RFC 7231中找的到,它定義了HTTP 1.1協(xié)議)。因此使用了枚舉。在計(jì)算圓周率的情況下,我們不知道關(guān)于圓周率的所有可能值(任何可能的double都是有效的),但同時(shí)又希望為圓形的rugs創(chuàng)建一個(gè)常量,使計(jì)算更容易(更容易閱讀);因此定義了一系列常量。

如果不能提前知道所有可能的值,但是又希望包含每個(gè)值的字段或方法,那么簡(jiǎn)單的方法就是可以新建一個(gè)類(lèi)來(lái)表示數(shù)據(jù)。盡管沒(méi)有說(shuō)過(guò)什么場(chǎng)景應(yīng)該絕對(duì)不用枚舉,但要想知道在什么地方、什么時(shí)間不使用枚舉的關(guān)鍵是提前意識(shí)到所有的值,并且禁止使用其他任何值。

3.重新定義類(lèi)里面的equals()方法

對(duì)象識(shí)別可能是一個(gè)很難解決的問(wèn)題:如果兩個(gè)對(duì)象在內(nèi)存中占據(jù)相同的位置,那么它們是相同的嗎?如果它們的id相同,它們是相同的嗎?或者如果所有的字段都相等呢?雖然每個(gè)類(lèi)都有自己的標(biāo)識(shí)邏輯,但是在系統(tǒng)中有很多西方都需要去判斷是否相等。例如,有如下的一個(gè)類(lèi),表示訂單購(gòu)買(mǎi)…

?
1
2
3
4
5
6
7
8
9
public class Purchase {
  private long id;
  public long getId() {
    return id;
  }
  public void setId(long id) {
    this.id = id;
  }
}

……就像下面寫(xiě)的這樣,代碼中肯定有很多地方都是類(lèi)似于的:

?
1
2
3
4
5
Purchase originalPurchase = new Purchase();
Purchase updatedPurchase = new Purchase();
if (originalPurchase.getId() == updatedPurchase.getId()) {
  // Execute some logic for equal purchases
}

這些邏輯調(diào)用的越多(反過(guò)來(lái),違背了DRY原則),Purchase

類(lèi)的身份信息也會(huì)變得越來(lái)越多。如果出于某種原因,更改了Purchase

類(lèi)的身份邏輯(例如,更改了標(biāo)識(shí)符的類(lèi)型),則需要更新標(biāo)識(shí)邏輯所在的位置肯定也非常多。

我們應(yīng)該在類(lèi)的內(nèi)部初始化這個(gè)邏輯,而不是通過(guò)系統(tǒng)將Purchase類(lèi)的身份邏輯進(jìn)行過(guò)多的傳播。乍一看,我們可以創(chuàng)建一個(gè)新的方法,比如isSame,這個(gè)方法的入?yún)⑹且粋€(gè)Purchase對(duì)象,并對(duì)每個(gè)對(duì)象的id進(jìn)行比較,看看它們是否相同:

?
1
2
3
4
5
6
public class Purchase {
  private long id;
  public boolean isSame(Purchase other) {
    return getId() == other.gerId(); 
  }
}

雖然這是一個(gè)有效的解決方案,但是忽略了Java的內(nèi)置功能:使用equals方法。Java中的每個(gè)類(lèi)都是繼承了Object類(lèi),雖然是隱式的,因此同樣也就繼承了equals方法。默認(rèn)情況下,此方法將檢查對(duì)象標(biāo)識(shí)(內(nèi)存中相同的對(duì)象),如JDK中的對(duì)象類(lèi)定義(version 1.8.0_131)中的以下代碼片段所示:

?
1
2
3
public boolean equals(Object obj) {
return (this == obj);
}

這個(gè)equals方法充當(dāng)了注入身份邏輯的自然位置(通過(guò)覆蓋默認(rèn)的equals實(shí)現(xiàn)):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Purchase {
  private long id;
  public long getId() {
    return id;
  }
  public void setId(long id) {
    this.id = id;
  }
  @Override
  public boolean equals(Object other) {
    if (this == other) {
      return true;
    }
    else if (!(other instanceof Purchase)) {
      return false;
    }
    else {
      return ((Purchase) other).getId() == getId();
    }
  }
}

雖然這個(gè)equals方法看起來(lái)很復(fù)雜,但由于equals方法只接受類(lèi)型對(duì)象的參數(shù),所以我們只需要考慮三個(gè)案例:

另一個(gè)對(duì)象是當(dāng)前對(duì)象(即originalPurchase.equals(originalPurchase)),根據(jù)定義,它們是同一個(gè)對(duì)象,因此返回true
另一個(gè)對(duì)象不是Purchase對(duì)象,在這種情況下,我們無(wú)法比較Purchase的id,因此,這兩個(gè)對(duì)象不相等

其他對(duì)象不是同一個(gè)對(duì)象,但卻是Purchase的實(shí)例,因此,是否相等取決于當(dāng)前Purchase的id和其他Purchase是否相等
現(xiàn)在可以重構(gòu)我們之前的條件,如下:

?
1
2
3
4
5
Purchase originalPurchase = new Purchase();
Purchase updatedPurchase = new Purchase();
if (originalPurchase.equals(updatedPurchase)) {
  // Execute some logic for equal purchases
}

除了可以在系統(tǒng)中減少?gòu)?fù)制,重構(gòu)默認(rèn)的equals方法還有一些其它的優(yōu)勢(shì)。例如,如果構(gòu)造一個(gè)Purchase對(duì)象列表,并檢查列表是否包含具有相同ID(內(nèi)存中不同對(duì)象)的另一個(gè)Purchase對(duì)象,那么我們就會(huì)得到true值,因?yàn)檫@兩個(gè)值被認(rèn)為是相等的:

?
1
2
3
List<Purchase> purchases = new ArrayList<>();
purchases.add(originalPurchase);
purchases.contains(updatedPurchase); // True

通常,無(wú)論在什么地方,如果需要判斷兩個(gè)類(lèi)是否相等,則只需要使用重寫(xiě)過(guò)的equals方法就可以了。如果希望使用由于繼承了Object對(duì)象而隱式具有的equals方法去判斷相等性,我們還可以使用= =操作符,如下:

?
1
2
3
if (originalPurchase == updatedPurchase) {
  // The two objects are the same objects in memory
}

還需要注意的是,當(dāng)equals方法被重寫(xiě)以后,hashCode方法也應(yīng)該被重寫(xiě)。有關(guān)這兩種方法之間關(guān)系的更多信息,以及如何正確定義hashCode

方法,請(qǐng)參見(jiàn)此線程。

正如我們所看到的,重寫(xiě)equals方法不僅可以將身份邏輯在類(lèi)的內(nèi)部進(jìn)行初始化,并在整個(gè)系統(tǒng)中減少了這種邏輯的擴(kuò)散,它還允許Java語(yǔ)言對(duì)類(lèi)做出有根據(jù)的決定。

4.盡量多使用多態(tài)性

對(duì)于任何一門(mén)編程語(yǔ)言來(lái)說(shuō),條件句都是一種很常見(jiàn)的結(jié)構(gòu),而且它的存在也是有一定原因的。因?yàn)椴煌慕M合可以允許用戶(hù)根據(jù)給定值或?qū)ο蟮乃矔r(shí)狀態(tài)改變系統(tǒng)的行為。假設(shè)用戶(hù)需要計(jì)算各銀行賬戶(hù)的余額,那么就可以開(kāi)發(fā)出以下的代碼:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public enum BankAccountType {
  CHECKING,
  SAVINGS,
  CERTIFICATE_OF_DEPOSIT;
}
public class BankAccount {
  private final BankAccountType type;
  public BankAccount(BankAccountType type) {
    this.type = type;
  }
  public double getInterestRate() {
    switch(type) {
      case CHECKING:
        return 0.03; // 3%
      case SAVINGS:
        return 0.04; // 4%
      case CERTIFICATE_OF_DEPOSIT:
        return 0.05; // 5%
      default:
        throw new UnsupportedOperationException();
    }
  }
  public boolean supportsDeposits() {
    switch(type) {
      case CHECKING:
        return true;
      case SAVINGS:
        return true;
      case CERTIFICATE_OF_DEPOSIT:
        return false;
      default:
        throw new UnsupportedOperationException();
    }
  }
}

雖然上面這段代碼滿(mǎn)足了基本的要求,但是有個(gè)很明顯的缺陷:用戶(hù)只是根據(jù)給定帳戶(hù)的類(lèi)型決定系統(tǒng)的行為。這不僅要求用戶(hù)每次要做決定之前都需要檢查賬戶(hù)類(lèi)型,還需要在做出決定時(shí)重復(fù)這個(gè)邏輯。例如,在上面的設(shè)計(jì)中,用戶(hù)必須在兩種方法都進(jìn)行檢查才可以。這就可能會(huì)出現(xiàn)失控的情況,特別是接收到添加新帳戶(hù)類(lèi)型的需求時(shí)。

我們可以使用多態(tài)來(lái)隱式地做出決策,而不是使用賬戶(hù)類(lèi)型用來(lái)區(qū)分。為了做到這一點(diǎn),我們將BankAccount的具體類(lèi)轉(zhuǎn)換成一個(gè)接口,并將決策過(guò)程傳入一系列具體的類(lèi),這些類(lèi)代表了每種類(lèi)型的銀行帳戶(hù):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/**
 * Java學(xué)習(xí)交流QQ群:589809992 我們一起學(xué)Java!
 */
public interface BankAccount {
  public double getInterestRate();
  public boolean supportsDeposits();
}
public class CheckingAccount implements BankAccount {
  @Override
  public double getIntestRate() {
    return 0.03;
  }
  @Override
  public boolean supportsDeposits() {
    return true;
  }
}
public class SavingsAccount implements BankAccount {
  @Override
  public double getIntestRate() {
    return 0.04;
  }
  @Override
  public boolean supportsDeposits() {
    return true;
  }
}
public class CertificateOfDepositAccount implements BankAccount {
  @Override
  public double getIntestRate() {
    return 0.05;
  }
  @Override
  public boolean supportsDeposits() {
    return false;
  }
}

這不僅將每個(gè)帳戶(hù)特有的信息封裝到了到自己的類(lèi)中,而且還支持用戶(hù)可以在兩種重要的方式中對(duì)設(shè)計(jì)進(jìn)行變化。首先,如果想要添加一個(gè)新的銀行帳戶(hù)類(lèi)型,只需創(chuàng)建一個(gè)新的具體類(lèi),實(shí)現(xiàn)了BankAccount的接口,給出兩個(gè)方法的具體實(shí)現(xiàn)就可以了。在條件結(jié)構(gòu)設(shè)計(jì)中,我們必須在枚舉中添加一個(gè)新值,在兩個(gè)方法中添加新的case語(yǔ)句,并在每個(gè)case語(yǔ)句下插入新帳戶(hù)的邏輯。

其次,如果我們希望在BankAccount接口中添加一個(gè)新方法,我們只需在每個(gè)具體類(lèi)中添加新方法。在條件設(shè)計(jì)中,我們必須復(fù)制現(xiàn)有的switch語(yǔ)句并將其添加到我們的新方法中。此外,我們還必須在每個(gè)case語(yǔ)句中添加每個(gè)帳戶(hù)類(lèi)型的邏輯。

在數(shù)學(xué)上,當(dāng)我們創(chuàng)建一個(gè)新方法或添加一個(gè)新類(lèi)型時(shí),我們必須在多態(tài)和條件設(shè)計(jì)中做出相同數(shù)量的邏輯更改。例如,如果我們?cè)诙鄳B(tài)設(shè)計(jì)中添加一個(gè)新方法,我們必須將新方法添加到所有n個(gè)銀行帳戶(hù)的具體類(lèi)中,而在條件設(shè)計(jì)中,我們必須在我們的新方法中添加n個(gè)新的case語(yǔ)句。如果我們?cè)诙鄳B(tài)設(shè)計(jì)中添加一個(gè)新的account類(lèi)型,我們必須在BankAccount接口中實(shí)現(xiàn)所有的m數(shù),而在條件設(shè)計(jì)中,我們必須向每個(gè)m現(xiàn)有方法添加一個(gè)新的case語(yǔ)句。

雖然我們必須做的改變的數(shù)量是相等的,但變化的性質(zhì)卻是完全不同的。在多態(tài)設(shè)計(jì)中,如果我們添加一個(gè)新的帳戶(hù)類(lèi)型并且忘記包含一個(gè)方法,編譯器會(huì)拋出一個(gè)錯(cuò)誤,因?yàn)槲覀儧](méi)有在我們的BankAccount接口中實(shí)現(xiàn)所有的方法。在條件設(shè)計(jì)中,沒(méi)有這樣的檢查,以確保每個(gè)類(lèi)型都有一個(gè)case語(yǔ)句。如果添加了新類(lèi)型,我們可以簡(jiǎn)單地忘記更新每個(gè)switch語(yǔ)句。這個(gè)問(wèn)題越嚴(yán)重,我們就越重復(fù)我們的switch語(yǔ)句。我們是人類(lèi),我們傾向于犯錯(cuò)誤。因此,任何時(shí)候,只要我們可以依賴(lài)編譯器來(lái)提醒我們錯(cuò)誤,我們就應(yīng)該這么做。

關(guān)于這兩種設(shè)計(jì)的第二個(gè)重要注意事項(xiàng)是它們?cè)谕獠渴堑韧?。例如,如果我們想要檢查一個(gè)支票帳戶(hù)的利率,條件設(shè)計(jì)就會(huì)類(lèi)似如下:

?
1
2
BankAccount checkingAccount = new BankAccount(BankAccountType.CHECKING);
System.out.println(checkingAccount.getInterestRate()); // Output: 0.03

相反,多態(tài)設(shè)計(jì)將類(lèi)似如下:

?
1
2
BankAccount checkingAccount = new CheckingAccount();
System.out.println(checkingAccount.getInterestRate()); // Output: 0.03

從外部的角度來(lái)看,我們只是在BankAccount對(duì)象上調(diào)用getintereUNK()。如果我們將創(chuàng)建過(guò)程抽象為一個(gè)工廠類(lèi)的話,這將更加明顯:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ConditionalAccountFactory {
  public static BankAccount createCheckingAccount() {
     return new BankAccount(BankAccountType.CHECKING);
  }
}
public class PolymorphicAccountFactory {
  public static BankAccount createCheckingAccount() {
     return new CheckingAccount();
  }
}
// In both cases, we create the accounts using a factory
BankAccount conditionalCheckingAccount = ConditionalAccountFactory.createCheckingAccount();
BankAccount polymorphicCheckingAccount = PolymorphicAccountFactory.createCheckingAccount();
// In both cases, the call to obtain the interest rate is the same
System.out.println(conditionalCheckingAccount.getInterestRate()); // Output: 0.03
System.out.println(polymorphicCheckingAccount.getInterestRate()); // Output: 0.03

將條件邏輯替換成多態(tài)類(lèi)是非常常見(jiàn)的,因此已經(jīng)發(fā)布了將條件語(yǔ)句重構(gòu)為多態(tài)類(lèi)的方法。這里就有一個(gè)簡(jiǎn)單的例子。此外,馬丁·福勒(Martin Fowler)的《重構(gòu)》(p . 255)也描述了執(zhí)行這個(gè)重構(gòu)的詳細(xì)過(guò)程。

就像本文中的其他技術(shù)一樣,對(duì)于何時(shí)執(zhí)行從條件邏輯轉(zhuǎn)換到多態(tài)類(lèi),沒(méi)有硬性規(guī)定。事實(shí)上,如論在何種情況下我們都是不建議使用。在測(cè)試驅(qū)動(dòng)的設(shè)計(jì)中:例如,Kent Beck設(shè)計(jì)了一個(gè)簡(jiǎn)單的貨幣系統(tǒng),目的是使用多態(tài)類(lèi),但發(fā)現(xiàn)這使設(shè)計(jì)過(guò)于復(fù)雜,于是便將他的設(shè)計(jì)重新設(shè)計(jì)成一個(gè)非多態(tài)風(fēng)格。經(jīng)驗(yàn)和合理的判斷將決定何時(shí)是將條件代碼轉(zhuǎn)換為多態(tài)代碼的合適時(shí)間。

結(jié)束語(yǔ)

作為程序員,盡管平常所使用的常規(guī)技術(shù)可以解決大部分的問(wèn)題,但有時(shí)我們應(yīng)該打破這種常規(guī),主動(dòng)需求一些創(chuàng)新。畢竟作為一名開(kāi)發(fā)人員,擴(kuò)展自己知識(shí)面的的廣度和深度,不僅能讓我們做出更明智的決定,也能讓我們變得越來(lái)越聰明。


分享到:

相關(guān)閱讀:

上一篇:編程有哪些技巧

下一篇:沒(méi)有了

近期文章

搶試聽(tīng)名額

名額僅剩66名

教育改變生活

WE CHANGE LIVES