国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

函數式編程思想:耦合和組合

開發 前端
總是在某種特定抽象(比如說面向對象)中進行編碼工作,這使得很難看清楚何時這一抽象會把你引向一種并非最好的解決方案上。作為這一系列的兩篇文章中的頭一篇,本文探討了用于代碼重用的面向對象編程思想的一些影響,并把它們與一些更函數化的可選方法,比如說組合,進行比較。

面向對象編程通過封裝變動部分把代碼變成易懂的,函數式編程則是通過最小化變動部分來把代碼變成易懂的。——Michael Feathers,Working with Legacy Code一書的作者

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

面向對象的目標之一是使封裝和狀態操作更加容易,因此,其抽象傾向于使用狀態來解決常見的問題,而這意味會用到多個類和交互——這就是前面引述Michael Feathers的話中所說的“變動部分”。函數式編程嘗試通過把各部分組合在一起而不是把結構耦合在一起來最小化變動的部分,這是一個微妙的概念,對于其經驗主要體現在面向對象語言方面的開發者來說,不太容易體會到。

經由結構的代碼重用

命令式的(特別是)面向對象的編程風格使用結構和消息來作為構建塊。若要重用面向對象的代碼,你需要把對象代碼提取到另一個類中,然后使用繼承來訪問它。

無意導致的代碼重復

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

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

清單1給出了使用命令式風格編寫的數字分類器:

清單1. 命令式的數字分類器

  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.  

我在第一部分內容中已討論了這一代碼的推導過程,因此我現在就不再重復了。該例子在這里的目標是說明代碼的重用,因此我給出了清單2中的代碼,該部分代碼檢測素數:

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

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

清單2中令人感興趣的其他部分是isFactor()和factors()方法。可以注意到,它們與(清單1的)ClassifierAlpha類中的相應部分相同,這是分開獨立實現兩個解決方案的自然結果,這讓你意識到你用到了相同的功能。

通過重構來消除重復

這一類重復的解決方法是使用單個的Factors類來重構代碼,如清單3所示:

清單3. 一般重構后的因子提取代碼

  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)這一重構做法的結果,需要注意的是,因為兩個提取出來的方法都使用了number這一成員變量,因此它也被放到了超類中。在執行這一重構時,IDE詢問我想要如何處理訪問(訪問器對、保護范圍等等),我選擇了protected(受保護)這一作用域,這一選擇把number加入了類中,并創建了一個構造函數來設置它的值。

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

清單4. 重構后簡化了的數字分類器

  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給出了重構后的素數測試器

清單5. 重構后簡化了的素數測試器

  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.     }  

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

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

經由組合的代碼重用

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

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

  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.     }  

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

清單7. 素數測試器的函數式版本

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

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

清單8 函數式的重構后的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.     }  

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

清單9. 重構后的數字分類器

  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給出了重構后的素數測試器:

清單10. 重構后的素數測試器

  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.     }  

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

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

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

結束語

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

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

責任編輯:陳貽新 來源: 譯言網
相關推薦

2010-08-03 08:54:07

JDK 7Lambda表達式函數式編程

2009-07-08 16:10:24

Scala簡介面向對象函數式

2020-09-23 16:07:52

JavaScript函數柯里化

2012-03-21 09:30:11

ibmdw

2023-11-21 07:17:36

Reac函數組件

2013-09-09 09:41:34

2023-10-07 00:01:02

Java函數

2025-03-11 10:00:20

Golang編程函數

2020-09-24 10:57:12

編程函數式前端

2016-10-31 20:46:22

函數式編程Javascript

2011-03-08 15:47:32

函數式編程

2020-05-26 21:17:28

函數式編程純函數

2020-05-26 16:27:58

函數孩子編程

2023-05-06 07:27:47

2011-08-24 09:13:40

編程

2023-12-14 15:31:43

函數式編程python編程

2022-09-22 08:19:26

WebFlux函數式編程

2017-06-08 14:25:46

Kotlin函數

2013-11-26 10:14:15

面向對象函數式

2013-06-27 09:31:37

聲明式編程命令式編程編程
點贊
收藏

51CTO技術棧公眾號

图片区亚洲欧美小说区| 精品视频亚洲| 国产精品萝li| 日本不卡久久| 成人av影音| 制服丝袜成人动漫| 美女av电影| 国产电影精品久久禁18| 成人一区二区三区四区| 欧美久久一区二区三区| 欧美videos大乳护士334| 激情丁香在线| 99视频精品全部免费在线| 国产传媒一区二区三区| 成人免费在线电影网| 亚洲第一偷拍网| 亚洲第一se情网站| 中文字幕五月欧美| 91网站在线观看免费| 国产女优一区| 国产精品自在线| 9l视频自拍蝌蚪9l视频成人| 亚洲欧美成人精品| 四虎影院观看视频在线观看| 狠狠久久亚洲欧美专区| 超碰色偷偷男人的天堂| 91在线精品一区二区三区| 超碰免费在线公开| 久久久久免费| 精品乱码一区二区三区| 欧美r级电影| 国产成人中文字幕| 日韩精品久久久久久久软件91| 日韩毛片在线观看| 2020国产在线| 亚洲国产精品成人一区二区| 久久99精品久久| 在线不卡欧美精品一区二区三区| 国产小视频免费在线网址| 欧美视频在线观看免费| 久草热久草在线频| 午夜a成v人精品| 中文字幕在线资源| 午夜国产不卡在线观看视频| 成人频在线观看| 疯狂欧美牲乱大交777| 可以直接在线观看的av| 91国产丝袜在线播放| 风间由美一区| 日韩精品中文字幕一区| 欧美13videosex性极品| 一区二区三区四区在线观看视频| 巨大黑人极品videos精品| 久久国产精品久久久久久| 国产福利一区二区精品秒拍| 秋霞成人午夜鲁丝一区二区三区| 综合国产视频| 91最新在线免费观看| 极品中文字幕一区| 一区二区在线不卡| 91蜜桃在线观看| 日本五十路在线| 欧美午夜影院一区| 多野结衣av一区| 色婷婷综合成人| 特黄特色欧美大片| 91黄在线观看| 理论电影国产精品| aa在线观看视频| 亚洲第一av色| 神马午夜伦理不卡 | cao在线视频| 日韩一区二区在线视频| 日韩欧美天堂| 精品一区久久久| 懂色av噜噜一区二区三区av| 999精彩视频| 在线观看一区二区精品视频| 国产污视频在线播放| 欧美激情一级二级| 国内精品99| 激情深爱综合网| 午夜免费久久看| 亚洲女同志freevdieo| 97av在线播放| 日韩精品欧美精品| 污污网站免费观看| 在线成人午夜影院| 8x国产一区二区三区精品推荐| 成人h视频在线观看播放| 捆绑紧缚一区二区三区视频| 亚洲激情在线观看视频| 欧美老女人第四色| 亚洲精品av在线播放| 亚洲缚视频在线观看| 亚洲精品777| 91在线免费看网站| 久久成人免费电影| 好男人看片在线观看免费观看国语| 欧美片在线播放| 综合激情五月婷婷| 欧美xxxx黑人又粗又长密月 | 欧美另类女人| 欧美日韩一区二区三区电影| 亚洲免费观看在线观看| 黄色的视频在线观看| 国产成人亚洲综合| 国产成人av自拍| 国产一二在线观看| 欧美—级a级欧美特级ar全黄| 国产精品久久久免费| 日韩中文字幕免费在线| 日韩一区二区在线播放| 蜜桃a∨噜噜一区二区三区| 一区二区精品在线| 色综合久久66| 日韩黄色网络| 丝袜人妻一区二区三区| 欧美一区二区三区不卡| 青青草原综合久久大伊人精品| 国产精品裸体瑜伽视频| 欧美一级在线免费| 五月激情综合| 欧美中日韩在线| 日本福利一区二区| 东京久久高清| 69堂精品视频在线播放| 国内激情视频在线观看| 亚洲xxxxx电影| 亚洲丝袜美腿综合| 久久亚洲人体| 一本一本久久a久久精品综合妖精| 亚洲大片精品永久免费| 亚洲日本一区二区三区在线| 樱空桃在线播放| 日韩欧美123| 激情久久一区| 小明精品国产一区二区三区| 国产精品99导航| 中文字幕日本不卡| 99久久人爽人人添人人澡| 日韩欧美一区二| 在线视频日本亚洲性| 国产中文一区二区三区| 91黄页在线观看| 一区二区三区我不卡| 日韩精品专区在线影院重磅| 亚洲综合日韩| 国产在线高清理伦片a| 91视频九色网站| 色综合一个色综合| 欧美日韩国产高清| 久久久久久久久亚洲精品| 亚洲一区二区在线播放| 色婷婷综合久久久久中文一区二区| 99精品国产一区二区三区| 神马久久久久| 国产伦精品一区二区| 欧美老肥妇做.爰bbww| 亚洲神马久久| 午夜av在线播放| 精品国产无码在线| 国产亚洲欧美日韩一区二区| 国产91在线看| 日本一区二区三区播放| 欧美黑人又粗又大又爽免费| 欧美丰满老妇厨房牲生活| 中文字幕第一区二区| 国产成人精品一区二区免费看京| 色视频www在线播放| 亚洲一区制服诱惑| 欧美日韩中文字幕一区二区| 日韩精品一卡二卡三卡四卡无卡| a级片在线免费观看| 青青青青在线视频| 久久99亚洲热视| 一区二区成人在线| 综合日韩在线| jizz一区二区三区| 波多野结衣乳巨码无在线| 91精品国产91久久久久福利| 午夜小视频福利在线观看| 清纯唯美激情亚洲| 91最新在线观看| 国产精品日日做人人爱| 欧洲国内综合视频| 日本va欧美va精品发布| 超碰这里只有精品| 成人亚洲一区二区三区| 成人自拍视频网站| 日韩精品免费在线视频观看| www日韩大片| 久久精品高清| 18网站在线观看| 久久99久久久久久| 国产成人精品av| 日韩一区二区在线观看视频 | 国产日本在线视频| 超级碰在线观看| 日本a级片电影一区二区|