如果你关注代码本身和代码的编写方式,而不是只关心它是否能工作,那么你写代码是有一定的水准。专业开发人员将为未来的自己和“其他人”编写代码,而不仅仅只编写当前能工作就行的代码。在此基础上,简洁代码可以定义为自解释的、易于人理解的、易于更改或扩展的代码。
1. 强类型检查
用===
代替 ==
// 如果处理不当,它会极大地影响程序逻辑。这就像,你想向左走,但由于某种原因,你向右走 0 == false // true 0 === false // false 2 == "2" // true 2 === "2" // false // 例子 const value = "500"; if (value === 500) { console.log(value); // 条件不成立,不会进入 } if (value === "500") { console.log(value); // 条件成立,会进入 }
2.变量
用知名其意的方式为变量命名,通过这种方式,当一个人看到它们时,易于搜索和理解。
不好的方式:
let daysSLV = 10; let y = new Date().getFullYear(); let ok; if (user.age > 30) { ok = true; }
好的方式:
const MAX_AGE = 30; let daysSinceLastVisit = 10; let currentYear = new Date().getFullYear(); ... const isUserOlderThanAllowed = user.age > MAX_AGE;
不要在变量名中添加额外的不需要的单词。
不好的方式:
let nameValue;
let theProduct;
好的方式:
let name;
let product;
不要简写变量上下文。
不好的方式:
const users = ["John", "Marco", "Peter"]; users.forEach(u => { doSomething(); doSomethingElse(); // ... // ... // ... // ... // 当上面代码很多的时候 ,这 `u` 是什么鬼 register(u); });
好的方式:
const users = ["John", "Marco", "Peter"]; users.forEach(user => { doSomething(); doSomethingElse(); // ... // ... // ... // ... register(user); });
不要添加不必要的上下文。
不好的方式:
const user = { userName: "John", userSurname: "Doe", userAge: "28" }; ... user.userName;
好的方式:
const user = { name: "John", surname: "Doe", age: "28" }; ... user.name
3. 函数
使用长而具有描述性的名称。 考虑到函数表示某种行为,函数名称应该是动词或短语,用以说明其背后的意图以及参数的意图。 函数的名字应该说明他们做了什么。
不好的方式:
function notif(user) { // implementation }
好的方式:
function notifyUser(emailAddress) { // implementation }
避免使用大量参数。 理想情况下,函数应该指定两个或更少的参数。 参数越少,测试函数就越容易,参数多的情况可以使用对象。
不好的方式:
function getUsers(fields, fromDate, toDate) { // implementation }
好的方式:
function getUsers({ fields, fromDate, toDate }) { // implementation } getUsers({ fields: ['name', 'surname', 'email'], fromDate: '2019-01-01', toDate: '2019-01-18' });
使用默认参数替代 ||
操作
不好的方式:
function createShape(type) { const shapeType = type || "cube"; // ... }
好的方式:
function createShape(type = "cube") { // ... }
一个函数应该只做一件事,不要在一个函数中执行多个操作。
不好的方式:
function notifyUsers(users) { users.forEach(user => { const userRecord = database.lookup(user); if (userRecord.isVerified()) { notify(user); } }); }
好的方式:
function notifyVerifiedUsers(users) { users.filter(isUserVerified).forEach(notify); } function isUserVerified(user) { const userRecord = database.lookup(user); return userRecord.isVerified(); }
使用Object.assign设置对象默认值。
不好的方式:
const shapeConfig = { type: "cube", 200, height: null }; function createShape(config) { config.type = config.type || "cube"; config.width = config.width || 250; config.height = config.height|| 250; } createShape(shapeConfig);
好的方式:
const shapeConfig = { type: "cube", 200 // Exclude the 'height' key }; function createShape(config) { config = Object.assign( { type: "cube", 250, height: 250 }, config ); ... } createShape(shapeConfig);
不要使用标志作为参数,因为它们告诉函数做的比它应该做的多。
不好的方式:
function createFile(name, isPublic) { if (isPublic) { fs.create(`./public/${name}`); } else { fs.create(name); } }
好的方式:
function createFile(name) { fs.create(name); } function createPublicFile(name) { createFile(`./public/${name}`); }
不要污染全局变量。 如果需要扩展现有对象,请使用ES类和继承,而不是在原生对象的原型链上创建函数。
不好的方式:
Array.prototype.myFunc = function myFunc() { // implementation };
好的方式:
class SuperArray extends Array { myFunc() { // implementation } }
4. 条件
避免使用反面条件。
不好的方式:
function isUserNotBlocked(user) { // implementation } if (!isUserNotBlocked(user)) { // implementation }
好的方式:
function isUserBlocked(user) { // implementation } if (isUserBlocked(user)) { // implementation }
使用条件简写。这可能微不足道,但值得一提。仅对布尔值使用此方法,并且如果你确信该值不会是undefined
或null
的,则使用此方法。
不好的方式:
if (isValid === true) { // do something... } if (isValid === false) { // do something... }
好的方式:
if (isValid) { // do something... } if (!isValid) { // do something... }
尽可能避免条件句,而是使用多态性和继承。
不好的方式:
class Car { // ... getMaximumSpeed() { switch (this.type) { case "Ford": return this.someFactor() + this.anotherFactor(); case "Mazda": return this.someFactor(); case "McLaren": return this.someFactor() - this.anotherFactor(); } } }
好的方式:
class Car { // ... } class Ford extends Car { // ... getMaximumSpeed() { return this.someFactor() + this.anotherFactor(); } } class Mazda extends Car { // ... getMaximumSpeed() { return this.someFactor(); } } class McLaren extends Car { // ... getMaximumSpeed() { return this.someFactor() - this.anotherFactor(); } }
5. 类
class
是JavaScript中新的语法糖。一切工作就像以前的原型,只是它现在看起来不同,你应该更喜欢他们比ES5普通功能。
不好的方式:
const Person = function(name) { if (!(this instanceof Person)) { throw new Error("Instantiate Person with `new` keyword"); } this.name = name; }; Person.prototype.sayHello = function sayHello() { /**/ }; const Student = function(name, school) { if (!(this instanceof Student)) { throw new Error("Instantiate Student with `new` keyword"); } Person.call(this, name); this.school = school; }; Student.prototype = Object.create(Person.prototype); Student.prototype.constructor = Student; Student.prototype.printSchoolName = function printSchoolName() { /**/ };
好的方式:
class Person { constructor(name) { this.name = name; } sayHello() { /* ... */ } } class Student extends Person { constructor(name, school) { super(name); this.school = school; } printSchoolName() { /* ... */ } }
使用链接。许多库(如jQuery和Lodash)都使用这种模式。在类中,只需在每个函数的末尾返回this
就可以将更多的该类方法链接到它上。
不好的方式:
class Person { constructor(name) { this.name = name; } setSurname(surname) { this.surname = surname; } setAge(age) { this.age = age; } save() { console.log(this.name, this.surname, this.age); } } const person = new Person("John"); person.setSurname("Doe"); person.setAge(29); person.save();
好的方式:
class Person { constructor(name) { this.name = name; } setSurname(surname) { this.surname = surname; // Return this for chaining return this; } setAge(age) { this.age = age; // Return this for chaining return this; } save() { console.log(this.name, this.surname, this.age); // Return this for chaining return this; } } const person = new Person("John") .setSurname("Doe") .setAge(29) .save();
这只是改进代码的一小部分。一般生活入,这里所说的原则是人们通常不遵守的原则。他们尝试着去做,但出于各种原因,就没有坚持下去。也许在项目开始时,代码是简洁的,但是当要在截止日期前完成时,这些原则常常被忽略,并被转移到“TODO”或“REFACTOR”部分。在这一点上,你的客户更希望您在最后期限之前完成任务,而不是编写简洁的代码。
扩展:
- 有意義的命名 讓名稱代表意圖,使之名符其實
- 避免误导 (命名) • 小写的 L 或是⼤大写的 O • ⼀一群帳⼾戶:accountList ❌ • 三⾓角形斜邊 (hypotenuse) : hp ❌ int a = 1; if (O == l) a = O1; else l = 01;
- 有意義的區別 • 別⽤用序列列命名 (a1, a2, … , aN) • 无意义的字詞是多餘的 ProductInfo vs ProductData • 参数名改⽤用 source, destination 會更更好理理解 public static void copyCharts(char a1[], char a2[]) { for (int i = 0; i < a1.length; i++) { a2[i] = a1[i]; } }
- 能念念出來來可以幫助記憶 genymdhms() 字⾯面上看起來來是⼀一個產⽣生 timestamp 的⽅方法,但無法藉 由發⾳音幫助記憶,不是個良好的命名。
- 可被搜寻的名字 • 長命名勝過短命名,常数作為命名,搜尋時更更準確,不易易 造成 bug 比 5 還來來的容易易被找到WORK_DAYS_PER_WEEK
- 不要⽤用匈牙利利命名法 https://zh.wikipedia.org/wiki/匈牙利利命名法 • 变数前加上型別 lAccountNum:變數是⼀一個長整數("l") arru8NumberList:變數是⼀一個無符號8位元整型陣列列("arru8") szName:變數是⼀一個零結束字串串(”sz") 成員的字⾸首 • 不要額外加上字⾸首 m_dsc = name;
- 類別的命名 • ⽤用名詞 Customer • 避免使⽤用含糊不清的字 Data, Info, Manager, Processor
- ⽅方法的命名 • ⽤用動詞 postPayment • Javabean ❖ 取出器(accessors): get ❖ 修改器(mutators): set ❖ 判定器(predicates): is
- 不要裝可愛 • 清楚闡述比娛樂還來來的重要多了了。 DeleteItems 命名成 HolyHandGrenade (神聖⼿手榴彈) EatMyShort(來來吃我褲襠) 來來表達 abort
- 別加無理理由的上下⽂文資訊 • 在同⼀一個應⽤用中,不需要在所有類別加上共同的字⾸首,如 此反⽽而造成各 IDE 中⾃自動補⿑齊功能無法於第⼀一時間補⿑齊。 • 較少的名稱若若可以清楚表達,勝過於長名稱。
- 遵循上⾯面所說的規則,然後觀察你的程式碼 可讀性是否獲得了了改善
- 函式 函式是所有程式組成的⾸首要基礎
- 簡短 • 函式的⾸首要準則就是要簡短。 • 第⼆二項準則,就是要比第⼀一項還要簡短。
- 只做⼀一件事情 • 函式應該做⼀一件事,它們應該把這件事做好,⽽而且只做這 件事。
- Switch 敘述 • 太過冗長 • 做超過⼀一件事 • 違反單⼀一職責原則 (SRP) • 當加入新形態時,違反開放封閉原則 (OCP) 1 public Money caluculatePay(Employee e) throws InvalidEmployeeType { 2 switch (e.type) { 3 case COMMISSIONED: 4 return calculateCommissionedPay(e); 5 case HOURLY: 6 return calculateHourlyPay(e); 7 default: 8 throw new InvalidEmployeeType(e.type); 9 } 10 }
- Switch 敘述 • 把 Switch 埋在抽象⼯工廠 (Abstract Factory) 底下 1 public Money caluculatePay(Employee e) throws InvalidEmployeeType { 2 switch (e.type) { 3 case COMMISSIONED: 4 return calculateCommissionedPay(e); 5 case HOURLY: 6 return calculateHourlyPay(e); 7 default: 8 throw new InvalidEmployeeType(e.type); 9 } 10 }
- 使⽤用具描述能⼒力力的名稱 • 『當每個你看到的程式,執⾏行行結果都與你想的差不多,你 會察覺到你正⼯工作在 Clean Code 之上』
- 函式的參參數 • 最理理想的是 (零參參數函式:niladic) • 其次是 (單函式參參數:monadic) • 再者才是 兩兩個(雙參參數函式:dyadic)。 • 非必要使⽤用 (三參參數函式:triadic),(多參參數函式:polyadic)
- 旗標參參數 • 將布林林傳給函式,是⼀一種很恐怖的習慣 • 這會造成該函式不只做⼀一件事情 True: doSomething, False: SaySomething
- 兩兩個參參數的函式的命名 • writeField(name) vs writeField(outputStream, name) 雖然兩兩個都很清楚,但第⼆二個要做短暫的停留留思考,容 易易令⼈人忽略略進⽽而有機會產⽣生 bug。 • assertEquals(expected, actual) 你是否曾經搞錯 expected, actual 這兩兩者的位置?
- 三個參參數的函式 • assertEquals(message, expected, actual) 看到 message 卻以為他是 expected? • 使⽤用者容易易被三個參參數絆住或頓住,需要反覆查看參參數。
- 要無副作⽤用 • 保證只做⼀一件事! • 不要在暗地裡偷偷做了了其他事情 ‣ 轉換成其他傳參參數傳給其他函式 ‣ 變成全域變數
- 使⽤用”例例外處理理” 取代 “回傳錯誤碼” • try/catch 會混淆結構,將函式從 try 和 catch 中提取出來來 1 public void delete(Page page) { 2 try { 3 deletePageAndAllReferences(pages); 4 } catch (Exception e) { 5 logError(e); 6 } 7 }
- 不要重複⾃自⼰己 • DRY (Don’t Repeat Yourself.) • 重複的程式碼是軟體裡所有邪惡惡的根源
- 結構化程式設計 • 每個函式,以及每個函式理理的區塊,都應該只有⼀一個進入 點及⼀一個離開點。 • 我們現在都⽤用 Guard Clause 原則了了
- 如何寫出這種函式 • 將函式分開,重新命名,減少重複 • 我不會⼀一開始就這樣寫,我也不認為有⼈人可以辦得到
- 函式是這個語⾔言的動詞 類別是這個語⾔言的名詞 如果你遵循這章的準則: 你的函式會簡短,有良好的命名,以及漂亮的結構
- 註解 真相永遠只存在⼀一個地⽅方:程式碼
- 註解無法彌補糟糕的程式碼 • 整潔具有表達⼒力力⼜又極少使⽤用註解的程式碼,遠優於雜亂⼜又 滿是註解的程式碼。
- ⽤用程式碼表達你的本意 • 多想幾秒鐘,在⼤大部分情況下的註解,都可以簡單地融入 到建立的函式名稱中。
- 有益的註解 • 真正有益的註解,是你想辦法不寫它的註解
- 法律律型的註解 • 有些需要撰寫標準規範,或者著作權聲明、作者資訊等, 就是必須且合理理的註解。 比較好的做法是讓註解去參參考⼀一個外部註解 // Copyright (c) 2003, 2004, 2005 by Object Mentor, Inc. All rights reserved. // Released under the terms of GNU General Public License version 2 or late.
- 對於後果的告誡 • ⽤用於警告其他⼯工程師會出現某種特殊後果的註解,也是有 ⽤用的。
- TODO (代辦事項) 註解 • TODO 是⼯工程師認為應該要完成的事情,但基於某些原因無法在此 時做到。 ‣ 提醒移除過時的功能 ‣ 請某⼈人注意這個問題 ‣ 請某⼈人尋找更更好的命名 ‣ 依賴於某個未來來計畫⽽而提醒所需要的修改 • 最後不管如何,都不應該成為讓糟糕程式碼留留在系統裡的藉⼝口。
- 糟糕的註解 • 喃喃⾃自語 • 多餘的註解 • 規定型註解 • ⼲干擾型註解
- 糟糕的註解 - 喃喃⾃自語 • 如果你決定要寫註解,你就應該要花上必要的時 間,確保你寫出的是最好的註解
- 糟糕的註解 - 多餘的註解 • 含有開頭註解的簡單函式,讀這段註解可能比讀 這段程式更更花時間 1 // Utility method that returns when this.closed is true. Throws an exception 2 // if the timeout is reached. 3 public synchronized void waitForClose(final long timeoutMillis) 4 throws Exception 5 { 6 if (!closed) { 7 wait(timeoutMillis); 8 if(!closed) 9 throw new Exception ("MockResponseSender could not be closed"); 10 } 11 }
- 糟糕的註解 - 規定型註解 • 含有開頭註解的簡單函式,讀這段註解可能比讀 這段程式更更花時間 1 /** 2 * 3 * @param title The title of the CD 4 * @param author The author of the CD 5 * @param tracks The number of tracks on the CD 6 * @param durationInMinutes The duration of the CD in munutes 7 */ 8 public void addCD(String title, String author, int tracks, int durationInMinutes) 9 { 10 CD cd = new CD(); 11 cd.title = title; 12 cd.author = author; 13 cd.tracks = tracks; 14 cd.duration = durationInMinutes; 15 cdList.add(cd); 16 }
- 糟糕的註解 - ⼲干擾型註解 • 沒有被⼲干擾到,真的嗎? 1 /* 2 * Default constructor. 3 */ 4 protected AnnualDateRule() {} 5 6 /* The day of the month. */ 7 private int dayOfMonth; 8 9 /* 10 * Return the day of the month. 11 * 12 * @return the day of the month. 13 */ 14 public int getDatofMonth() 15 { 16 return dayOfMonth; 17 }
- 當你可以使⽤用函式或者變數時就別 ⽤用註解 1 // does the module from the global list<mod> depend on the 2 // subsystem we are part of? 3 if (semodule.getDependSubSystems().contains(subSysMod.getSubSystem()) 4 5 ArrayList moduleDpendees = smodule.getDependSubSystems(); 6 String outSubSystem = subSysMod.getSubSystem(); 7 if (moduleDpendees.contains(ourSubSystem))
- 出處及署名 • 想要儲存該類的訊息,原始碼版本控制系統(e.g. GIT, SVN)是⼀一個比較好的選擇。 /* Added by Rick */
- 被註解起來來的程式碼 • 把程式碼註解掉的⾏行行為,這是很討⼈人厭的,不要這樣做!! • 我可以刪掉嗎?還是要繼續留留著?
- 不要替糟糕的程式碼寫註解,你應該重寫程式碼
- 編排 良好的編排有助於閱讀,也是專業
- 編排的⽬目的 • 程式的編排實在是太重要了了。 • 程式碼的風格與可讀性,會影響程式的可維護性及可擴充 性。
- 垂直的編排 • 報紙的啟發 • 最重要的概念念會最先出現,希望⽤用最少的細節來來表達他們。
- 垂直的編排 • 概念念間的垂直空⽩白區隔 • 空⽩白⾏行行代表⼀一個視覺上的提⽰示,提⽰示著空⽩白⾏行行的後⽅方將接 續⼀一個新⽽而不同的概念念。 1 private static final Pattern pattern = Pattern.compile("'''(.+?)'''", Pattern.MULTILINE); 2 3 public BoldWidget(ParentWidge parent, String text) throws Exception { 4 super(parent); 5 6 Matcher match = pattern.matcher(text); 7 match.find(); 8 9 addChildWidgets(match.group(1)); 10 }
- 垂直的密度 • 如果垂直空⽩白區分開個個概念念,那垂直密度則意味著密切 相關的程度 • ⼀一看就知道有兩兩個變數,⼀一個⽅方法的類別。 1 public class ReporterConfig { 2 private String m_className; 3 private List<Property> m_properties = new ArrayList<Property>(); 4 5 public void addProperty($Property property) { 6 m_properties.add(property); 7 } 8 }
- 垂直的距離 • 相近的概念念,在垂直編排上,要盡可能的靠近 • 相近的概念念不該被分散在不同的檔案裡 • 垂直的距離⽤用來來衡量量他們對彼此的了了解有多重要
- 垂直的距離 - 變數宣告 • 盡可能靠近變數被使⽤用的地⽅方,因為我們的函⽰示非常簡短。 1 public int countTestCase() { 2 int count = 0; 3 for (Test each: tests) 4 count += each.countTestCased(); 5 return count; 6 }
- 垂直的距離 - 實體變數 • 實體變數都應該被宣告在⼀一個⼤大家都熟悉的地⽅方,通常放 在類別的最上⽅方。
- 垂直的距離 - 相依的函式 • 如果函式呼叫了了另⼀一個函式,那兩兩個函式在垂直編排上要 盡可能靠近。由上⽽而下,增加模組的可讀性。
- 垂直的距離 - 概念念相似性 • 函式的概念念上有⾼高度相似性時,垂直距離應該愈短愈好。 • 概念念上的⾼高度相似性 • 類似的命名規則 • 就算沒有互相呼叫,也應該盡可能放在⼀一起
- 水平的編排 - 空⽩白間格和密度 • 運算⼦子(assignment operators) 的左右都加入空⽩白,使其 更更為突出 • 函式名稱和⼩小左括號之間不空⽩白 • 空⽩白的另⼀一種⽤用途是強調運算⼦子的優先權
- 水平的對⿑齊 • 沒有幫助,我傾向不再為宣告與設定敘述作特別的對⿑齊 1 public int FitNesseExpediter(Scoket s, 2 FitNesseContext context) throws Exception 3 { 4 this.context = context; 5 scocket = s; 6 input = s.getInputStream(); 7 output = s.getOutputStream(); 8 reguestParsingTimeLimit = 1000, 9 10 }
- 縮排 • ⼀一個原始檔是個階層結構,⽽而非⼤大綱結構。 • 讓視野的層次結構更更顯⽽而易易⾒見見 ‣ 檔案 ‣ 類別 ‣ ⽅方法 ‣ 程式區塊 ‣ ⺟母程式 ‣ ⼦子程式
- 你們團隊的共同準則?
- 錯誤處理理 錯誤處理理很重要,但不該模糊原本程式碼邏輯
- 使⽤用例例外事件⽽而非回傳錯誤碼 • 比較好的做法是,在你遇到⼀一個錯誤的時候,拋出⼀一個例例 外事件。如此,呼叫程式碼就會變得乾淨許多。
- 在開頭寫下你的 Try-Catch-Finally 敘述 • 如果你寫的程式可能會拋出例例外事件,請養成 try-catch- finally 成為開頭敘述的好習慣。
- 提供發⽣生例例外的相關資訊 • 請傳遞⾜足夠的資訊給 catch 區,使例例外能夠被記錄下來來。
- 不要回傳 null (空值) • 不要回傳 null ,因為只要有⼀一處忘記檢查 null,就會導致 程式進入混亂無法控制的狀狀態。 • 如果你想讓⽅方法回傳 null ,那不如試著拋出⼀一個例例外事 件,或回傳⼀一個 SPECIAL CASE 物件。
- 不要傳遞 null • ⽅方法回傳 null 是糟糕的⾏行行為。然⽽而傳遞 null 到⽅方法裡是更更 糟糕的⾏行行為。 • 當你養成這樣的習慣之後你在寫程式碼時,就會避免傳遞 null 給函式,因為你知道如果 null 出現在參參數裡,代表了了 潛在問題的預兆。 最終能⼤大幅降低出錯的可能。
- Clean Code 是易易讀的,但也是耐⽤用的,這兩兩者不是互相 衝突的⽬目標。 當我們將錯誤處理理獨立於主要邏輯的可讀程式,我們就能 獨立地處理理它,並且在維護性上也向前邁進⼀一⼤大步。
- 類別 更更⾼高層次:《程式碼敘述和程式碼所構成的函式》
- 類別的結構 • 公⽤用函式(Public function) 應該接在⼀一連串串變數宣告的後⽅方 • 我們喜歡將私有⼯工具函式(Private function) 緊接在呼叫的 公⽤用函⽰示後⽅方 • 以上兩兩點遵循 『降層法則(Stepdown rule)』,也有助於 『讓程式閱讀』
- 封裝 • 先想辦法保持變數以及⼯工具函式的私有性 (private) ,放鬆 封裝限制是最後不得已的⼿手段 • 優先順序:1. protected(⽅方便便進⾏行行測試) 2. public
- 類別要夠簡短 • 在函式裡,計算真正的程式⾏行行數,來來衡量量函式的⼤大⼩小在類 別裡,計算職責的數量量,來來衡量量類別的⼤大⼩小
- 單⼀一職責原則 (SRP) • 主張⼀一個類別或⼀一個模組只能有⼀一個修改的理理由。 • 我們強調的主張:系統是由許多⼩小型類別左組成,⽽而不是 由少數幾個⼤大型類別組成 • 每個⼩小類別『封裝單⼀一的職責』、『只有⼀一個修改的裡由』 以及『與其他少數幾個類別合作來來完成系統要求的⾏行行為』
- 凝聚性 • 保持凝聚性會得到許多⼩小型的類別 • 在⽅方法裡操縱愈多的變數,代表這個⽅方法更更凝聚於該類別。 • 若若有某個類別的每個變數都被使⽤用在每個⽅方法中,那麼這 個類別就是具有最⼤大凝聚性的類別
- 凝聚性 • 當凝聚性⾼高,代表類別裡的⽅方法和變數是相互依賴的, 並且相互結和為邏輯上的整體。 • 當類別失去凝聚性的時候,就把他們拆開來來吧!
- 羽化 透過⽻化設計來來達到整潔
- 一個簡單的設計 ① 執⾏行行完所有的測試 ② 沒有重複的部分 ③ 表達程式設計師的本意 ④ 最⼩小化類別和⽅方法的數量量 這些守則,根據重要性來來排 序。
- 簡單設計守則 1: 執⾏行行完所有的測試 • 類別若若遵守單⼀一職責原則 (SRP),那麼測試就說⼀一件很簡 單的事。 • 系統以物件導向的主要⽬目標,讓程式有低耦合度和⾼高凝聚 度。 所以撰寫測試程式,最終帶來來了了更更佳的設計。
- 簡單設計守則 2~4: 程式重構 • ⼀一旦有了了測試,就能保持程式和類別的整潔,利利⽤用逐步增 加的⽅方式來來進⾏行行重構。 • 重構的過程中,我們可以應⽤用與良好軟體設計有關的所有 知識。 ➡ 增加凝聚性 ➡ 降低耦合度 ➡ 分離關注點 ‣ 模組化系統關注點 ‣ 替函式和類別瘦⾝身 ‣ 良好的命名
- 禁⽌止重複 • 重複程式碼是『良好設計系統』的主要敵⼈人。他代表額外 的⼯工作、額外的風險及額外不必要的複雜性。
- 具表達⼒力力 • 『選擇⼀一個良好的名稱』 • 『讓函式和類別簡短』 • 『標準命名法』 • 『良好的單元測試』也具良好的表達⼒力力,主要⽬目的『⽤用範例例學』 • 多花點時間在你的函式和類別上,選擇較好的名稱,將⼤大型函式 拆解成⼩小型函式 • ⽤用⼼心是⼀一項珍貴的資源
- 最⼩小化類別及⽅方法的數量量 • ⼩小⼼心『消除重複程式碼』,『單⼀一職責原則』這類的基本 觀念念會做過頭,⽽而產⽣生太多微型的類別和⽅方法。 所以該守 則建議我們,必須讓類別及⽅方法的數量量保持少少的。 • 但這條守則卻是簡單設計四守則裡優先最低的。所以測 試、消除重複及表達⼒力力等其他三條守則,才是最重要的。
- 簡單的設計 ① 執⾏行行完所有的測試 ② 沒有重複的部分 ③ 表達程式設計師的本意 ④ 最⼩小化類別和⽅方法的數量量