日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
函數(shù)式編程思想:耦合和組合

面向?qū)ο缶幊掏ㄟ^封裝變動部分把代碼變成易懂的,函數(shù)式編程則是通過最小化變動部分來把代碼變成易懂的?!狹ichael Feathers,Working with Legacy Code一書的作者

專注于為中小企業(yè)提供成都網(wǎng)站制作、成都做網(wǎng)站服務(wù),電腦端+手機端+微信端的三站合一,更高效的管理,為中小企業(yè)烏海海南免費做網(wǎng)站提供優(yōu)質(zhì)的服務(wù)。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動了近1000家企業(yè)的穩(wěn)健成長,幫助中小企業(yè)通過網(wǎng)站建設(shè)實現(xiàn)規(guī)模擴充和轉(zhuǎn)變。

每天都以某種特定的抽象來進行編碼工作,這種抽象會逐漸滲透到你的大腦中,影響到你解決問題的方式。這一文章系列的目標(biāo)之一是說明如何以一種函數(shù)方式看待典型的問題。就本文和下一篇文章來說,我通過重構(gòu)和隨之帶來的抽象影響來解決代碼的重用問題。

面向?qū)ο蟮哪繕?biāo)之一是使封裝和狀態(tài)操作更加容易,因此,其抽象傾向于使用狀態(tài)來解決常見的問題,而這意味會用到多個類和交互——這就是前面引述Michael Feathers的話中所說的“變動部分”。函數(shù)式編程嘗試通過把各部分組合在一起而不是把結(jié)構(gòu)耦合在一起來最小化變動的部分,這是一個微妙的概念,對于其經(jīng)驗主要體現(xiàn)在面向?qū)ο笳Z言方面的開發(fā)者來說,不太容易體會到。

經(jīng)由結(jié)構(gòu)的代碼重用

命令式的(特別是)面向?qū)ο蟮木幊田L(fēng)格使用結(jié)構(gòu)和消息來作為構(gòu)建塊。若要重用面向?qū)ο蟮拇a,你需要把對象代碼提取到另一個類中,然后使用繼承來訪問它。

無意導(dǎo)致的代碼重復(fù)

為了說明代碼的重用及其影響,我重提之前的文章用來說明代碼結(jié)構(gòu)和風(fēng)格的一個數(shù)字分類器版本,該分類器確定一個正數(shù)是富余的(abundant)、完美的(perfect)還是欠缺的(deficient),如果數(shù)字因子的總和大于數(shù)字的兩倍,它就是富余的,如果總和等于數(shù)字的兩倍,它就是完美的,否則(如果總和小于數(shù)字的兩倍)就是欠缺的。

你還可以編寫這樣的代碼,使用正數(shù)的因子來確定它是否是一個素數(shù)(定義是,一個大于1的整數(shù),它的因子只有1和它自身)。因為這兩個問題都依賴于數(shù)字的因子,因此它們是用于重構(gòu)從而也是用于說明代碼重用風(fēng)格的很好的可選案例。

清單1給出了使用命令式風(fēng)格編寫的數(shù)字分類器:

清單1. 命令式的數(shù)字分類器

 
 
  1. import java.util.HashSet;  
  2. import java.util.Iterator;  
  3. import java.util.Set;  
  4.  
  5. import static java.lang.Math.sqrt;  
  6.  
  7. public class ClassifierAlpha {  
  8.     private int number;  
  9.  
  10.     public ClassifierAlpha(int number) {  
  11.         this.number = number;  
  12.     }  
  13.  
  14.     public boolean isFactor(int potential_factor) {  
  15.         return number % potential_factor == 0;  
  16.     }  
  17.  
  18.     public Set factors() {  
  19.         HashSet factors = new HashSet();  
  20.         for (int i = 1; i <= sqrt(number); i++)  
  21.             if (isFactor(i)) {  
  22.                 factors.add(i);  
  23.                 factors.add(number / i);  
  24.  
  25.             }  
  26.         return factors;  
  27.     }  
  28.  
  29.     static public int sum(Set factors) {  
  30.         Iterator it = factors.iterator();  
  31.         int sum = 0;  
  32.         while (it.hasNext())  
  33.             sum += (Integer) it.next();  
  34.         return sum;  
  35.     }  
  36.  
  37.     public boolean isPerfect() {  
  38.         return sum(factors()) - number == number;  
  39.     }  
  40.  
  41.     public boolean isAbundant() {  
  42.         return sum(factors()) - number > number;  
  43.     }  
  44.  
  45.     public boolean isDeficient() {  
  46.         return sum(factors()) - number < number;  
  47.     }  
  48.  

我在第一部分內(nèi)容中已討論了這一代碼的推導(dǎo)過程,因此我現(xiàn)在就不再重復(fù)了。該例子在這里的目標(biāo)是說明代碼的重用,因此我給出了清單2中的代碼,該部分代碼檢測素數(shù):

清單2. 素數(shù)測試,以命令方式來編寫

 
 
  1. import java.util.HashSet;  
  2. import java.util.Set;  
  3.  
  4. import static java.lang.Math.sqrt;  
  5.  
  6. public class PrimeAlpha {  
  7.     private int number;  
  8.  
  9.     public PrimeAlpha(int number) {  
  10.         this.number = number;  
  11.     }  
  12.  
  13.     public boolean isPrime() {  
  14.         Set primeSet = new HashSet() {{  
  15.             add(1); add(number);}};  
  16.         return number > 1 &&  
  17.                 factors().equals(primeSet);  
  18.     }  
  19.  
  20.     public boolean isFactor(int potential_factor) {  
  21.         return number % potential_factor == 0;  
  22.     }  
  23.  
  24.     public Set factors() {  
  25.         HashSet factors = new HashSet();  
  26.         for (int i = 1; i <= sqrt(number); i++)  
  27.             if (isFactor(i)) {  
  28.                 factors.add(i);  
  29.                 factors.add(number / i);  
  30.             }  
  31.         return factors;  
  32.     }  

清單2中出現(xiàn)了幾個值得注意的事項,首先是isPrime()方法中的初始化代碼有些不同尋常,這是一個實例初始化器的例子(若要了解更多關(guān)于實例初始化——一種附帶了函數(shù)式編程的Java技術(shù)——這一方面的內(nèi)容,請參閱“Evolutionary architecture and emergent design: Leveraging reusable code, Part 2”。)

清單2中令人感興趣的其他部分是isFactor()和factors()方法??梢宰⒁獾?,它們與(清單1的)ClassifierAlpha類中的相應(yīng)部分相同,這是分開獨立實現(xiàn)兩個解決方案的自然結(jié)果,這讓你意識到你用到了相同的功能。

通過重構(gòu)來消除重復(fù)

這一類重復(fù)的解決方法是使用單個的Factors類來重構(gòu)代碼,如清單3所示:

清單3. 一般重構(gòu)后的因子提取代碼

 
 
  1. import java.util.Set;  
  2. import static java.lang.Math.sqrt;  
  3. import java.util.HashSet;  
  4.  
  5. public class FactorsBeta {  
  6.     protected int number;  
  7.  
  8.     public FactorsBeta(int number) {  
  9.         this.number = number;  
  10.     }  
  11.  
  12.     public boolean isFactor(int potential_factor) {  
  13.         return number % potential_factor == 0;  
  14.     }  
  15.  
  16.     public Set getFactors() {  
  17.         HashSet factors = new HashSet();  
  18.         for (int i = 1; i <= sqrt(number); i++)  
  19.             if (isFactor(i)) {  
  20.                 factors.add(i);  
  21.                 factors.add(number / i);  
  22.             }  
  23.         return factors;  
  24.     }  

清單3中的代碼是使用提取超類(Extract Superclass)這一重構(gòu)做法的結(jié)果,需要注意的是,因為兩個提取出來的方法都使用了number這一成員變量,因此它也被放到了超類中。在執(zhí)行這一重構(gòu)時,IDE詢問我想要如何處理訪問(訪問器對、保護范圍等等),我選擇了protected(受保護)這一作用域,這一選擇把number加入了類中,并創(chuàng)建了一個構(gòu)造函數(shù)來設(shè)置它的值。

一旦我孤立并刪除了重復(fù)的代碼,數(shù)字分類器和素數(shù)測試器兩者就都變得簡單多了。清單4給出了重構(gòu)后的數(shù)字分類器:

清單4. 重構(gòu)后簡化了的數(shù)字分類器

 
 
  1. mport java.util.Iterator;  
  2. import java.util.Set;  
  3.  
  4. public class ClassifierBeta extends FactorsBeta {  
  5.  
  6.     public ClassifierBeta(int number) {  
  7.         super(number);  
  8.     }  
  9.  
  10.     public int sum() {  
  11.         Iterator it = getFactors().iterator();  
  12.         int sum = 0;  
  13.         while (it.hasNext())  
  14.             sum += (Integer) it.next();  
  15.         return sum;  
  16.     }  
  17.  
  18.     public boolean isPerfect() {  
  19.         return sum() - number == number;  
  20.     }  
  21.  
  22.     public boolean isAbundant() {  
  23.         return sum() - number > number;  
  24.     }  
  25.  
  26.     public boolean isDeficient() {  
  27.         return sum() - number < number;  
  28.     }  
  29.  

清單5給出了重構(gòu)后的素數(shù)測試器

清單5. 重構(gòu)后簡化了的素數(shù)測試器

 
 
  1. import java.util.HashSet;  
  2. import java.util.Set;  
  3.  
  4. public class PrimeBeta extends FactorsBeta {  
  5.     public PrimeBeta(int number) {  
  6.         super(number);  
  7.     }  
  8.  
  9.     public boolean isPrime() {  
  10.         Set primeSet = new HashSet() {{  
  11.             add(1); add(number);}};  
  12.         return getFactors().equals(primeSet);  
  13.     }  

無論在重構(gòu)時為number成員選擇的訪問選項是哪一種,你在考慮這一問題時都必須要處理類之間的網(wǎng)絡(luò)關(guān)系。通常這是一件好事,因為其允許你獨立出問題的某些部分,但在修改父類時也會帶來不利的后果。

這是一個通過耦合(coupling)來重用代碼的例子:通過number域這一共享狀態(tài)和超類的getFactors()方法來把兩個元素(在本例中是類)捆綁在一起。換句話說,這種做法起作用是因為利用了內(nèi)置在語言中的耦合規(guī)則。面向?qū)ο蠖x了耦合的交互方式(比如說,你通過繼承訪問成員變量的方式),因此你擁有了關(guān)于事情如何交互的一些預(yù)定義好的風(fēng)格——這沒有什么問題,因為你可以以一種一致的方式來推理行為。不要誤解我——我并非是在暗示使用繼承是一個糟糕的主意,相反,我的意思是,它在面向?qū)ο蟮恼Z言中被過度使用,結(jié)果取代了另一種有著更好特性的抽象。

經(jīng)由組合的代碼重用

在這一系列的第二部分內(nèi)容中,我給出了一個用Java編寫的數(shù)字分類器的函數(shù)式版本,如清單6所示:

清單6. 數(shù)字分類器的一個更加函數(shù)化的版本

 
 
  1. public class FClassifier {  
  2.  
  3.     static public boolean isFactor(int number, int potential_factor) {  
  4.         return number % potential_factor == 0;  
  5.     }  
  6.  
  7.     static public Set factors(int number) {  
  8.         HashSet factors = new HashSet();  
  9.         for (int i = 1; i <= sqrt(number); i++)  
  10.             if (isFactor(number, i)) {  
  11.                 factors.add(i);  
  12.                 factors.add(number / i);  
  13.             }  
  14.         return factors;  
  15.     }  
  16.  
  17.     public static int sumOfFactors(int number) {  
  18.         Iterator it = factors(number).iterator();  
  19.         int sum = 0;  
  20.         while (it.hasNext())  
  21.             sum += it.next();  
  22.         return sum;  
  23.     }  
  24.  
  25.     public static boolean isPerfect(int number) {  
  26.         return sumOfFactors(number) - number == number;  
  27.     }  
  28.  
  29.     public static boolean isAbundant(int number) {  
  30.         return sumOfFactors(number) - number > number;  
  31.     }  
  32.  
  33.     public static boolean isDeficient(int number) {  
  34.         return sumOfFactors(number) - number < number;  
  35.     }  

我也有素數(shù)測試器的一個函數(shù)式版本(使用了純粹的函數(shù),沒有共享狀態(tài)),該版本的 isPrime()方法如清單7所示。其余部分代碼與清單6中的相同命名方法的代碼一樣。

清單7. 素數(shù)測試器的函數(shù)式版本

 
 
  1. public static boolean isPrime(int number) {  
  2.     Set factorsfactors = factors(number);  
  3.     return number > 1 &&  
  4.             factors.size() == 2 &&  
  5.             factors.contains(1) &&  
  6.             factors.contains(number);  

就像我在命令式版本中所做的那樣,我把重復(fù)的代碼提取到它自己的Factors類中,基于可讀性,我把factors方法的名稱改為of,如圖8所示:

清單8 函數(shù)式的重構(gòu)后的Factors類

 
 
  1. mport java.util.HashSet;  
  2. import java.util.Set;  
  3. import static java.lang.Math.sqrt;  
  4.  
  5. public class Factors {  
  6.     static public boolean isFactor(int number, int potential_factor) {  
  7.         return number % potential_factor == 0;  
  8.     }  
  9.  
  10.     static public Set of(int number) {  
  11.         HashSet factors = new HashSet();  
  12.         for (int i = 1; i <= sqrt(number); i++)  
  13.             if (isFactor(number, i)) {  
  14.                 factors.add(i);  
  15.                 factors.add(number / i);  
  16.             }  
  17.         return factors;  
  18.     }  

因為函數(shù)式版本中所有狀態(tài)都是作為參數(shù)傳遞的,因此提取出來的這部分內(nèi)容沒有共享狀態(tài)。一旦提取了該類之后,我就可以重構(gòu)函數(shù)式的分類器和素數(shù)測試器來使用它了。清單9給出了重構(gòu)后的分類器:

清單9. 重構(gòu)后的數(shù)字分類器

 
 
  1. public class FClassifier {  
  2.  
  3.     public static int sumOfFactors(int number) {  
  4.         Iterator it = Factors.of(number).iterator();  
  5.         int sum = 0;  
  6.         while (it.hasNext())  
  7.             sum += it.next();  
  8.         return sum;  
  9.     }  
  10.  
  11.     public static boolean isPerfect(int number) {  
  12.         return sumOfFactors(number) - number == number;  
  13.     }  
  14.  
  15.     public static boolean isAbundant(int number) {  
  16.         return sumOfFactors(number) - number > number;  
  17.     }  
  18.  
  19.     public static boolean isDeficient(int number) {  
  20.         return sumOfFactors(number) - number < number;  
  21.     }  

清單10給出了重構(gòu)后的素數(shù)測試器:

清單10. 重構(gòu)后的素數(shù)測試器

 
 
  1. import java.util.Set;  
  2.  
  3. public class FPrime {  
  4.  
  5.     public static boolean isPrime(int number) {  
  6.         Set factors = Factors.of(number);  
  7.         return number > 1 &&  
  8.                 factors.size() == 2 &&  
  9.                 factors.contains(1) &&  
  10.                 factors.contains(number);  
  11.     }  

可以注意到,我并未使用任何特殊的庫或是語言來把第二個版本變得更加的函數(shù)化,相反,我通過使用組合而不是耦合式的代碼重用做到了這一點。清單9和清單10都用到了Factors類,但它的使用完全是包含在了單獨方法的內(nèi)部之中。

耦合和組合之間的區(qū)別很細微但很重要,在一個像這樣的簡單例子中,你可以看到顯露出來的代碼結(jié)構(gòu)骨架。但是,當(dāng)你最終重構(gòu)的是一個大型的代碼庫時,耦合就顯得無處不在了,因為這是面向?qū)ο笳Z言中的重用機制之一。繁復(fù)的耦合結(jié)構(gòu)的難以理解性損害到了面向?qū)ο笳Z言的重用性,把有效的重用局限在了諸如對象-關(guān)系映射和構(gòu)件庫一類已明確定義的技術(shù)領(lǐng)域上,當(dāng)我們在寫少量的明顯結(jié)構(gòu)化的Java代碼時(比如說你在業(yè)務(wù)應(yīng)用中編寫的代碼),這種層面的重用我們就用不上了。

你可以通過這樣的做法來改進命令式的版本,即在重構(gòu)期間會告之哪些內(nèi)容由IDE提供,先客氣地拒絕,然后使用組合來替代。

結(jié)束語

作為一個更函數(shù)化的編程者來進行思考,這意味著以不同的方式來思考編碼的各個方面。代碼重用顯然是開發(fā)的一個目標(biāo),命令式抽象傾向于以不同于函數(shù)式編程者的方式來解決該問題。這部分內(nèi)容對比了代碼重用的兩種方式:經(jīng)由繼承的耦合方式和經(jīng)由參數(shù)的組合方式。下一部分內(nèi)容會繼續(xù)探討這一重要的分歧。

原文:http://article.yeeyan.org/view/213582/224721


網(wǎng)站名稱:函數(shù)式編程思想:耦合和組合
本文來源:http://www.dlmjj.cn/article/dhidogd.html