五種編寫"自然"代碼的方法,讓每個(gè)人都愛不釋手
為什么我們使用JavaScript、Dart和Python等語言,而不是古老的匯編語言?
這是因?yàn)樗鼈兣c自然語言更接近。
或者說,它們有可能更接近自然語言。
因?yàn)橛袝r(shí)我們編寫代碼只是為了讓它能工作,而不關(guān)心向其他人展示我們在做什么。
而這種做法往往會(huì)在日后造成痛苦的反噬。特別是當(dāng)其中一個(gè)"其他人"是未來的自己時(shí)。
1. 使用詞性命名
當(dāng)你的代碼盡可能地像英語時(shí),你就知道它是自然的。就像一個(gè)有趣、描述性的故事。
這意味著你已經(jīng)智能地創(chuàng)造了故事中的實(shí)體和動(dòng)作,以強(qiáng)有力地表達(dá)從開始到完成的代碼流程。
名詞
我們在談?wù)撃男?shí)體?
- 變量
- 屬性(getter和setter)
- 類和對(duì)象
- 模塊
每個(gè)角色都有一個(gè)名字,所以我們用表達(dá)力強(qiáng)的名詞和名詞短語來描述它們。
不要這樣:
// ? do-examples.ts
// ? 難以理解
const f = 'Coding';
const l = 'Beauty';
// ? Verb
// ? 動(dòng)詞
const makeFullName = `${f} ${l}`;
class Book {
// ? Adjectival phrase
// ? 形容詞短語
createdAt: Date;
}
而要這樣:
// ? examples.ts
// ? 可讀性高
const firstName = 'Coding';
const lastName = 'Beauty';
// ? Noun
// ? 名詞
const fullName = `${firstName} ${lastName}`;
class Book {
// ? Noun phrase
// ? 名詞短語
dateCreated: Date;
}
動(dòng)詞
你的代碼庫中有哪些動(dòng)作?
- 函數(shù)
- 對(duì)象方法
動(dòng)作意味著實(shí)體在做某事;命名它們的自然方式是使用描述性的動(dòng)詞和動(dòng)詞短語。
不要這樣:
class Product {
constructor(name, price, quantity) {
this.name = name;
this.price = price;
this.quantity = quantity;
}
// ? Noun
// ? 名詞
total() {
return this.price * this.quantity;
}
}
const product = new Product('Pineapple??', 5, 8);
console.log(product.total()); // 40
而要這樣:
class Product {
constructor(name, price, quantity) {
this.name = name;
this.price = price;
this.quantity = quantity;
}
// ? Verb
// ? 動(dòng)詞
getTotal() {
return this.price * this.quantity;
}
}
const product = new Product('Banana??', 7, 7);
console.log(product.getTotal()); // 49
方法是用來做某事的。屬性是用來擁有某物的。
所以更好的做法是:
class Product {
constructor(name, price, quantity) {
this.name = name;
this.price = price;
this.quantity = quantity;
}
get total() {
return this.price * this.quantity;
}
}
const product = new Product('Orange??', 7, 10);
console.log(product.total); // 70
副詞
記住,副詞用來告訴你更多關(guān)于名詞、動(dòng)詞或另一個(gè)副詞的信息。
在JavaScript中,這是指任何接受函數(shù)并返回另一個(gè)函數(shù)的函數(shù):一個(gè)高階函數(shù)。它們升級(jí)了函數(shù)。
所以與其這樣:
// ? 動(dòng)詞
function silence(fn) {
try {
return fn();
} catch (error) {
return null;
}
}
const getFileContents = (filePath) =>
silence(() => fs.readFileSync(filePath, 'utf8'));
不如這樣更自然:
// ? 副詞
function silently({ fn }) { // or "withSilence"
try {
return fn();
} catch (error) {
return null;
}
}
const getFileContents = (filePath) =>
silently({ fn: () => fs.readFileSync(filePath, 'utf8') });
這就像在說"悄悄地獲取文件內(nèi)容"。
2. 編寫自然的輸入
編碼和計(jì)算都是關(guān)于處理某些輸入以產(chǎn)生輸出。
在自然代碼中,處理是動(dòng)作,而輸入+輸出是實(shí)體。
假設(shè)我們有一個(gè)計(jì)算矩形面積并將其乘以某個(gè)數(shù)量的函數(shù)。
你能看出這里的問題嗎?
const calculateArea = (length, width, height) => length * width * height;
const area = calculateArea(2, 5, 10); // 100
哪個(gè)參數(shù)是寬度和高度?哪個(gè)是乘數(shù)?
這段代碼讀起來不自然;在英語中,我們總是指定動(dòng)作的對(duì)象。
如何修復(fù)這個(gè)問題?命名參數(shù):
// 輸入是實(shí)體 - 名詞 ?
const area = calculateArea({ multiplier: 2, height: 5, width: 10 });
function calculateArea({ multiplier, height, width }) {
return multiplier * height * width;
}
這樣更容易閱讀;我們立即就能理解我們在處理什么輸入。
即使只有1個(gè)參數(shù),我也建議使用這種方式。
3. 編寫自然的輸出
我們在輸出上也可以同樣明確:
const { area } = calculateArea({
multiplier: 2,
height: 5,
width: 10,
});
function calculateArea({ multiplier, height, width }) {
return { area: multiplier * height * width };
}
這也允許我們之后輕松升級(jí)函數(shù):
const { area, perimeter } = calculateArea({
multiplier: 2,
height: 5,
width: 10,
});
console.log(area, perimeter); // 100 60
function calculateArea({ multiplier, height, width }) {
return {
area: multiplier * height * width,
perimeter: (height + width) * 2 * multiplier,
};
}
4. 避免魔法數(shù)字
編碼不是懸疑驚悚片!它是一篇描述性文章;盡可能地描述清楚。
而不是這種令人費(fèi)解的混亂:
function c(a) {
return a / 13490190;
}
const b = c(40075);
console.log(b); // 0.002970677210624906
在整個(gè)代碼庫的大背景下,所有這些數(shù)字和變量意味著什么?它們告訴我們——人類——什么?
什么也沒告訴我們。實(shí)體和動(dòng)作的名稱要么不存在,要么質(zhì)量很差。
這就像告訴你的朋友:
是的,我去了這個(gè)地方,然后做了這件事,之后我做了某事去了另一個(gè)地方,并做了某事,120!
這毫無意義。
自然代碼描述一切。為實(shí)體使用優(yōu)雅的名詞,為動(dòng)作使用出色的動(dòng)詞。
const speedstersSpeedKmPerHr = 13490190;
const earthCircumferenceKm = 40075;
function calculateSpeedstersTime(distance) {
return distance / speedstersSpeedKmPerHr;
}
const time = calculateSpeedstersTime(earthCircumferenceKm);
console.log(time); // 0.002970677210624906 ~ 11s
現(xiàn)在你說了些有意義的話。
是的,我去了餐廳,吃了一個(gè)雞肉三明治,然后我開車去了健身房,做了二頭肌卷舉,120磅!
5. 創(chuàng)建"無用"變量
在自然代碼中,變量不再僅僅用于在這里那里存儲(chǔ)值。
它們也是解釋你在做什么的工具:
這就是為什么我們不這樣做:
if (
!user.isBanned &&
user.pricing === 'premium' &&
user.isSubscribedTo(channel)
) {
console.log('Playing video...');
}
而是這樣做:
const canUserWatchVideo =
!user.isBanned &&
user.pricing === 'premium' &&
user.isSubscribedTo(channel);
if (canUserWatchVideo) {
console.log('Playing video...');
}
我們只會(huì)使用這個(gè)變量一次,但這并不重要。它不是一個(gè)功能性變量,而是一個(gè)裝飾性變量;一個(gè)自然變量。
最后的思考
代碼是為你的同伴人類編寫的,而不僅僅是為編譯器。
一個(gè)不懂編程的人能理解你的代碼中發(fā)生了什么嗎?
毫無疑問,這是一個(gè)強(qiáng)有力的指導(dǎo)性問題,可以讓你的代碼盡可能地易讀和自然。