From ed1b3b90a6b99e821d699740569460ad5a95d73d Mon Sep 17 00:00:00 2001 From: QP Date: Mon, 27 Feb 2017 23:49:00 +0800 Subject: [PATCH 1/6] updated table of contents --- README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index b4449ec..e06a23b 100644 --- a/README.md +++ b/README.md @@ -8,12 +8,13 @@ Original Repository: [ryanmcdermott/clean-code-javascript](https://github.com/ry 3. [Hàm](#hàm) 4. [Đối tượng và Cấu trúc dữ liệu](#Đối-tượng-và-cấu-trúc-dữ-liệu) 5. [Lớp](#lớp) - 6. [Testing](#testing) - 7. [Xử lí đồng thời](#xử-lí-đồng-thời) - 8. [Xử lí lỗi](#xử-lí-lỗi) - 9. [Định dạng](#Định-dạng) - 10. [Viết chú thích](#viết-chú-thích) - 11. [Các ngôn ngữ khác](#các-ngôn-ngữ-khác) + 6. [SOLID](#SOLID) + 7. [Testing](#testing) + 8. [Xử lí đồng thời](#xử-lí-đồng-thời) + 9. [Xử lí lỗi](#xử-lí-lỗi) + 10. [Định dạng](#Định-dạng) + 11. [Viết chú thích](#viết-chú-thích) + 12. [Các ngôn ngữ khác](#các-ngôn-ngữ-khác) ## Giới thiệu ![Humorous image of software quality estimation as a count of how many expletives From b7c1c8a4e6a1c1f6a2f3c10886fc8bb442ae68d3 Mon Sep 17 00:00:00 2001 From: QP Date: Mon, 27 Feb 2017 23:57:56 +0800 Subject: [PATCH 2/6] Swapped Classes and SOLID --- README.md | 452 +++++++++++++++++++++++++++--------------------------- 1 file changed, 227 insertions(+), 225 deletions(-) diff --git a/README.md b/README.md index e06a23b..e26f391 100644 --- a/README.md +++ b/README.md @@ -1027,6 +1027,233 @@ console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe ## **Lớp** + +### Ưu tiên lớp ES2015/ES6 hơn các chức năng thuần ES5 +Rất khó khăn để có thể đọc được lớp thừa kế, lớp khởi tạo, và các định nghĩa phương thức +trong các lớp ES5 cổ điển. Nếu bạn cần kế thừa (và lưu ý rằng bạn có thể không), +tốt hơn là nên sử dụng lớp. Tuy nhiên ưu tiên sử dụng những hàm nhỏ hơn là lớp cho +đến khi bạn cần những đối tượng lớn và phức tạp hơn. + +**Không tốt:** +```javascript +const Animal = function(age) { + if (!(this instanceof Animal)) { + throw new Error('Instantiate Animal with `new`'); + } + + this.age = age; +}; + +Animal.prototype.move = function move() {}; + +const Mammal = function(age, furColor) { + if (!(this instanceof Mammal)) { + throw new Error('Instantiate Mammal with `new`'); + } + + Animal.call(this, age); + this.furColor = furColor; +}; + +Mammal.prototype = Object.create(Animal.prototype); +Mammal.prototype.constructor = Mammal; +Mammal.prototype.liveBirth = function liveBirth() {}; + +const Human = function(age, furColor, languageSpoken) { + if (!(this instanceof Human)) { + throw new Error('Instantiate Human with `new`'); + } + + Mammal.call(this, age, furColor); + this.languageSpoken = languageSpoken; +}; + +Human.prototype = Object.create(Mammal.prototype); +Human.prototype.constructor = Human; +Human.prototype.speak = function speak() {}; +``` + +**Tốt:** +```javascript +class Animal { + constructor(age) { + this.age = age; + } + + move() { /* ... */ } +} + +class Mammal extends Animal { + constructor(age, furColor) { + super(age); + this.furColor = furColor; + } + + liveBirth() { /* ... */ } +} + +class Human extends Mammal { + constructor(age, furColor, languageSpoken) { + super(age, furColor); + this.languageSpoken = languageSpoken; + } + + speak() { /* ... */ } +} +``` +**[⬆ về đầu trang](#mục-lục)** + +### Sử dụng các hàm liên tiếp nhau +Đây là một pattern rất hữu ích trong JavaScript và bạn thấy nó trong rất +nhiều thư viện chẳng hạn như jQuery và Lodash. Nó cho phép code của bạn +có tính truyền tải và ngắn gọn. Vì lý do đó, theo tôi, sử dụng phương pháp +các hàm liên tiếp nhau và hãy xem code của bạn sẽ sạch sẽ như thế nào. Trong +các hàm của lớp, đơn giản là trả về `this` ở cuối mỗi hàm, và bạn có thể xâu +chuỗi các phương thức khác vào trong nó. + +**Không tốt:** +```javascript +class Car { + constructor() { + this.make = 'Honda'; + this.model = 'Accord'; + this.color = 'white'; + } + + setMake(make) { + this.make = make; + } + + setModel(model) { + this.model = model; + } + + setColor(color) { + this.color = color; + } + + save() { + console.log(this.make, this.model, this.color); + } +} + +const car = new Car(); +car.setColor('pink'); +car.setMake('Ford'); +car.setModel('F-150'); +car.save(); +``` + +**Tốt:** +```javascript +class Car { + constructor() { + this.make = 'Honda'; + this.model = 'Accord'; + this.color = 'white'; + } + + setMake(make) { + this.make = make; + // Ghi chú: Trả về this để xâu chuỗi các phương thức + return this; + } + + setModel(model) { + this.model = model; + // Ghi chú: Trả về this để xâu chuỗi các phương thức + return this; + } + + setColor(color) { + this.color = color; + // Ghi chú: Trả về this để xâu chuỗi các phương thức + return this; + } + + save() { + console.log(this.make, this.model, this.color); + // Ghi chú: Trả về this để xâu chuỗi các phương thức + return this; + } +} + +const car = new Car() + .setColor('pink') + .setMake('Ford') + .setModel('F-150') + .save(); +``` +**[⬆ về đầu trang](#mục-lục)** + +### Ưu tiên thành phần hơn là kế thừa +Như đã được nhấn mạnh nhiều trong [*Design Patterns*](https://en.wikipedia.org/wiki/Design_Patterns) +của Gang of Four, bạn nên sử dụng cấu trúc thành phần hơn là thừa kế nếu có thể. +Có rất nhiều lý do tốt để sử dụng kế thừa cũng như sử dụng thành phần. +Điểm nhấn cho phương châm này đó là nếu tâm trí của bạn đi theo bản năng thừa kế, +thử nghĩ nếu thành phần có thể mô hình vấn đề của bạn tốt hơn. Trong một số trường +hợp nó có thể. + +Bạn có thể tự hỏi, "khi nào tôi nên sử dụng thừa kế?" Nó phụ thuộc vào +vấn đề trong tầm tay của bạn, nhưng đây là một danh sách manh nha khi kế thừa +có ý nghĩa hơn thành phần: + +1. Kế thừa của bạn đại diện cho mỗi quan hệ "is-a" và không có mỗi quan hệ "has-a" +(Human->Animal vs. User->UserDetails). +2. Bạn có thể sử dụng lại code từ lớp cơ bản (Humans có thể di chuyển giống tất cả Animals). +3. Bạn muốn làm thay đổi toàn cục đến các lớp dẫn xuất bằng cách thay đổi lớp cơ bản. +(Thay đổi lượng calo của tất cả animal khi chúng di chuyển) + +**Không tốt:** +```javascript +class Employee { + constructor(name, email) { + this.name = name; + this.email = email; + } + + // ... +} + +// Không tốt bởi vì Employees "có" dữ liệu thuế. +// EmployeeTaxData không phải là một loại của Employee +class EmployeeTaxData extends Employee { + constructor(ssn, salary) { + super(); + this.ssn = ssn; + this.salary = salary; + } + + // ... +} +``` + +**Tốt:** +```javascript +class EmployeeTaxData { + constructor(ssn, salary) { + this.ssn = ssn; + this.salary = salary; + } + + // ... +} + +class Employee { + constructor(name, email) { + this.name = name; + this.email = email; + } + + setTaxData(ssn, salary) { + this.taxData = new EmployeeTaxData(ssn, salary); + } + // ... +} +``` +**[⬆ về đầu trang](#mục-lục)** + +## **SOLID** ### Nguyên lí đơn trách nhiệm (Single Responsibility Principle) Như đã được nói đến trong cuốn Clean Code, "Chỉ có thể thay đổi một lớp vì một lí do duy nhất". Thật là hấp dẫn để nhồi nhét nhiều chức năng vào cho một lớp, giống @@ -1459,231 +1686,6 @@ inventoryTracker.requestItems(); ``` **[⬆ về đầu trang](#mục-lục)** -### Ưu tiên lớp ES2015/ES6 hơn các chức năng thuần ES5 -Rất khó khăn để có thể đọc được lớp thừa kế, lớp khởi tạo, và các định nghĩa phương thức -trong các lớp ES5 cổ điển. Nếu bạn cần kế thừa (và lưu ý rằng bạn có thể không), -tốt hơn là nên sử dụng lớp. Tuy nhiên ưu tiên sử dụng những hàm nhỏ hơn là lớp cho -đến khi bạn cần những đối tượng lớn và phức tạp hơn. - -**Không tốt:** -```javascript -const Animal = function(age) { - if (!(this instanceof Animal)) { - throw new Error('Instantiate Animal with `new`'); - } - - this.age = age; -}; - -Animal.prototype.move = function move() {}; - -const Mammal = function(age, furColor) { - if (!(this instanceof Mammal)) { - throw new Error('Instantiate Mammal with `new`'); - } - - Animal.call(this, age); - this.furColor = furColor; -}; - -Mammal.prototype = Object.create(Animal.prototype); -Mammal.prototype.constructor = Mammal; -Mammal.prototype.liveBirth = function liveBirth() {}; - -const Human = function(age, furColor, languageSpoken) { - if (!(this instanceof Human)) { - throw new Error('Instantiate Human with `new`'); - } - - Mammal.call(this, age, furColor); - this.languageSpoken = languageSpoken; -}; - -Human.prototype = Object.create(Mammal.prototype); -Human.prototype.constructor = Human; -Human.prototype.speak = function speak() {}; -``` - -**Tốt:** -```javascript -class Animal { - constructor(age) { - this.age = age; - } - - move() { /* ... */ } -} - -class Mammal extends Animal { - constructor(age, furColor) { - super(age); - this.furColor = furColor; - } - - liveBirth() { /* ... */ } -} - -class Human extends Mammal { - constructor(age, furColor, languageSpoken) { - super(age, furColor); - this.languageSpoken = languageSpoken; - } - - speak() { /* ... */ } -} -``` -**[⬆ về đầu trang](#mục-lục)** - -### Sử dụng các hàm liên tiếp nhau -Đây là một pattern rất hữu ích trong JavaScript và bạn thấy nó trong rất -nhiều thư viện chẳng hạn như jQuery và Lodash. Nó cho phép code của bạn -có tính truyền tải và ngắn gọn. Vì lý do đó, theo tôi, sử dụng phương pháp -các hàm liên tiếp nhau và hãy xem code của bạn sẽ sạch sẽ như thế nào. Trong -các hàm của lớp, đơn giản là trả về `this` ở cuối mỗi hàm, và bạn có thể xâu -chuỗi các phương thức khác vào trong nó. - -**Không tốt:** -```javascript -class Car { - constructor() { - this.make = 'Honda'; - this.model = 'Accord'; - this.color = 'white'; - } - - setMake(make) { - this.make = make; - } - - setModel(model) { - this.model = model; - } - - setColor(color) { - this.color = color; - } - - save() { - console.log(this.make, this.model, this.color); - } -} - -const car = new Car(); -car.setColor('pink'); -car.setMake('Ford'); -car.setModel('F-150'); -car.save(); -``` - -**Tốt:** -```javascript -class Car { - constructor() { - this.make = 'Honda'; - this.model = 'Accord'; - this.color = 'white'; - } - - setMake(make) { - this.make = make; - // Ghi chú: Trả về this để xâu chuỗi các phương thức - return this; - } - - setModel(model) { - this.model = model; - // Ghi chú: Trả về this để xâu chuỗi các phương thức - return this; - } - - setColor(color) { - this.color = color; - // Ghi chú: Trả về this để xâu chuỗi các phương thức - return this; - } - - save() { - console.log(this.make, this.model, this.color); - // Ghi chú: Trả về this để xâu chuỗi các phương thức - return this; - } -} - -const car = new Car() - .setColor('pink') - .setMake('Ford') - .setModel('F-150') - .save(); -``` -**[⬆ về đầu trang](#mục-lục)** - -### Ưu tiên thành phần hơn là kế thừa -Như đã được nhấn mạnh nhiều trong [*Design Patterns*](https://en.wikipedia.org/wiki/Design_Patterns) -của Gang of Four, bạn nên sử dụng cấu trúc thành phần hơn là thừa kế nếu có thể. -Có rất nhiều lý do tốt để sử dụng kế thừa cũng như sử dụng thành phần. -Điểm nhấn cho phương châm này đó là nếu tâm trí của bạn đi theo bản năng thừa kế, -thử nghĩ nếu thành phần có thể mô hình vấn đề của bạn tốt hơn. Trong một số trường -hợp nó có thể. - -Bạn có thể tự hỏi, "khi nào tôi nên sử dụng thừa kế?" Nó phụ thuộc vào -vấn đề trong tầm tay của bạn, nhưng đây là một danh sách manh nha khi kế thừa -có ý nghĩa hơn thành phần: - -1. Kế thừa của bạn đại diện cho mỗi quan hệ "is-a" và không có mỗi quan hệ "has-a" -(Human->Animal vs. User->UserDetails). -2. Bạn có thể sử dụng lại code từ lớp cơ bản (Humans có thể di chuyển giống tất cả Animals). -3. Bạn muốn làm thay đổi toàn cục đến các lớp dẫn xuất bằng cách thay đổi lớp cơ bản. -(Thay đổi lượng calo của tất cả animal khi chúng di chuyển) - -**Không tốt:** -```javascript -class Employee { - constructor(name, email) { - this.name = name; - this.email = email; - } - - // ... -} - -// Không tốt bởi vì Employees "có" dữ liệu thuế. -// EmployeeTaxData không phải là một loại của Employee -class EmployeeTaxData extends Employee { - constructor(ssn, salary) { - super(); - this.ssn = ssn; - this.salary = salary; - } - - // ... -} -``` - -**Tốt:** -```javascript -class EmployeeTaxData { - constructor(ssn, salary) { - this.ssn = ssn; - this.salary = salary; - } - - // ... -} - -class Employee { - constructor(name, email) { - this.name = name; - this.email = email; - } - - setTaxData(ssn, salary) { - this.taxData = new EmployeeTaxData(ssn, salary); - } - // ... -} -``` -**[⬆ về đầu trang](#mục-lục)** - ## **Testing** Testing thì quan trọng hơn shipping. Nếu bạn không có test hoặc không đủ, thì mỗi lần ship code bạn sẽ không chắc là mình có làm hư hại thứ gì không. From 7d9565932300e8a8df147038663a8c281212f33c Mon Sep 17 00:00:00 2001 From: QP Date: Tue, 28 Feb 2017 00:12:00 +0800 Subject: [PATCH 3/6] updated Examples in 'Objects and Data Structures' --- README.md | 53 +++++++++++++++++++++++++---------------------------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index e26f391..64c5720 100644 --- a/README.md +++ b/README.md @@ -943,48 +943,45 @@ cần phải tìm kiếm và thay đổi mỗi accessor trong codebase của b **Không tốt:** ```javascript -class BankAccount { - constructor() { - this.balance = 1000; - } -} +function makeBankAccount() { + // ... -const bankAccount = new BankAccount(); + return { + balance: 0, + // ... + }; +} -// Buy shoes... -bankAccount.balance -= 100; +const account = makeBankAccount(); +account.balance = 100; ``` **Tốt:** ```javascript -class BankAccount { - constructor(balance = 1000) { - this._balance = balance; - } +function makeBankAccount() { + // this one is private + let balance = 0; - // Không cần phải thêm tiền tố `get` hay `set` để trở thành một getter hay setter - set balance(amount) { - if (this.verifyIfAmountCanBeSetted(amount)) { - this._balance = amount; - } + // Một "getter", thiết lập public thông qua đối tượng được trả về dưới đây + function getBalance() { + return balance; } - get balance() { - return this._balance; + // Một "setter", thiết lập public thông qua đối tượng được trả về dưới đây + function setBalance(amount) { + // ... validate before updating the balance + balance = amount; } - verifyIfAmountCanBeSetted(val) { + return { // ... - } + getBalance, + setBalance, + }; } -const bankAccount = new BankAccount(); - -// Buy shoes... -bankAccount.balance -= shoesPrice; - -// Get balance -let balance = bankAccount.balance; +const account = makeBankAccount(); +account.setBalance(100); ``` **[⬆ về đầu trang](#mục-lục)** From 3ecb5580722e6b36fb5dcef32235304cae340692 Mon Sep 17 00:00:00 2001 From: QP Date: Tue, 28 Feb 2017 09:06:05 +0800 Subject: [PATCH 4/6] manually add 'id' to SOLID field --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 64c5720..dab51ed 100644 --- a/README.md +++ b/README.md @@ -1250,7 +1250,7 @@ class Employee { ``` **[⬆ về đầu trang](#mục-lục)** -## **SOLID** +## **SOLID** ### Nguyên lí đơn trách nhiệm (Single Responsibility Principle) Như đã được nói đến trong cuốn Clean Code, "Chỉ có thể thay đổi một lớp vì một lí do duy nhất". Thật là hấp dẫn để nhồi nhét nhiều chức năng vào cho một lớp, giống From bc9130f2f0c137e55868b326a10e43de592e85fc Mon Sep 17 00:00:00 2001 From: QP Date: Tue, 28 Feb 2017 09:12:13 +0800 Subject: [PATCH 5/6] removed span. tried lowercase --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index dab51ed..93146cc 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Original Repository: [ryanmcdermott/clean-code-javascript](https://github.com/ry 3. [Hàm](#hàm) 4. [Đối tượng và Cấu trúc dữ liệu](#Đối-tượng-và-cấu-trúc-dữ-liệu) 5. [Lớp](#lớp) - 6. [SOLID](#SOLID) + 6. [SOLID](#solid) 7. [Testing](#testing) 8. [Xử lí đồng thời](#xử-lí-đồng-thời) 9. [Xử lí lỗi](#xử-lí-lỗi) @@ -1250,7 +1250,7 @@ class Employee { ``` **[⬆ về đầu trang](#mục-lục)** -## **SOLID** +## **SOLID** ### Nguyên lí đơn trách nhiệm (Single Responsibility Principle) Như đã được nói đến trong cuốn Clean Code, "Chỉ có thể thay đổi một lớp vì một lí do duy nhất". Thật là hấp dẫn để nhồi nhét nhiều chức năng vào cho một lớp, giống From 9cbed9da877e11cacc801b6357673397fc92dd50 Mon Sep 17 00:00:00 2001 From: QP Date: Tue, 28 Feb 2017 14:34:36 +0800 Subject: [PATCH 6/6] updated Chinese Version --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 93146cc..3917745 100644 --- a/README.md +++ b/README.md @@ -2164,7 +2164,9 @@ Tài liệu này cũng có sẵn ở các ngôn ngữ sau: - ![en](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags-iso/shiny/24/US.png) **English**: [ryanmcdermott/clean-code-javascript](https://github.com/ryanmcdermott/clean-code-javascript) - ![br](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Brazil.png) **Brazilian Portuguese**: [fesnt/clean-code-javascript](https://github.com/fesnt/clean-code-javascript) - - ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinese**: [alivebao/clean-code-js](https://github.com/alivebao/clean-code-js) + - ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinese**: + - [alivebao/clean-code-js](https://github.com/alivebao/clean-code-js) + - [beginor/clean-code-javascript](https://github.com/beginor/clean-code-javascript) - ![de](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Germany.png) **German**: [marcbruederlin/clean-code-javascript](https://github.com/marcbruederlin/clean-code-javascript) - ![kr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/South-Korea.png) **Korean**: [qkraudghgh/clean-code-javascript-ko](https://github.com/qkraudghgh/clean-code-javascript-ko) - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Uruguay.png) **Spanish**: [andersontr15/clean-code-javascript](https://github.com/andersontr15/clean-code-javascript-es)