五種編寫"自然"代碼的方法,讓每個人都愛不釋手
為什么我們使用JavaScript、Dart和Python等語言,而不是古老的匯編語言?
這是因為它們與自然語言更接近。
或者說,它們有可能更接近自然語言。
因為有時我們編寫代碼只是為了讓它能工作,而不關心向其他人展示我們在做什么。
而這種做法往往會在日后造成痛苦的反噬。特別是當其中一個"其他人"是未來的自己時。
1. 使用詞性命名
當你的代碼盡可能地像英語時,你就知道它是自然的。就像一個有趣、描述性的故事。
這意味著你已經智能地創造了故事中的實體和動作,以強有力地表達從開始到完成的代碼流程。
名詞
我們在談論哪些實體?
- 變量
- 屬性(getter和setter)
- 類和對象
- 模塊
每個角色都有一個名字,所以我們用表達力強的名詞和名詞短語來描述它們。
不要這樣:
// ? do-examples.ts
// ? 難以理解
const f = 'Coding';
const l = 'Beauty';
// ? Verb
// ? 動詞
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;
}
動詞
你的代碼庫中有哪些動作?
- 函數
- 對象方法
動作意味著實體在做某事;命名它們的自然方式是使用描述性的動詞和動詞短語。
不要這樣:
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
// ? 動詞
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
副詞
記住,副詞用來告訴你更多關于名詞、動詞或另一個副詞的信息。
在JavaScript中,這是指任何接受函數并返回另一個函數的函數:一個高階函數。它們升級了函數。
所以與其這樣:
// ? 動詞
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') });
這就像在說"悄悄地獲取文件內容"。
2. 編寫自然的輸入
編碼和計算都是關于處理某些輸入以產生輸出。
在自然代碼中,處理是動作,而輸入+輸出是實體。
假設我們有一個計算矩形面積并將其乘以某個數量的函數。
你能看出這里的問題嗎?
const calculateArea = (length, width, height) => length * width * height;
const area = calculateArea(2, 5, 10); // 100
哪個參數是寬度和高度?哪個是乘數?
這段代碼讀起來不自然;在英語中,我們總是指定動作的對象。
如何修復這個問題?命名參數:
// 輸入是實體 - 名詞 ?
const area = calculateArea({ multiplier: 2, height: 5, width: 10 });
function calculateArea({ multiplier, height, width }) {
return multiplier * height * width;
}
這樣更容易閱讀;我們立即就能理解我們在處理什么輸入。
即使只有1個參數,我也建議使用這種方式。
3. 編寫自然的輸出
我們在輸出上也可以同樣明確:
const { area } = calculateArea({
multiplier: 2,
height: 5,
width: 10,
});
function calculateArea({ multiplier, height, width }) {
return { area: multiplier * height * width };
}
這也允許我們之后輕松升級函數:
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. 避免魔法數字
編碼不是懸疑驚悚片!它是一篇描述性文章;盡可能地描述清楚。
而不是這種令人費解的混亂:
function c(a) {
return a / 13490190;
}
const b = c(40075);
console.log(b); // 0.002970677210624906
在整個代碼庫的大背景下,所有這些數字和變量意味著什么?它們告訴我們——人類——什么?
什么也沒告訴我們。實體和動作的名稱要么不存在,要么質量很差。
這就像告訴你的朋友:
是的,我去了這個地方,然后做了這件事,之后我做了某事去了另一個地方,并做了某事,120!
這毫無意義。
自然代碼描述一切。為實體使用優雅的名詞,為動作使用出色的動詞。
const speedstersSpeedKmPerHr = 13490190;
const earthCircumferenceKm = 40075;
function calculateSpeedstersTime(distance) {
return distance / speedstersSpeedKmPerHr;
}
const time = calculateSpeedstersTime(earthCircumferenceKm);
console.log(time); // 0.002970677210624906 ~ 11s
現在你說了些有意義的話。
是的,我去了餐廳,吃了一個雞肉三明治,然后我開車去了健身房,做了二頭肌卷舉,120磅!
5. 創建"無用"變量
在自然代碼中,變量不再僅僅用于在這里那里存儲值。
它們也是解釋你在做什么的工具:
這就是為什么我們不這樣做:
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...');
}
我們只會使用這個變量一次,但這并不重要。它不是一個功能性變量,而是一個裝飾性變量;一個自然變量。
最后的思考
代碼是為你的同伴人類編寫的,而不僅僅是為編譯器。
一個不懂編程的人能理解你的代碼中發生了什么嗎?
毫無疑問,這是一個強有力的指導性問題,可以讓你的代碼盡可能地易讀和自然。