หลักการวิศวกรรมซอฟต์แวร์จากหนังสือ Robert C Martin Code , Air -ปรับสภาพด้วย JavaScript นี่ไม่ใช่วิธีการสไตล์ มันเป็นคู่มือการผลิตสำหรับโปรแกรมที่อ่านได้นำมาใช้ซ้ำได้และการผลิตซ้ำในสคริปต์ Java
ไม่ใช่ทุกหลักการที่นี่จะต้องปฏิบัติตามอย่างถูกต้องและแม้แต่น้อยกว่าที่จะตกลงกันทั่วโลก เหล่านี้เป็นแนวทางไม่มาก แต่เป็นแนวทางสำหรับประสบการณ์โดยรวมหลายปีโดย Clean Code _
อายุการใช้งานของวิศวกรรมซอฟต์แวร์มีอายุมากกว่า 50 ปีและเรายังคงเรียนรู้หลายสิ่งหลายอย่าง "เมื่อวิศวกรรมซอฟต์แวร์เก่าแก่วิศวกรรมอยู่ในตัวของมันเอง" บางทีเราอาจจะมีกฎที่ยากกว่าที่จะปฏิบัติตาม ตอนนี้ให้แนวทางเหล่านี้ทำหน้าที่เป็นแบบทดสอบเพื่อประเมินคุณภาพของรหัส JavaScript ที่คุณและทีมงานของคุณผลิต
อีกสิ่งหนึ่ง: การรู้หลักการเหล่านี้จะไม่ทำให้คุณเป็นโปรแกรมเมอร์ที่ดีที่สุดเท่าที่เคยมีมาและการทำงานกับพวกเขาเป็นเวลาหลายปีไม่ได้หมายความว่าคุณจะไม่ทำผิดพลาด แต่ละส่วนของรหัสเริ่มต้นเป็นร่างแรกเช่นการทำงานกับดินเหนียวเปียกที่จะไม่ได้รับรูปร่างสุดท้ายตั้งแต่แรกเห็นเพราะเรามักจะลบข้อบกพร่องเมื่อ เราตรวจสอบกับเพื่อนของเรา ดังนั้นอย่ากดตัวเองเพราะร่างแรกที่ต้องการการปรับปรุง เอาชนะรหัสแทน!
แย่:
const yyyymmdstr = moment ( ) . format ( "YYYY/MM/DD" )ดี:
const currentDate = moment ( ) . format ( "YYYY/MM/DD" )⬆กลับไปด้านบน
แย่:
getUserInfo ( )
getClientData ( )
getCustomerRecord ( )ดี:
getUser ( )⬆กลับไปด้านบน
เราจะอ่านโค้ดมากกว่าที่เราจะเขียน เป็นสิ่งสำคัญที่รหัสที่เราเขียนสามารถอ่านได้และการวิจัย ไม่ตั้งชื่อตัวแปรที่มีความหมายที่จะเข้าใจโปรแกรมของเราทำให้เป็นเรื่องยากสำหรับผู้อ่าน ค้นหาชื่อของคุณ เครื่องมือเช่น buddy.js และ eslint สามารถช่วยกำหนดค่าคงที่ที่ไม่มีชื่อ
แย่:
// What the heck is 86400000 for?
setTimeout ( blastOff , 86400000 )ดี:
// Declare them as capitalized named constants.
const MILLISECONDS_PER_DAY = 60 * 60 * 24 * 1000 //86400000;
setTimeout ( blastOff , MILLISECONDS_PER_DAY )⬆กลับไปด้านบน
แย่:
const address = "One Infinite Loop, Cupertino 95014"
const cityZipCodeRegex = / ^[^,\]+[,\s]+(.+?)s*(d{5})?$ /
saveCityZipCode (
address . match ( cityZipCodeRegex ) [ 1 ] ,
address . match ( cityZipCodeRegex ) [ 2 ]
)ดี:
const address = "One Infinite Loop, Cupertino 95014"
const cityZipCodeRegex = / ^[^,\]+[,\s]+(.+?)s*(d{5})?$ /
const [ _ , city , zipCode ] = address . match ( cityZipCodeRegex ) || [ ]
saveCityZipCode ( city , zipCode )⬆กลับไปด้านบน
มันชัดเจนดีกว่าโดยปริยายเสมอ
แย่:
const locations = [ "Austin" , "New York" , "San Francisco" ]
locations . forEach ( ( l ) => {
doStuff ( )
doSomeOtherStuff ( )
// ...
// ...
// ...
// Wait, what is `l` for again?
dispatch ( l )
} )ดี:
const locations = [ "Austin" , "New York" , "San Francisco" ]
locations . forEach ( ( location ) => {
doStuff ( )
doSomeOtherStuff ( )
// ...
// ...
// ...
dispatch ( location )
} )⬆กลับไปด้านบน
หากคลาสชื่อ/วัตถุบอกคุณบางอย่างอย่าทำซ้ำในชื่อตัวแปรของคุณ
แย่:
const Car = {
carMake : "Honda" ,
carModel : "Accord" ,
carColor : "Blue" ,
}
function paintCar ( car , color ) {
car . carColor = color
}ดี:
const Car = {
make : "Honda" ,
model : "Accord" ,
color : "Blue" ,
}
function paintCar ( car , color ) {
car . color = color
}⬆กลับไปด้านบน
อาร์กิวเมนต์เริ่มต้นเป็นของออฟไลน์ เบย์ว่าถ้าคุณพวกเราฟังก์ชั่นของคุณจะให้ค่าเริ่มต้นสำหรับอาร์กิวเมนต์ undefined เท่านั้น อื่น ๆ "falsy" value su '' , "" false , null , 0 และ NaN จะไม่เชื่อมั่นในค่าเริ่มต้น
แย่:
function createMicrobrewery ( name ) {
const breweryName = name || "Hipster Brew Co."
// ...
}ดี:
function createMicrobrewery ( name = "Hipster Brew Co." ) {
// ...
}⬆กลับไปด้านบน
เป็นการยากมากที่จะได้รับการถ่ายทอดมรดกการอ่านเพื่อสร้างและคำจำกัดความคลาสสิกของคลาส ES5 หากคุณต้องการการถ่ายทอดทางพันธุกรรม (และโปรดทราบว่าคุณอาจไม่ต้องการ) คุณจะชอบคลาส ES2015/ES6 อย่างไรก็ตามพยายามที่จะยังคงเป็นฟังก์ชั่นเล็ก ๆ ในชั้นเรียนเพื่อให้คุณพบว่าตัวเองต้องการตัวเองมากขึ้นและซับซ้อนขึ้น
แย่:
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 ( ) { }ดี:
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 ( ) {
/* ... */
}
}⬆กลับไปด้านบน
สไตล์นี้มีประโยชน์มากในสคริปต์ Java และเห็นในห้องสมุดหลายแห่งเช่น Jquey และ Lodash ช่วยให้รหัสของคุณแสดงและนินทาน้อยที่สุด ด้วยเหตุนี้ฉันขอแนะนำให้ใช้วิธีการลำดับและดูว่ารหัสของคุณสะอาดแค่ไหน ในฟังก์ชั่นคลาสของคุณคุณจะต้องส่งคืนสิ่งนี้ในตอนท้ายของแต่ละฟังก์ชั่นและคุณสามารถเชื่อมต่อวิธีการเรียนเพิ่มเติมได้
แย่:
class Car {
constructor ( make , model , color ) {
this . make = make
this . model = model
this . color = color
}
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 ( "Ford" , "F-150" , "red" )
car . setColor ( "pink" )
car . save ( )ดี:
class Car {
constructor ( make , model , color ) {
this . make = make
this . model = model
this . color = color
}
setMake ( make ) {
this . make = make
// NOTE: Returning this for chaining
return this
}
setModel ( model ) {
this . model = model
// NOTE: Returning this for chaining
return this
}
setColor ( color ) {
this . color = color
// NOTE: Returning this for chaining
return this
}
save ( ) {
console . log ( this . make , this . model , this . color )
// NOTE: Returning this for chaining
return this
}
}
const car = new Car ( "Ford" , "F-150" , "red" ) . setColor ( "pink" ) . save ( )⬆กลับไปด้านบน
ตามที่ระบุไว้ใน [ รูปแบบการออกแบบ ] (https://en.wikipedia.org/wiki/design_patterns) โดยแก๊งค์สี่คนคุณควรเลือกองค์ประกอบมากกว่ามรดกเมื่อคุณทำได้ มีหลายสาเหตุของใบหน้าที่จะใช้มรดกและใบหน้าหลายอย่างของการใช้องค์ประกอบ ประเด็นหลักของหลักการนี้คือถ้าใจของคุณติดตั้งเพื่อรับมรดกลองคิดว่าการแต่งเพลงสามารถแก้ปัญหาของคุณได้ดีขึ้นหรือไม่ ในบางกรณีมันสามารถ
คุณอาจสงสัยหลังจากนี้ "ฉันควรใช้มรดกเมื่อไหร่" ขึ้นอยู่กับปัญหาของคุณที่คุณต้องการแก้ไข แต่นี่เป็นรายการที่ดีเมื่อมรดกมีเหตุผลมากกว่าองค์ประกอบ:
มรดกของคุณคือความสัมพันธ์ "และไม่มี" มี "ความสัมพันธ์ (มนุษย์-> สัตว์ตรงข้ามกับผู้ใช้-> รายละเอียดผู้ใช้)
คุณสามารถนำรหัสกลับมาใช้ใหม่จากคลาสพื้นฐาน (มนุษย์สามารถเคลื่อนไหวเหมือนสัตว์ทุกตัว)
คุณต้องการเปลี่ยนแปลงทั่วไปในคลาสที่ได้รับโดยการเปลี่ยนคลาสพื้นฐาน (เปลี่ยนแคลอรี่สำหรับสัตว์ทุกตัวเมื่อพวกมันเคลื่อนไหว)
แย่:
class Employee {
constructor ( name , email ) {
this . name = name
this . email = email
}
// ...
}
// Bad because Employees "have" tax data. EmployeeTaxData is not a type of Employee
class EmployeeTaxData extends Employee {
constructor ( ssn , salary ) {
super ( )
this . ssn = ssn
this . salary = salary
}
// ...
}ดี:
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 )
}
// ...
}⬆กลับไปด้านบน