无暇代码(js的整洁之道)

        如果你关注代码本身和代码的编写方式,而不是只关心它是否能工作,那么你写代码是有一定的水准。专业开发人员将为未来的自己和“其他人”编写代码,而不仅仅只编写当前能工作就行的代码在此基础上,简洁代码可以定义为自解释的、易于人理解的、易于更改或扩展的代码。

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”部分。在这一点上,你的客户更希望您在最后期限之前完成任务,而不是编写简洁的代码。

扩展:

  1. 有意義的命名 讓名稱代表意圖,使之名符其實
  2. 避免误导 (命名) • 小写的 L 或是⼤大写的 O • ⼀一群帳⼾戶:accountList ❌ • 三⾓角形斜邊 (hypotenuse) : hp ❌ int a = 1; if (O == l) a = O1; else l = 01;
  3. 有意義的區別 • 別⽤用序列列命名 (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]; } }
  4. 能念念出來來可以幫助記憶 genymdhms() 字⾯面上看起來來是⼀一個產⽣生 timestamp 的⽅方法,但無法藉 由發⾳音幫助記憶,不是個良好的命名。
  5. 可被搜寻的名字 • 長命名勝過短命名,常数作為命名,搜尋時更更準確,不易易 造成 bug 比 5 還來來的容易易被找到WORK_DAYS_PER_WEEK
  6. 不要⽤用匈牙利利命名法 https://zh.wikipedia.org/wiki/匈牙利利命名法 • 变数前加上型別 lAccountNum:變數是⼀一個長整數("l") arru8NumberList:變數是⼀一個無符號8位元整型陣列列("arru8") szName:變數是⼀一個零結束字串串(”sz") 成員的字⾸首 • 不要額外加上字⾸首 m_dsc = name;
  7.  類別的命名 • ⽤用名詞 Customer • 避免使⽤用含糊不清的字 Data, Info, Manager, Processor
  8. ⽅方法的命名 • ⽤用動詞 postPayment • Javabean ❖ 取出器(accessors): get ❖ 修改器(mutators): set ❖ 判定器(predicates): is
  9. 不要裝可愛 • 清楚闡述比娛樂還來來的重要多了了。 DeleteItems 命名成 HolyHandGrenade (神聖⼿手榴彈) EatMyShort(來來吃我褲襠) 來來表達 abort
  10. 別加無理理由的上下⽂文資訊 • 在同⼀一個應⽤用中,不需要在所有類別加上共同的字⾸首,如 此反⽽而造成各 IDE 中⾃自動補⿑齊功能無法於第⼀一時間補⿑齊。 • 較少的名稱若若可以清楚表達,勝過於長名稱。
  11. 遵循上⾯面所說的規則,然後觀察你的程式碼 可讀性是否獲得了了改善
  12. 函式 函式是所有程式組成的⾸首要基礎
  13. 簡短 • 函式的⾸首要準則就是要簡短。 • 第⼆二項準則,就是要比第⼀一項還要簡短。
  14. 只做⼀一件事情 • 函式應該做⼀一件事,它們應該把這件事做好,⽽而且只做這 件事。
  15. 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 }
  16. 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 }
  17. 使⽤用具描述能⼒力力的名稱 • 『當每個你看到的程式,執⾏行行結果都與你想的差不多,你 會察覺到你正⼯工作在 Clean Code 之上』
  18. 函式的參參數 • 最理理想的是 (零參參數函式:niladic) • 其次是 (單函式參參數:monadic) • 再者才是 兩兩個(雙參參數函式:dyadic)。 • 非必要使⽤用 (三參參數函式:triadic),(多參參數函式:polyadic)
  19. 旗標參參數 • 將布林林傳給函式,是⼀一種很恐怖的習慣 • 這會造成該函式不只做⼀一件事情 True: doSomething, False: SaySomething
  20. 兩兩個參參數的函式的命名 • writeField(name) vs writeField(outputStream, name) 雖然兩兩個都很清楚,但第⼆二個要做短暫的停留留思考,容 易易令⼈人忽略略進⽽而有機會產⽣生 bug。 • assertEquals(expected, actual) 你是否曾經搞錯 expected, actual 這兩兩者的位置?
  21. 三個參參數的函式 • assertEquals(message, expected, actual) 看到 message 卻以為他是 expected? • 使⽤用者容易易被三個參參數絆住或頓住,需要反覆查看參參數。
  22. 要無副作⽤用 • 保證只做⼀一件事! • 不要在暗地裡偷偷做了了其他事情 ‣ 轉換成其他傳參參數傳給其他函式 ‣ 變成全域變數
  23. 使⽤用”例例外處理理” 取代 “回傳錯誤碼” • try/catch 會混淆結構,將函式從 try 和 catch 中提取出來來 1 public void delete(Page page) { 2 try { 3 deletePageAndAllReferences(pages); 4 } catch (Exception e) { 5 logError(e); 6 } 7 }
  24. 不要重複⾃自⼰己 • DRY (Don’t Repeat Yourself.) • 重複的程式碼是軟體裡所有邪惡惡的根源
  25. 結構化程式設計 • 每個函式,以及每個函式理理的區塊,都應該只有⼀一個進入 點及⼀一個離開點。 • 我們現在都⽤用 Guard Clause 原則了了
  26. 如何寫出這種函式 • 將函式分開,重新命名,減少重複 • 我不會⼀一開始就這樣寫,我也不認為有⼈人可以辦得到
  27. 函式是這個語⾔言的動詞 類別是這個語⾔言的名詞 如果你遵循這章的準則: 你的函式會簡短,有良好的命名,以及漂亮的結構
  28. 註解 真相永遠只存在⼀一個地⽅方:程式碼
  29. 註解無法彌補糟糕的程式碼 • 整潔具有表達⼒力力⼜又極少使⽤用註解的程式碼,遠優於雜亂⼜又 滿是註解的程式碼。
  30. ⽤用程式碼表達你的本意 • 多想幾秒鐘,在⼤大部分情況下的註解,都可以簡單地融入 到建立的函式名稱中。
  31. 有益的註解 • 真正有益的註解,是你想辦法不寫它的註解
  32. 法律律型的註解 • 有些需要撰寫標準規範,或者著作權聲明、作者資訊等, 就是必須且合理理的註解。 比較好的做法是讓註解去參參考⼀一個外部註解 // 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.
  33. 對於後果的告誡 • ⽤用於警告其他⼯工程師會出現某種特殊後果的註解,也是有 ⽤用的。
  34. TODO (代辦事項) 註解 • TODO 是⼯工程師認為應該要完成的事情,但基於某些原因無法在此 時做到。 ‣ 提醒移除過時的功能 ‣ 請某⼈人注意這個問題 ‣ 請某⼈人尋找更更好的命名 ‣ 依賴於某個未來來計畫⽽而提醒所需要的修改 • 最後不管如何,都不應該成為讓糟糕程式碼留留在系統裡的藉⼝口。
  35. 糟糕的註解 • 喃喃⾃自語 • 多餘的註解 • 規定型註解 • ⼲干擾型註解
  36. 糟糕的註解 - 喃喃⾃自語 • 如果你決定要寫註解,你就應該要花上必要的時 間,確保你寫出的是最好的註解
  37. 糟糕的註解 - 多餘的註解 • 含有開頭註解的簡單函式,讀這段註解可能比讀 這段程式更更花時間 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 }
  38. 糟糕的註解 - 規定型註解 • 含有開頭註解的簡單函式,讀這段註解可能比讀 這段程式更更花時間 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 }
  39. 糟糕的註解 - ⼲干擾型註解 • 沒有被⼲干擾到,真的嗎? 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 }
  40. 當你可以使⽤用函式或者變數時就別 ⽤用註解 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))
  41. 出處及署名 • 想要儲存該類的訊息,原始碼版本控制系統(e.g. GIT, SVN)是⼀一個比較好的選擇。 /* Added by Rick */
  42. 被註解起來來的程式碼 • 把程式碼註解掉的⾏行行為,這是很討⼈人厭的,不要這樣做!! • 我可以刪掉嗎?還是要繼續留留著?
  43. 不要替糟糕的程式碼寫註解,你應該重寫程式碼
  44.  編排 良好的編排有助於閱讀,也是專業
  45. 編排的⽬目的 • 程式的編排實在是太重要了了。 • 程式碼的風格與可讀性,會影響程式的可維護性及可擴充 性。
  46. 垂直的編排 • 報紙的啟發 • 最重要的概念念會最先出現,希望⽤用最少的細節來來表達他們。
  47. 垂直的編排 • 概念念間的垂直空⽩白區隔 • 空⽩白⾏行行代表⼀一個視覺上的提⽰示,提⽰示著空⽩白⾏行行的後⽅方將接 續⼀一個新⽽而不同的概念念。 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 }
  48.  垂直的密度 • 如果垂直空⽩白區分開個個概念念,那垂直密度則意味著密切 相關的程度 • ⼀一看就知道有兩兩個變數,⼀一個⽅方法的類別。 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 }
  49. 垂直的距離 • 相近的概念念,在垂直編排上,要盡可能的靠近 • 相近的概念念不該被分散在不同的檔案裡 • 垂直的距離⽤用來來衡量量他們對彼此的了了解有多重要
  50. 垂直的距離 - 變數宣告 • 盡可能靠近變數被使⽤用的地⽅方,因為我們的函⽰示非常簡短。 1 public int countTestCase() { 2 int count = 0; 3 for (Test each: tests) 4 count += each.countTestCased(); 5 return count; 6 }
  51. 垂直的距離 - 實體變數 • 實體變數都應該被宣告在⼀一個⼤大家都熟悉的地⽅方,通常放 在類別的最上⽅方。
  52. 垂直的距離 - 相依的函式 • 如果函式呼叫了了另⼀一個函式,那兩兩個函式在垂直編排上要 盡可能靠近。由上⽽而下,增加模組的可讀性。
  53. 垂直的距離 - 概念念相似性 • 函式的概念念上有⾼高度相似性時,垂直距離應該愈短愈好。 • 概念念上的⾼高度相似性 • 類似的命名規則 • 就算沒有互相呼叫,也應該盡可能放在⼀一起
  54. 水平的編排 - 空⽩白間格和密度 • 運算⼦子(assignment operators) 的左右都加入空⽩白,使其 更更為突出 • 函式名稱和⼩小左括號之間不空⽩白 • 空⽩白的另⼀一種⽤用途是強調運算⼦子的優先權
  55. 水平的對⿑齊 • 沒有幫助,我傾向不再為宣告與設定敘述作特別的對⿑齊 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 }
  56. 縮排 • ⼀一個原始檔是個階層結構,⽽而非⼤大綱結構。 • 讓視野的層次結構更更顯⽽而易易⾒見見 ‣ 檔案 ‣ 類別 ‣ ⽅方法 ‣ 程式區塊 ‣ ⺟母程式 ‣ ⼦子程式
  57. 你們團隊的共同準則?
  58. 錯誤處理理 錯誤處理理很重要,但不該模糊原本程式碼邏輯
  59. 使⽤用例例外事件⽽而非回傳錯誤碼 • 比較好的做法是,在你遇到⼀一個錯誤的時候,拋出⼀一個例例 外事件。如此,呼叫程式碼就會變得乾淨許多。
  60. 在開頭寫下你的 Try-Catch-Finally 敘述 • 如果你寫的程式可能會拋出例例外事件,請養成 try-catch- finally 成為開頭敘述的好習慣。
  61. 提供發⽣生例例外的相關資訊 • 請傳遞⾜足夠的資訊給 catch 區,使例例外能夠被記錄下來來。
  62. 不要回傳 null (空值) • 不要回傳 null ,因為只要有⼀一處忘記檢查 null,就會導致 程式進入混亂無法控制的狀狀態。 • 如果你想讓⽅方法回傳 null ,那不如試著拋出⼀一個例例外事 件,或回傳⼀一個 SPECIAL CASE 物件。
  63. 不要傳遞 null • ⽅方法回傳 null 是糟糕的⾏行行為。然⽽而傳遞 null 到⽅方法裡是更更 糟糕的⾏行行為。 • 當你養成這樣的習慣之後你在寫程式碼時,就會避免傳遞 null 給函式,因為你知道如果 null 出現在參參數裡,代表了了 潛在問題的預兆。 最終能⼤大幅降低出錯的可能。
  64. Clean Code 是易易讀的,但也是耐⽤用的,這兩兩者不是互相 衝突的⽬目標。 當我們將錯誤處理理獨立於主要邏輯的可讀程式,我們就能 獨立地處理理它,並且在維護性上也向前邁進⼀一⼤大步。
  65. 類別 更更⾼高層次:《程式碼敘述和程式碼所構成的函式》
  66. 類別的結構 • 公⽤用函式(Public function) 應該接在⼀一連串串變數宣告的後⽅方 • 我們喜歡將私有⼯工具函式(Private function) 緊接在呼叫的 公⽤用函⽰示後⽅方 • 以上兩兩點遵循 『降層法則(Stepdown rule)』,也有助於 『讓程式閱讀』
  67. 封裝 • 先想辦法保持變數以及⼯工具函式的私有性 (private) ,放鬆 封裝限制是最後不得已的⼿手段 • 優先順序:1. protected(⽅方便便進⾏行行測試) 2. public
  68. 類別要夠簡短 • 在函式裡,計算真正的程式⾏行行數,來來衡量量函式的⼤大⼩小在類 別裡,計算職責的數量量,來來衡量量類別的⼤大⼩小
  69. 單⼀一職責原則 (SRP) • 主張⼀一個類別或⼀一個模組只能有⼀一個修改的理理由。 • 我們強調的主張:系統是由許多⼩小型類別左組成,⽽而不是 由少數幾個⼤大型類別組成 • 每個⼩小類別『封裝單⼀一的職責』、『只有⼀一個修改的裡由』 以及『與其他少數幾個類別合作來來完成系統要求的⾏行行為』
  70. 凝聚性 • 保持凝聚性會得到許多⼩小型的類別 • 在⽅方法裡操縱愈多的變數,代表這個⽅方法更更凝聚於該類別。 • 若若有某個類別的每個變數都被使⽤用在每個⽅方法中,那麼這 個類別就是具有最⼤大凝聚性的類別
  71. 凝聚性 • 當凝聚性⾼高,代表類別裡的⽅方法和變數是相互依賴的, 並且相互結和為邏輯上的整體。 • 當類別失去凝聚性的時候,就把他們拆開來來吧!
  72. 羽化 透過⽻化設計來來達到整潔
  73. 一個簡單的設計 ① 執⾏行行完所有的測試 ② 沒有重複的部分 ③ 表達程式設計師的本意 ④ 最⼩小化類別和⽅方法的數量量 這些守則,根據重要性來來排 序。
  74. 簡單設計守則 1: 執⾏行行完所有的測試 • 類別若若遵守單⼀一職責原則 (SRP),那麼測試就說⼀一件很簡 單的事。 • 系統以物件導向的主要⽬目標,讓程式有低耦合度和⾼高凝聚 度。 所以撰寫測試程式,最終帶來來了了更更佳的設計。
  75. 簡單設計守則 2~4: 程式重構 • ⼀一旦有了了測試,就能保持程式和類別的整潔,利利⽤用逐步增 加的⽅方式來來進⾏行行重構。 • 重構的過程中,我們可以應⽤用與良好軟體設計有關的所有 知識。 ➡ 增加凝聚性 ➡ 降低耦合度 ➡ 分離關注點 ‣ 模組化系統關注點 ‣ 替函式和類別瘦⾝身 ‣ 良好的命名
  76. 禁⽌止重複 • 重複程式碼是『良好設計系統』的主要敵⼈人。他代表額外 的⼯工作、額外的風險及額外不必要的複雜性。
  77. 具表達⼒力力 • 『選擇⼀一個良好的名稱』 • 『讓函式和類別簡短』 • 『標準命名法』 • 『良好的單元測試』也具良好的表達⼒力力,主要⽬目的『⽤用範例例學』 • 多花點時間在你的函式和類別上,選擇較好的名稱,將⼤大型函式 拆解成⼩小型函式 • ⽤用⼼心是⼀一項珍貴的資源
  78. 最⼩小化類別及⽅方法的數量量 • ⼩小⼼心『消除重複程式碼』,『單⼀一職責原則』這類的基本 觀念念會做過頭,⽽而產⽣生太多微型的類別和⽅方法。 所以該守 則建議我們,必須讓類別及⽅方法的數量量保持少少的。 • 但這條守則卻是簡單設計四守則裡優先最低的。所以測 試、消除重複及表達⼒力力等其他三條守則,才是最重要的。
  79. 簡單的設計 ① 執⾏行行完所有的測試 ② 沒有重複的部分 ③ 表達程式設計師的本意 ④ 最⼩小化類別和⽅方法的數量量
原文地址:https://www.cnblogs.com/thomas-yang-github/p/11277827.html