iT邦幫忙

2023 iThome 鐵人賽

DAY 12
0
IT管理

FID 打造強力前端團隊系列 第 12

一眼看穿的程式

  • 分享至 

  • xImage
  •  

一眼看穿的程式

昨天說到開箱即用的程式,今天來看看一眼看穿的程式。

先從專案的目錄結構說起,不管你用什麼語言開發,都必須要有一個固定的目錄結構,這樣才能讓其他人看得懂你的程式。

忌同一個小組,出現不同的目錄結構,這樣會讓人很頭痛,這樣的程式碼,只有原作者看得懂,其他人都看不懂。

而且其他小組的成員也無法來協助你,不管在日後的維護或是開發上,都會變得很困難。

目錄結構-業務的核心 core

相信不少團隊在開發的時候,應該有有一個 project boilerplate,這樣才能讓團隊的成員,都能夠快速的開發,而且不會有太多的問題,就是我們上一章說的開箱即用的程式。

這種 boilerplate 的設計百百種,但對於一個面向業務的程式,我建議專案中需要存在一個「core」的目錄

蝦米瓦「core」?

core

我們來舉例 core 是幹嘛用的,如果一個停車系統,那麼專案裡面的 core 應該會有(但不限於)以下的目錄

- core
  - parking
  - Lot
  - Space
  - Ticket

如果是一個麥當勞點餐系統,那麼專案裡面的 core 應該會有(但不限於)以下的目錄

- core
  - order
  - menu
  - item
  - payment

我猜你應該猜出來,core 是幹嘛,core 和我們設計軟體時的「核心業務」有關,也就是說,core 裡面的程式碼,都是和業務有關的,而且是核心業務,和 MVC 的 M 有點像,但不完全一樣,因為 core 裡面的程式碼,不一定是 model,也可能是 controller

core 的好處

當我們打開一個陌生的程式,我們可以很快的找到程式的核心業務,而且不會有太多的雜物(當然,這個前提是你的程式碼是乾淨的)。

有了核心業務為主的程式碼,我們可以從這裡開始閱讀程式碼,並且如果你的程式碼是乾淨的,要注意,我是說乾淨,不是簡單,也就是你可能會很複雜,就好像一副大型的拼圖,從規律中找出規律,這樣的程式碼,才是乾淨的,所以並非說業務很複雜,就無法寫出乾淨的程式碼。

目錄的規則 - 一目了然

市面上有很多目錄規則和範例,大家可按照自己的偏好來設計,並無傷大雅,但有幾個小建議:

  • 可以從目錄的名稱,來看出目錄的用途。
  • 繼承規則,例如:core 下面的目錄,都是和業務有關的,而且是核心業務;view 下面的目錄,都是和畫面有關的。
  • 孤立規則:將特定功能的放在專屬他的目錄下,例如以下的目錄結構

建議:

- components
  - List
    - Items
      - Item
        - Title
        - Content
        - Action

不建議

- components
  - List
  - Items
  - Item
    - Title
    - Content
    - Action

使用 List 的時候,就會很明顯的知道,他是一個列表,而且他的子元件,都是和列表有關的,這樣的目錄結構,可以讓我們一目了然,並且程式碼需要寫成 compose components

// 這樣寫沒問題,但不建議,如果需要這樣寫,改成render props
<List>
	<List.Items>
		<List.Item>
			<List.Title />
			<List.Content />
			<List.Action />
		</List.Item>
	</List.Items>
</List>
// 或者可以這樣寫
<List>
	<List.Items>
		{({ Item }) => (
			<Item>
				<List.Item>
					{({ Title, Content, Action }) => (
						<>
							<Title />
							<Content />
							<Action />
						</>
					)}
				</List.Item>
			</Item>
		)}
	</List.Items>
</List>

但這樣寫好複雜,如果需要這樣的結構,它或許可以這樣寫


// 如果妳想客製化
const listData = []
<List
  data={listData}
  renderItem={({ item }) => (
    <List.Item>
      <List.Title />
      <List.Content />
      <List.Action />
    </List.Item>
  )}
/>

// 如果你只需要一個簡單的列表
const listData = []
<List data={listData} />

self-explanatory

盡然要一眼看穿,那麼程式碼也要 self-explanatory,也就是說,程式碼要自我解釋,不需要註解,就能看懂。

來看看以下的程式碼

const isAdult = (age) => {
	if (age >= 18) {
		return true;
	} else {
		return false;
	}
};

另外一個沒有 self-explanatory 的程式碼

const ia = (age) => {
	if (age >= 18) {
		return true;
	} else {
		return false;
	}
};

去標籤化

另外一個極端的例子,就是去標籤化,也就是說,程式碼不僅限於某個 core,而是可以跨 core,這類的程式碼,以描述功能為主,而不是描述業務。

你可以這樣寫:

// 假設座位狀態存儲在一個物件中,座位號作為鍵,值表示是否已預約(true 表示已預約,false 表示未預約)
const seatStatus = {
	A1: true,
	A2: false,
	A3: true,
	B1: false,
	B2: true,
	// 其他座位...
};

const isSeatReserved = (seat) => {
	// 檢查座位是否存在於 seatStatus 中,如果存在,返回其預約狀態,否則返回 false
	return seatStatus.hasOwnProperty(seat) ? seatStatus[seat] : false;
};

// 測試座位是否已預約
console.log(isSeatReserved("A1")); // true
console.log(isSeatReserved("B1")); // false
console.log(isSeatReserved("C1")); // false(不存在的座位)

我們可以改成

const isReserved = (isReserved) => {
	return isReserved === true;
};

console.log(isReserved("B1")); // true
console.log(isReserved("C1")); // false

目的明確

如果一段程式碼,你不知道他的目的是什麼,那麼這段程式碼就是不好的,因為你不知道他的目的,在修改的時候,你可能會破壞他的目的。

舉個例子,我們在使用 redux 的時候,需要用 dispatch 來觸發 action,語法大概如下

import useDispath from "react-redux";
import { addTodo } from "./actions";

const dispatch = useDispath();

dispatch(
	addTodo({
		id: 1,
		text: "hello world",
	})
);

這樣寫沒有問題,但就是太麻煩,我們可以這樣寫

import redux from "globalState/redux";

redux.addTodo({
	id: 1,
	text: "hello world",
});

之後如果你想換 redux,你只需要改一個地方就好了,而且你不需要知道 dispatch 是什麼,你只需要知道 redux 是什麼,而且他的目的是什麼。

Declarative

Declarative vs Imperative,這是一個很有趣的話題,也是一個很有趣的討論,但我們不會在這裡討論這個話題,我們只會討論一個簡單的問題,就是說,我們在面對業務的時候,儘量用 Declarative 的方式來寫程式碼,而不是 Imperative 的方式。

因為面對千變萬化的業務,我們永遠無法預測未來,也無法預測老闆的心,如果程式的順序即是程式的邏輯,那麼當業務變動的時候,我們的修改風險就會變大,因為我們不知道這個修改會不會影響其他的程式碼。

以下是一個簡單的例子

// Imperative
const isAdult = (age) => {
	if (age >= 18) {
		return true;
	} else {
		return false;
	}
};

// Declarative
const isAdult = (age) => {
	return age >= 18;
};

再來看另外一個比較複雜的例子

// Imperative
// 声明一个变量表示公交车的距离
var busDistance = 500; // 假设距离为500米

// 定义一个函数来检查公交车是否到站
function checkBusArrival() {
	if (busDistance <= 0) {
		console.log("公交车已经到站了!");
	} else {
		console.log("公交车还未到站,距离站台还有 " + busDistance + " 米。");
	}
}

// 模拟定时器,每秒更新公交车的距离
var interval = setInterval(function () {
	if (busDistance > 0) {
		busDistance -= 50; // 假设每秒减少50米
		checkBusArrival();
	} else {
		clearInterval(interval); // 公交车到站后停止定时器
	}
}, 1000); // 每秒执行一次

以下是改寫後的例子

// 声明一个函数,用于检查公交车是否到站
function isBusAtStation(distance) {
	return distance <= 0;
}

// 声明一个函数,用于显示公交车状态
function displayBusStatus(busAtStation, distance) {
	if (busAtStation) {
		console.log("公交车已经到站了!");
	} else {
		console.log("公交车还未到站,距离站台还有 " + distance + " 米。");
		// 模拟定时器,每秒更新公交车的距离
		setTimeout(function () {
			const newDistance = distance - 50; // 假设每秒减少50米
			if (newDistance > 0) {
				displayBusStatus(false, newDistance);
			} else {
				displayBusStatus(true, 0);
			}
		}, 1000);
	}
}

// 声明公交车的初始距离
const initialBusDistance = 500; // 假设初始距离为500米

// 使用声明式方式检查并显示公交车的状态
displayBusStatus(false, initialBusDistance);

下面的例子改起來,是不是比較「明確」和「有信心」呢?!

結論

以上列舉了幾個小技巧,讓我們可以一眼看穿程式碼,但這些技巧並不是一定要遵守,而是一個建議,如果你有更好的方法,歡迎分享給我。

當然包括 clean code 和 SOLID 原則,也是一個很好的技巧,但這些技巧,我們會在之後的章節中討論。


上一篇
技術儲備之技術目的:開箱即用
下一篇
技術儲備之人才:魚塘效益
系列文
FID 打造強力前端團隊30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言