重構C語言版(二) 技能基礎篇: 簡化邏輯結構

上一篇:https://blog.csdn.net/weixin_42523774/article/details/105619681

· 爲何 簡化邏輯結構 單獨作爲一篇闡述?
· 如果代碼邏輯複雜,如何才能理清代碼中的浮雲,顯現出其最原本的邏輯,爲後續修改邏輯來鋪平道路。 這就需要一系列的手法,我稱之爲 技能基礎。而這些技能遵循二八法則,學到這20%,使用會佔到80%。因此學習這些手法是很有必要的。
· 由於重構用到了很多面向對象的思維,如果對C語言面向對象編程不熟悉,請查看這篇《學會C語言面向對象編程,弄清面向對象實質。》
· 首先介紹一下四個名詞的含義:
(1)提煉——增加代碼邏輯的層次,增加代碼邏輯的中間層;
(2)內聯——減少代碼邏輯的層次,幹掉代碼邏輯的中間層;
(3)組合——將代碼放到一起,增加相關性;
(4)拆分——將代碼分開放置,減少耦合。
· 下面逐步介紹各種方法:

1. 提煉函數

· 將諸多的代碼提煉成一個函數,那麼何時需要提煉呢?
· 如果你要花一段時間來理解代碼的意圖,你就需要將它提煉到一個函數中,讓人一看到這個函數就知道它在做什麼。
· 函數不要太大,我的經驗是超過6行的函數就感覺有點多了,你可能會擔心短函數會導致大量的函數調用,而影響性能,
· 但是現在短函數常常能讓編譯器的優化功能運轉更爲良好,因此不用過多擔心性能問題。
· 小函數需要有好名字,你可以從註釋中得到提示。
· 範例:

(1)無局部變量:這部分最簡單,就是把一段無影響的功能提出來

比如有一行異常打印:

printf("error happened in func!\n");

提煉成:

void print_error(void) {
	printf("error happened in func!\n");
}
(2)有局部變量:

比如我想把這個func提出來,作爲一個參數輸入,就可以這樣;
提煉成:

void print_error(const char *func_name) {
	printf("error happened in %s!\n", func_name);
}
(3)對局部變量再賦值:

· 這種情況就是函數修改的變量,需要在函數外使用;我們可以把參數作爲輸入,然後用返回值返回;
· 比如上面的打印函數需要記錄打印出錯的次數:
提煉成:

int print_error(const char *func_name) {
	static count = 0;
	printf("error %d happened in %s!\n", ++count, func_name);
	return count;
}

· 如果需要返回的函數參數有多個,我建議可以用多個函數來提煉,保證只返回一個,如果實在是需要多個,建議通過封裝對象(struct)的方式範返回。

2.內聯函數

· 內聯函數就是提煉函數的反向重構。
· 當函數的內部代碼和函數名稱同樣清晰易讀,也可能是你重構了函數實現,使其內容和其名稱同樣清晰時,這樣就建議你直接使用其中的代碼。
· 間接性可能帶來幫助,但是非必要的間接性總是讓人不舒服。通過內聯手法,可以找出有用的間接層,也可以將無用的間接層去除。
做法:
找出函數的所有調用點,逐個替換。
範例:

(1)簡單情況
int rating(struct Driver_info aDriver) {
	return moreThanFiveLateDeliveries(aDriver) ? 2 : 1;
}
int moreThanFiveLateDeliveries() {
	return aDriver.numberOfLateDeliveries > 5;
}

內聯爲:

int rating(struct Driver_info aDriver) {
	return aDriver.numberOfLateDeliveries > 5? 2 : 1;
}
(2)複雜情況:

當需要移出的函數內容很複雜時,你就需要"剪切-粘貼-調整"來進行,當調用函數衆多,則需要調整一次就測試一次;
如果你遇到了麻煩,就意味着需要使用更精細的重構手法:搬移語句到調用者(217)。

3. 提煉變量

· 有時表達式可能非常難以閱讀,這時,局部變量可以幫助我們將表達式分解爲比較容易管理的形式,讓我們理解這部分的邏輯是幹什麼的。
· 命名:如果考慮使用提煉變量,就意味着我要給代碼中的一個表達式命名。
· 如果這個名字只在當前函數中有意義,提煉變量是個不錯的選擇。
· 如果這個命名,在更寬的上下文中,我就會考慮將其暴露出來,通常以函數的形式。
範例:

(1)簡單計算提取
int price(struct goods order) {
	//price is base price - quantity discount + shipping
	return order.quantity * order.itemPrice - 
	max(0, order.quantity - 500) * order.itemPrice * 0.05 +
	min(order.quantity * order.itemPrice * 0.1, 100);
}

提煉出basePrice爲:

int price(struct goods order) {
	//price is base price - quantity discount + shipping
	const int basePrice = order.quantity * order.itemPrice;
	return basePrice - max(0, order.quantity - 500) * order.itemPrice * 0.05 +
	min(basePrice * 0.1, 100);
}

再提煉出quantity discount爲:

int price(struct goods order) {
	//price is base price - quantity discount + shipping
	const int basePrice = order.quantity * order.itemPrice;
	const quantityDiscount = max(0, order.quantity - 500) * order.itemPrice * 0.05;
	return basePrice - quantityDiscount + min(basePrice * 0.1, 100);
}

最後提煉出shipping爲,修改之後,註釋也就不需要了:

int price(struct Order order) {
	const int basePrice = order.quantity * order.itemPrice;
	const quantityDiscount = max(0, order.quantity - 500) * order.itemPrice * 0.05;
	const shipping = min(basePrice * 0.1, 100);
	return basePrice - quantityDiscount + shipping;
}
(2)類對象提取

· 同樣的內容,比如在類對象中,可以如下操作:
· C語言面向對象操作,見另一篇文章《學會C語言面向對象編程,弄清面向對象實質。》
順帶介紹一下:

struct Order {
	int data;
	struct OrderOperations *orderOp;
};
struct OrderOperations {
	int (*price)(struct Order *order);
};
int price(struct Order *order) {
	//price is base price - quantity discount + shipping
	return order->quantity * order->itemPrice - 
	max(0, order->quantity - 500) * order->itemPrice * 0.05 +
	min(order->quantity * order->itemPrice * 0.1, 100);
}
struct Order* alloc_Order(void) {
#define ORDER_DEFAULT_DATA 0
	static struct OrderOperations orderOp = {
		.price = price,
	};
	struct Order* order = (struct Order* )malloc(sizeof(struct Order));
	order->orderOp = orderOp;
	order->data = ORDER_DEFAULT_DATA;
	return order;
}
int main(int argc, char *argv[]) {
	struct Order* order = alloc_Order();
	printf("order price is %d.\n", order->orderOp->price(order));
	return 0;
}

提煉爲:

struct OrderOperations {
	int (*getPrice)(struct Order *order);
	int (*getBasePrice)(struct Order *order);
	int (*getQuantityDiscount)(struct Order *order);
	int (*getShipping)(struct Order *order);
};
int getBasePrice(struct Order *order) {
	return order->quantity * order->itemPrice;
}
int getQuantityDiscount(struct Order *order) {
	return max(0, order->quantity - 500) * order->itemPrice * 0.05;
}
int getShipping(struct Order *order) {
	return min(order->quantity * order->itemPrice * 0.1, 100);
}
int getPrice(struct Order *order) {
	return order->getBasePrice(order) - order->getQuantityDiscount(order) + order->getShipping(order);
}
struct Order* alloc_Order(void) {
#define ORDER_DEFAULT_DATA 0
	static struct OrderOperations orderOp = {
		.getPrice = getPrice,
		.getBasePrice = getBasePrice,
		.getQuantityDiscount = getQuantityDiscount,
		.getShipping = getShipping,
	};
	struct Order* order = (struct Order* )malloc(sizeof(struct Order));
	order->orderOp = orderOp;
	order->data = ORDER_DEFAULT_DATA;
	return order;
}
int main(int argc, char *argv[]) {
	struct Order* order = alloc_Order();
	printf("order price is %d.\n", order->orderOp->price(order));
}

· 在一個簡單的對象中暫時看不出太明顯的好處,但是當這個對象很大的時候,如果找出了可以共用的行爲,賦予它獨立的概念,起個好名字,對於使用對象的人來說會很有幫助。

4.內聯變量

· 雖然有時候,變量可以給表達式提供更有意義的名字;但是有時候,這個名字並不比表達式本身更有表現力,甚至妨礙重構附近的代碼,這時就該通過內聯手法消除變量。
範例:

int basePrice = anOrder.basePrice;
return (basePrice > 1000);

內聯爲:

return anOrder.basePrice > 1000;

5.改變函數聲明

· 函數是我們將程序拆成小塊的主要方式,而這中方式其中最重要的當屬函數的名字。一個好名字能讓我一眼看出函數的用途,而不必查看其實現代碼。
· 如果看到函數名字滿足不了上面的要求,一旦發現更好的名字,就得儘快給函數改名。對於函數的參數列表也是一樣的道理。對函數的參數也是同理。
這裏說的改變函數聲明,包括函數改名,函數參數改名,函數參數增減等項。

1)簡單做法(可以一步到位)

直接將函數修改爲新的函數聲明,然後測試。
如果你既想修改函數名,又想添加參數,最好分2步做。

2)遷移式做法(不能做到一步到位)

.1.如果有必要的話,可以先對函數體內部進行重構,使得後面的提煉步驟易於開展;
.2.先提煉函數,將函數提煉成一個新的函數,如果想沿用舊函數名,建議先給新函數起一個便於查找的臨時名字;
.3.如果需要添加參數,就用之前的方式添加;
.4.測試;
.5.對舊函數使用內聯函數,釋放函數中的內容;
.6.如果新函數使用了臨時名字,在此使用改變函數聲明將其改回來。

範例:

(1)函數改名(簡單做法)
int circum(int radius) {
	return 2 * PI * radius;
}

修改成:

int circumference(int radius) {
	return 2 * PI * radius;
}
(2)函數改名(遷移式做法)
int circum(int radius) {
	return 2 * PI * radius;
}

修改成:

int circum(int radius) {
	return circumference(radius);
}
int circumference(int radius) {
	return 2 * PI * radius;
}

然後測試,通過後使用內聯函數手法。

(3)添加參數

遷移式做法類似,不在重複介紹。

(4)把參數修改爲屬性

遷移式做法類似,不在重複介紹。

6.封裝變量

· 數據的重構相對於函數要麻煩得多。
· 如果想要搬移一處被廣泛使用的數據,最好的辦法是先以函數的形式封裝所有對數據的訪問。
· 對於所有的可變數據,只要它的作用域超過單個函數,我就會將其封裝起來,只允許通過函數訪問。數據的作用域越大,封裝就越重要。處理遺留代碼時,一旦需要修改或增加使用可變數據的代碼,我就會藉機把這份數據封裝起來,從而避免繼續加重耦合。面向對象的方法如此強調對象的數據應該保持私有,背後也是同樣的原理。
· 相比於封裝數據,不可變的數據更重要,不可變讓大家可以放心使用舊數據,不用做搬移,不用擔心代碼失效。

範例:
(1)全局變量

賦值:

struct Owner defaultOwner = {
	.firstName = "Martin",
	.lastName = "Fowler",
};

使用:

owner = defaultOwner;

更新:

defaultOwner.firstName = "Rebecca";
defaultOwner.lastName = "Parsons";

· 重構第一步:封裝成函數
· 獲取值的時候,建議不用指針,返回的內容就是副本,這樣原來數值不會被修改。獲取時建議不加get。

struct Owner defaultOwner(void) {
	return defaultOwner;
}
void setDefaultOwner(struct Owner newOwner) {
	defaultOwner = newOwner;
	return;
}

· 重構第二步:修改變量限制
然後可以將原來的變量增加限制,比如加上 static 限制在此文件中使用,如果做不到,建議將變量取一個有意義有難看的名字。
例如 __privateOnly_defaultOwner 。本次將其改爲

static struct Owner defaultOwnerData = {
	.firstName = "Martin",
	.lastName = "Fowler"
};

· 重構第三步:將其封裝爲一個對象,但是C語言不支持,因此封裝成結構體是唯一選擇。前面已經做好。

7.變量改名

· 好的命名是整潔編程的核心。變量名可以很好的解釋這段程序在幹啥——如果名字起的好的話。
· 使用範圍越廣,名字的好壞就越重要。
· 我習慣將變量的類型信息也放進名字裏面,我的類型對於的名字前綴表:

char - c,
unsigned char - uc, 
short - s,
unsigned short - us
int - i,
unsigned int - ui,
struct - t或對應的類型名字,
union - u,
指針 - p,
enum - e,
數組 - a,
float - f,
double - d,

· 如果變量被廣泛使用,建議使用封裝變量的方法,將其封裝起來。
· 常量改名,通常先複製這個常量,用新常量複製給舊的常量,這樣刪除就常量時會稍微快一點。
範例:此處前面變量封裝例子類似,就不做此範例。

8.引入參數對象

· 當同一組數據項經常同時出現在多個函數的參數列表中時,我喜歡代之以一個數據結構,將其組合起來。
· 這件事情的價值,在於將數據項之間關係變得明晰,而進一步圍繞該結構來捕捉共用行爲,這個結構將提升爲新的抽象概念。這個力量是強大的。
做法:
· 在該函數增加一個創建的數據結構的參數,然後一個一個的將參數項移到這個結構中,記得每一步都要保證測試通過。
範例:

int  readingsOutsideRange(int station, int min, int max) {
	return (station < min)? min : ((station > max)? max: station);
}

首先逐步重構爲:

struct NumberRange {
	int station;
	int min;
	int max;
};
int  readingsOutsideRange(struct NumberRange range) {
	return (range.station < range.min)? range.min : ((range.station > range.max)? range.max: range.station);
}

對此結構中的數據提取最好封裝成函數:

struct NumberRange {
	int station;
	int min;
	int max;
};
int min(struct NumberRange *range) {
	return range->min;
}
int max(struct NumberRange *range) {
	return range->max;
}
int  readingsOutsideRange(struct NumberRange range) {
	return (range.station < min(&range))? min(&range) : ((range.station > max(&range) )? max(&range): range.station);
}

9.函數組合成類

將數據組合起來,將數組的操作通過函數也放進來。

· 函數組合成類是將函數重新組織的一種方式,當一組函數形影不離地操作同一塊數據,這是就該組件一個類了。一般來說,類可以提供一套環境,可以讓我們的函數少傳許多參數,而一個對象也可以更方便的傳遞給系統的其他部分。只是在C語言中,沒有面向對象的支持,但是仍然可以將其組合成結構體使用。
做法:
(1)運用封裝記錄 對多個函數共用的數據記錄加以封裝;
(2)對於使用該記錄結構的每個函數,運用搬移函數 將其移入新類;
(3)用以處理數據記錄的邏輯,可以用提煉函數 的方法提煉出,並移入新類。
範例:
· 延續運用第8節中的例子,上一節中使用瞭如下代碼

struct NumberRange {
	int station;
	int min;
	int max;
	int (*getMin)(struct NumberRange *range);
	int (*getMax)(struct NumberRange *range);
};
int min(struct NumberRange *range) {
	return range->min;
}
int max(struct NumberRange *range) {
	return range->max;
}
struct NumberRange* allocNumberRange(void) {
    static struct OrderOperations orderOp = {
        .getMin = min,
		.getMax = max,
    };
    struct NumberRange* range = (struct NumberRange* )malloc(sizeof(struct NumberRange));
    range->min = ORDER_DEFAULT_DATA;
    range->max = ORDER_DEFAULT_DATA;
    range->getMin = min;
	range->getMax = max;
    return range;
}

int readingsOutsideRange(struct NumberRange *range) {
	return (range->station < min(range))? min(range) : ((range->station > max(range) )? max(range): range->station);
}
int main(void **argc,void *argv[]) {
	struct NumberRange* range = allocNumberRange();
	printf("OutsideRange is %d.\n", readingsOutsideRange(range));
}

· 我想把 readingsOutsideRange函數搬移到 NumberRange 的內部;

struct NumberRange {
    int station;
    int min;
    int max;
    int (*getMin)(struct NumberRange *range);
    int (*getMax)(struct NumberRange *range);
    int (*readingsOutsideRange)(struct NumberRange *range);/*添加*/
};
static int min(struct NumberRange *range) {
    return range->min;
}
static int max(struct NumberRange *range) {
    return range->max;
}
static int _readingsOutsideRange(struct NumberRange *range) {
    return (range->station < min(range))? min(range) : ((range->station > max(range) )? max(range): range->station);
}
struct NumberRange* allocNumberRange(void) {
#define ORDER_DEFAULT_DATA 1
    struct NumberRange* range = (struct NumberRange* )malloc(sizeof(struct NumberRange));
    range->min = ORDER_DEFAULT_DATA;
    range->max = ORDER_DEFAULT_DATA;
    range->getMin = min;
    range->getMax = max;
    range->readingsOutsideRange = _readingsOutsideRange;/*添加*/
    return range;
}


int main(void **argc,void *argv[]) {
    struct NumberRange* range = allocNumberRange();
    range->station = 3;
    printf("OutsideRange is %d.\n", range->readingsOutsideRange(range));/*修改*/
}

10.函數組合成變換

將數據組合起來,將各種函數的操作集合成一個函數。

· 我們經常會將數據"喂"給一個程序,讓它產生很多派生信息。這些派生數據可能在多個不同的地方用到,而計算的邏輯也就會在多個地方重複。
我更願意將所有計算派生數據的邏輯收攏到一處,這樣可以在固定的地方找到和更新這些邏輯,避免重複。
· 本方案的替代方案是 函數組合成類,如何判斷使用哪一個呢?如果代碼中會對源數據進行更新,那麼使用類要好得多。使用變換的話,源數據更新之後會導致與派生數據不一致。
使用方式:
(1)創建一個變化函數,入參是需要變換的記錄,並直接返回記錄的值。
(2)選擇一塊邏輯,將主體移入該函數中,並將結果添加到輸出記錄中。
(3)測試並重覆上述步驟。

範例:
延續使用第8節的例子,用函數組合成變換的方式進行重構。

struct NumberRange {
	int station;
	int min;
	int max;
};
int min(struct NumberRange *range) {
	return range->min;
}
int max(struct NumberRange *range) {
	return range->max;
}
int  readingsOutsideRange(struct NumberRange range) {
	return (range.station < min(&range))? min(&range) : ((range.station > max(&range) )? max(&range): range.station);
}
/*新增一個獲取範圍寬度的函數*/
int  readingsRangeWidth(struct NumberRange range) {
	return max(&range) - min(&range);
}

· 首先,readingsOutsideRange 和 readingsRangeWidth 的兩個函數是獲取的擴展信息,首先將擴展信息放入struct中,然後創建一個計算擴展信息的函數,並逐步把函數移到這裏。

struct NumberRange {
	int station;
	int min;
	int max;
	/*擴展信息*/
	int outsideRange;
	int rangeWidth;
};

struct NumberRange enrichReadings(struct NumberRange range) {
	range.outsideRange = readingsOutsideRange(range);
	range.rangeWidth = readingsRangeWidth(range);
	return range;
}

· 這裏要記住,不能一步到位,沒改一步都要測試,調用的位置要非常仔細。

11.拆分階段

學會了組合,也要學會拆分。每當同一塊代碼在同時處理兩件不同的事情,我就想將其拆分成獨自的模塊,這是運用解耦的思想。因爲到了要修改的時候,我就可以單獨處理每個主題而不用考慮兩個不同的主題。
最簡潔的方法:將一大塊行爲分成順序執行的兩個階段。如果數據不符合要求,你可能需要先對輸入數據做調整。
做法:
(1)將第二階段的代碼提煉成獨立的函數,並測試。(假設只有2段需要拆分的邏輯)
(2)引入一箇中轉數據結構,將其作爲參數添加到提煉的新函數參數列表中,並測試。
(3)判斷入參是否被第一階段代碼使用,是就將入參逐步搬移到 中轉數據結構中,注意每次搬移都要測試一次;
(4)對第一階段的代碼運用提煉函數,將提煉出的函數返回中轉數據結構。
範例:
這裏有一個計算價格的函數:

struct Product {
	int basePrice;
	int discountThreshold;
	int discountRate;
};
struct ShippingMethod {
	int discountedFee;
	int feePerCase;
	int discountThreshold;
};
int priceOrder(struct Product product, int quantity, struct ShippingMethod shippingMethod) {
	const int basePrice = product.basePrice * quantity;
	const int discount = max(quantity - product.discountThreshold, 0) * product.basePrice * product.discountRate;
	const int shippingPerCase = (basePrice > shippingMethod.discountThreshold)? shippingMethod.discountedFee : shippingMethod.feePerCase;
	const int shippingCost = quantity * shippingPerCase;
	int price = basePrice - discount + shippingCost;
	return price;
}

· 該函數中有點混亂,需要逐步拆分,首先拆出shipping配送相關的部分;

int priceOrder(struct Product product, int quantity, struct ShippingMethod shippingMethod) {
	int basePrice = product.basePrice * quantity;
	int discount = max(quantity - product.discountThreshold, 0) * product.basePrice * product.discountRate;
	int price = applyShipping(basePrice, shippingMethod, quantity, discount);
	return price;
}
int applyShipping(int basePrice, struct ShippingMethod shippingMethod, int quantity, int discount) {
	int shippingPerCase = (basePrice > shippingMethod.discountThreshold)? shippingMethod.discountedFee : shippingMethod.feePerCase;
	int shippingCost = quantity * shippingPerCase;
	int price = basePrice - discount + shippingCost;
	return price;
}

· 增加一個結構,審視各個參數,將函數參數逐步放入。shippingMethod第一階段沒用到,可以不放,quantity這個參數可以繼續選擇放不放,我還是想儘可能放進去,得到如下代碼。

struct PriceData {
	int basePrice;
	int quantity;
	int discount;
};
int priceOrder(struct Product product, int quantity, struct ShippingMethod shippingMethod) {
	int basePrice = product.basePrice * quantity;
	int discount = max(quantity - product.discountThreshold, 0) * product.basePrice * product.discountRate;
	struct PriceData priceData = {basePrice, quantity, discount};/*新建結構體*/
	int price = applyShipping(priceData, shippingMethod/*, basePrice, quantity, discount*/);
	return price;
}
int applyShipping(struct PriceData priceData, struct ShippingMethod shippingMethod/*, int basePrice, int quantity, int discount*/) {
	int shippingPerCase = (priceData.basePrice > shippingMethod.discountThreshold)? shippingMethod.discountedFee : shippingMethod.feePerCase;
	int shippingCost = priceData.quantity * shippingPerCase;
	int price = priceData.basePrice - priceData.discount + priceData.shippingCost;
	return price;
}

· 最後將第一階段也組合成一個函數:

struct PriceData {
	int basePrice;
	int quantity;
	int discount;
};
/*新的函數,計算出priceData*/
struct priceData caculatePriceData(struct Product product, int quantity) {
	struct PriceData priceData;
	priceData.basePrice = product.basePrice * quantity;
	priceData.discount = max(quantity - product.discountThreshold, 0) * product.basePrice * product.discountRate;
	priceData.quantity = quantity;
	return priceData;
}
int priceOrder(struct Product product, int quantity, struct ShippingMethod shippingMethod) {
	/*int basePrice = product.basePrice * quantity;
	int discount = max(quantity - product.discountThreshold, 0) * product.basePrice * product.discountRate;*/
	struct PriceData priceData = caculatePriceData(product, quantity);{basePrice, quantity, discount};/*新建結構體*/
	int price = applyShipping(priceData, shippingMethod/*, basePrice, quantity, discount*/);
	return price;
}
int applyShipping(struct PriceData priceData, struct ShippingMethod shippingMethod/*, int basePrice, int quantity, int discount*/) {
	int shippingPerCase = (priceData.basePrice > shippingMethod.discountThreshold)? shippingMethod.discountedFee : shippingMethod.feePerCase;
	int shippingCost = priceData.quantity * shippingPerCase;
	int price = priceData.basePrice - priceData.discount + priceData.shippingCost;
	return price;
}

最後去掉註釋,整理下,得最終結果。


struct PriceData {
	int basePrice;
	int quantity;
	int discount;
};
int priceOrder(struct Product product, int quantity, struct ShippingMethod shippingMethod) {
	struct PriceData priceData = caculatePriceData(product, quantity)return applyShipping(priceData, shippingMethod/*, basePrice, quantity, discount*/);
}
/*第一階段計算出priceData*/
struct priceData caculatePriceData(struct Product product, int quantity) {
	struct PriceData priceData;
	priceData.basePrice = product.basePrice * quantity;
	priceData.discount = max(quantity - product.discountThreshold, 0) * product.basePrice * product.discountRate;
	priceData.quantity = quantity;
	return priceData;
}
/*第二階段計算出price*/
int applyShipping(struct PriceData priceData, struct ShippingMethod shippingMethod) {
	int shippingPerCase = (priceData.basePrice > shippingMethod.discountThreshold)? shippingMethod.discountedFee : shippingMethod.feePerCase;
	int shippingCost = priceData.quantity * shippingPerCase;
	return priceData.basePrice - priceData.discount + priceData.shippingCost;
}

12. 總結

· 本文介紹了簡化邏輯結構的各種方法,主要思想就是用 2 對武器 {提煉,內聯},{組合,拆分}。就像2把快刀,將程序修剪得整整齊齊。這2對武器大部分情況下都是夠用的了。
· 學到東西,就要去實踐,實踐中出現的問題,也請大家給我留言反饋,謝謝!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章