程式設計裡有一個規則叫 SDP(穩定依賴原則):最下面的「基礎」一定要很穩定,也要有安全鎖。基礎不穩或沒上鎖,上面再漂亮都會倒。
你在蓋積木城堡——地基要用最大最穩的積木(而且鎖好),牆壁與屋頂才可以自由換顏色。有人把「會亂跑的小積木」塞進地基,整座城堡就垮了;網站把「個資」放在沒上鎖的地基,也會出事。
在程式裡,像「登入系統、帳號資料、電話與地址」都是基礎模組。它們要同時滿足:
下面用兩個超小的 JS 例子,對照「壞例子」與「好例子」。
// 壞:沒有驗證、沒有權限、資料全裸
const db = { volunteers: [{ name: "小米", phone: "0912-345-678" }] };
// 任何人開 F12 都能呼叫,直接改掉別人的資料
function setVolunteerPhone(index, newPhone) {
db.volunteers[index].phone = newPhone; // 沒檢查就改
}
// 任何人都能直接列出全部電話
function listPhones() {
return db.volunteers.map(v => v.phone); // 全部曝露
}
console.log(listPhones()); // ["0912-345-678"]
setVolunteerPhone(0, "0999-000-000");
console.log(listPhones()); // 被亂改了!
// 1) 穩定的「基礎介面」:只提供必要、固定不常變的功能
const Security = (() => {
const VALID_TOKENS = new Set(["TOKEN_ADMIN"]);
const maskPhone = (phone) => phone.replace(/\d(?=\d{4})/g, "*");
return {
check(token) { return VALID_TOKENS.has(token); },
maskPhone
};
})();
// 2) 基礎資料層:不直接外露實體資料,只透過固定的 API 讀寫
const VolunteerRepo = (() => {
const _volunteers = [{ name: "小米", phone: "0912-345-678" }];
return {
listPublic() {
return _volunteers.map(v => ({ name: v.name, phone: Security.maskPhone(v.phone) }));
},
updatePhone(index, newPhone, token) {
if (!Security.check(token)) throw new Error("沒有權限");
_volunteers[index].phone = newPhone;
}
};
})();
// 3) 上層功能依賴「穩定基礎」,而不是反過來
console.log(VolunteerRepo.listPublic());
// [{ name: "小米", phone: "***-***-5678" }]
try {
VolunteerRepo.updatePhone(0, "0999-000-000", "BAD_TOKEN");
} catch (e) {
console.log("更新失敗:", e.message);
}
VolunteerRepo.updatePhone(0, "0999-000-000", "TOKEN_ADMIN");
console.log(VolunteerRepo.listPublic());
// 仍然只看到遮罩後的電話
這樣做的好處:
穩定:上層(常變)只依賴固定不常改的「基礎介面」。
安全:沒有權限的人改不到資料;讀取也只拿到遮罩後的內容。
清楚分層:上層想怎麼變(新增頁面、換 UI)都不會拖垮地基。
SDP:基礎模組要穩,上層來依賴它;不要讓基礎去依賴會常變的東西。
安全:基礎一定要上鎖(驗證、授權、遮罩/加密、最小權限)。
如果你做「線上表單」,欄位驗證、登入與權限、資料遮罩,哪一些應該放在「基礎模組」?為什麼?