[打造可維護軟體] Part II

Yu-Song Syu
Kuma君的閱讀雜記
7 min readJul 14, 2018

(本文中程式碼可由此下載:https://github.com/bearhsu2/building_maintainable_software)

前篇中,我們關注了三種跟元件本身有關的主題,現在,我們擴大一下範圍,關注一下元件之間的互動吧!

讓程式碼單元的介面保持簡單

寫程式時,你不太可能不引用別的元件,除非你能用一個3萬行的method搞定一切(同時創造出更可怕的災難)。既然引用不可避免,那何不讓它容易一點?

要說什麼是簡單的介面,不如先來看看「不簡單」的介面長什麼樣子吧…

public class ThreeDPackMan {

// Ignored...

public void move(int xSteps, int ySteps, int hSteps, int fatigueConsumption) {
this.x += xSteps;
this.y += ySteps;
this.h += hSteps;
this.fatigue -= fatigueConsumption;
}
}

設計很簡單,就是一個PackMan,在3D的地圖上移動,除了x, y, z軸上的位置會變化以外,體力也會下降。

參數有4個,看似還能Handle,如果再多加一個呢?多加兩個呢?重點是xyz軸各移動多少,我需要關心這麼多嗎?看看這個如何?

public void move(Step step, int fatigueConsumption) {
changePosition(step);
changeFatigue(fatigueConsumption);
}

private void changeFatigue(int fatigueConsumption) {
this.fatigue -= fatigueConsumption;
}

private void changePosition(Step step) {
this.x += step.getxSteps();
this.y += step.getySteps();
this.h += step.gethSteps();
}

move()方法現在只剩兩個參數了。此時如果引用者不在意細節,我收到什麼Step,如實pass下去便是。如果我真的須要看細節,就再打開Step或是private method changePosition()看看囉!

對於「表現意圖」來說,我們提供足夠多的抽象訊息就可以了。

把細節往下層藏,是提高可讀性的常用技巧。

不同模組之間的關注點分離

試想一個情境:你有一個Phone類別,一開始只須要打電話,所以有了3個method,後來因為要支援照相功能,多了3個method,再過半年,為了電話簿,再加4個method…

不知不覺,你的Phone長成了一個1500行的巨怪。

「創造不是問題,再平庸的RD都懂得創造。重點在維護!」

一個1500行的class,如果是一個新進同仁,他敢動手修改嗎?光是打開就壓力山大吧?

「好吧那就請資深同事改吧。」

所以資深同事打算在貴司幹一輩子?
所以資深同事打算在貴司幹一輩子?
所以資深同事打算在貴司幹一輩子?

再說了,如果你們團隊能輕易創造出這一個巨怪,我不相信其他元件的情況能好去哪裡!

看看這個例子:

public class SmartPhone {

public void takePhoto(){
}
public void zoomIn(){
}
public void zoomOut(){
}

public void talk(){
}

public void sendMessage(){
}
}

比剛剛那個例子好多了。但還是做太多事了…電話功能也歸它管,照相功能對歸它管。不現在想想辦法,程式碼長大是很快的,就像孩子一樣 XD

public class SmartPhone {

Camera camera;

public void takePhoto() {
camera.takePhoto()
}
public void talk() {
}

public void sendMessage() {
}

}

SmartPhone管電話功能,camera管相機功能,各司其職,多好!喔,其實還可以再拆唷!把message也拆出去如何?會不會太麻煩?會不會其實這樣就夠了?

這,就要靠RD自行拿捏了。

Unclu Bob曾說:「需要修改一個類別的原因,不應該有超過一種。」

以上面的SmartPhone為例,你可能為了三種原因而來修改SmartPhone這個Class:電話功能、訊息功能,以及照相功能。

這會讓SmartPhone這個class變成程式裡的「修改熱點」,一但一個功能改了,其他功能就同時暴露在出錯誤的風險中(畢竟同一class嘛),實不智也。

以鬆散耦合的方式架構元件

「鬆散耦合」是全世界RD都知道要做的事,但大部分的人,都覺得「我的程式已經鬆散耦合到不行,簡直是傑作!」

但真是如此?我們來看看下面這段:

public class Application {

public void execute(){

Reader baseReader = new Reader(5, 10, true, false);
Reader uninterruptableReader = new Reader(5, 10, false, false);
Reader singleThreadReader = new Reader(1, 0, true, false);
Reader quietReader = new Reader(5, 10, true, true);

// do other things...
}

}

很明顯的,Reader看起來有四種可控參數,外面可以自由搭配,造出自己合用的Read。至此,看出問題了嗎?

「沒問題呀!我平常coding時都這樣寫呀,怎麼了?有什麼問題嗎?」

只有一個問題:你知道得太多了

使用者要的是一個「特性上合用的Reader」,不是很想管參數細節,此時「Factory」可以幫忙隱藏一些細節與低階邏輯:

ublic class ReaderFactory {

public static Reader newBaseReader(int threads, int buffer) {
return new Reader(threads, buffer, true, false);
}


public static Reader newUninterruptableReader(int threads, int buffer) {
return new Reader(threads, buffer, false, false);

}

public static Reader newSingleThreadReader() {
return new Reader(1, 0, true, false);
}

public static Reader newQuietReader(int threads, int buffer) {
return new Reader(threads, buffer, true, true);
}
}

有了Factory,外層就不用管那麼多細節了:

public void execute() {

Reader baseReader = ReaderFactory.newBaseReader(5, 10);
Reader uninterruptableReader = ReaderFactory.newUninterruptableReader(5, 10);
Reader singleThreadReader = ReaderFactory.newSingleThreadReader();
Reader quietReader = ReaderFactory.newQuietReader(5,10);

// do other things...
}

一樣,太細節的東西,盡量往下藏起來,就像當兵遇到督導一樣,不是不給長官看,只是先收起來,以免影響長官視察流程,如果長官真的問了,我們再拿出來給他看囉!

下次見!

--

--