beio Logobeio

【Android 架構系列 02】與 AI 的契約:重新解讀 SOLID 原則

發布於

01. 前言:SOLID 不只是程式碼規範,它是 Prompt 的文法

在上一篇,我們見證了「義大利麵程式碼 (Spaghetti Code)」如何讓 AI 陷入幻覺與錯誤的泥沼。這篇我們將進入核心方法論:如何將古老的 SOLID 原則,轉化為控制 AI 的精準指令。

在過去,資深工程師教導新手遵守 SOLID 原則,是為了「人類的可讀性」與「好維護」。但在 2024 年,這些原則有了全新的戰略意義。

SOLID 是你與 AI 溝通的「共同語言」。

你是否發現,有時候 AI 給出的答案牛頭不對馬嘴?或者它在重構時改壞了不該改的地方?這通常不是 AI 笨,而是因為你的程式碼結構對 AI 來說「雜訊 (Noise)」太多。

AI 模型(LLM)的運作基於機率與注意力機制。如果我們能透過架構設計,減少 AI 需要關注的上下文(Context),它的準確率就會直線上升。

本篇將逐一解讀 SOLID 五大原則,看看它們如何變身為 Prompt Engineering (提示工程) 的最高指導原則。

02. S - 單一職責原則 (SRP)

Single Responsibility Principle

  • 傳統定義: 一個類別應該只有一個改變的理由。
  • AI 時代定義: 縮小上下文視窗 (Optimize Context Window),減少 AI 幻覺。

這是最重要的一條。AI 的「短期記憶」是有限的。

違反 SRP 的場景 (God Class): 當你把 UI、邏輯、資料庫都寫在 NewsScreen.kt (Compose) 或 MainActivity.kt,這意味著每當你想叫 AI 改一個「日期格式」的小功能,你都必須把這幾千行程式碼「餵」給它。AI 的注意力被分散,它在修改日期邏輯時,可能會「不小心」刪掉了 UI 的某個 Modifier。

遵守 SRP 的場景 (Micro Context): 假設你將資料轉換邏輯獨立成 NewsMapper.kt

Prompt 策略:

"請幫我優化 NewsMapper,處理 publishedAt 欄位為 null 的情況,預設回傳當前時間。"

這時,AI 眼前只有不到 50 行的純邏輯代碼。沒有 UI 干擾,沒有執行緒問題。在這種極簡的環境下,AI 的智商會達到巔峰,準確率接近 100%。

03. O - 開閉原則 (OCP)

Open/Closed Principle

  • 傳統定義: 對擴充開放,對修改封閉。
  • AI 時代定義: AI 擅長「生成新檔案」,不擅長「外科手術」。

你有沒有過這種經驗?你叫 AI 去修改一個複雜的舊函式,結果它把舊邏輯改得面目全非,Bug 滿天飛。

AI 的強項在於「從無到有」的生成 (Generation),而非「理解後修改」 (Refactoring)。

應用實例: 假設你的 App 原本使用 Glide 載入圖片,現在要換成 Coil。

  • Bad Way (修改模式): 叫 AI 去掃描所有 Composable,把 GlideImage 改掉。這絕對會漏。
  • Good Way (擴充模式): 你原本就有定義一個 ImageLoader 介面。

Prompt:

"請建立一個新類別 CoilImageLoader 實作 ImageLoader 介面。內部使用 Coil 的 ImageRequest。"

AI 會生成一個全新的檔案。你只需要在 DI (Dependency Injection) 的模組裡,把注入的實作換成新的類別。這完全規避了 AI 去觸碰舊程式碼的風險。

04. L - 里氏替換原則 (LSP)

Liskov Substitution Principle

  • 傳統定義: 子類別必須能替換父類別,且不影響程式正確性。
  • AI 時代定義: 確保 AI 生成的「插件」能無縫接軌。

這條原則保證了 AI 生成的「新零件」可以完美卡進你的機器裡。

當你要求 AI:「幫我實作一個 OfflineNewsRepository 來支援離線模式。」

如果你的架構遵守 LSP,這個新的 Repository 回傳的資料結構 (Result<List<Article>>)、錯誤拋出機制,必須跟原本的 OnlineNewsRepository 完全一致。

這讓你可以放心地對 AI 說:「把這個新實作換上去。」而不用擔心 UI 層會因為接不到資料而崩潰。這對於我們後續要講的 Mock 測試 至關重要。

05. I - 介面隔離原則 (ISP)

Interface Segregation Principle

  • 傳統定義: 不應強迫客戶端依賴它不使用的方法。
  • AI 時代定義: 減少 Prompt 的雜訊,讓 AI 專注。

不要定義一個包山包海的 NewsManager 介面,裡面有 getNews(), deleteNews(), updateUserPreferences(), trackAnalytics() 等 50 個方法。

如果你把這個介面丟給 AI 並要求它寫測試,它會困惑:「你到底要我測哪一個?」它可能會生成一堆空的 Mock 方法,讓程式碼變得很髒。

AI 友善的設計: 將介面拆小。例如 NewsReader (唯讀) 和 NewsEditor (編輯)。 當你只要 AI 處理顯示邏輯時,只給它 NewsReader。這能有效防止 AI 自作聰明去呼叫刪除或修改的方法。

06. D - 依賴反轉原則 (DIP)

Dependency Inversion Principle

  • 傳統定義: 高層模組不應依賴低層模組,兩者都應依賴抽象。
  • AI 時代定義: 介面 (Interface) 是你給 AI 的「規格書」與「合約」。

這是 Clean Architecture 的核心,也是指揮 AI 的最高指令

在 AI 時代,開發流程發生了逆轉:

  1. 人類 (架構師): 定義 Interface。這是「合約」。
  2. AI (施工隊): 實作 Implementation。

實戰 Prompt 示範:

我們想做一個天氣功能。

Step 1: 人類定義合約 (在 Domain Layer)

// WeatherRepository.kt
interface WeatherRepository {
    // 我們規定:不管底層怎麼做,回傳一定要是 Result<Weather>
    suspend fun getCityWeather(city: String): Result<Weather>
}

Step 2: AI 執行實作

Prompt:

"你是 Android 資深工程師。請實作 WeatherRepository 介面。 要求:使用 Retrofit 呼叫 OpenWeatherMap API。請處理 IOException 並回傳 Result.failure。"

因為你已經鎖死了 interface,AI 沒辦法亂改回傳型別,也沒辦法發明奇怪的函式名稱。它只能乖乖地在 override fun 的框架內填寫邏輯。DIP 讓 AI 的產出變得可預測、可控制。

07. 總結:SOLID 讓 AI 變得聽話

如果說 AI 是一匹野馬,那麼 SOLID 原則就是韁繩。

  • S (SRP): 讓 AI 一次只專注一件事,減少分心。
  • O (OCP): 讓 AI 多寫新檔,少改舊檔,減少破壞。
  • L (LSP): 確保 AI 的新零件能無縫替換舊零件。
  • I (ISP): 給 AI 的指令要精確,不要給多餘的資訊。
  • D (DIP): 先寫好介面合約,再讓 AI 填空。

掌握了這些心法,我們就準備好進入實作階段了。

但是,有了介面 (Interface) 和實作 (Implementation),我們要怎麼把它們「組裝」起來?如果還是手動 new RepositoryImpl(),那耦合還是沒解開。

在下一篇,我們將介紹如何讓 Hilt 成為 AI 的「接線生」,解決架構落地的最後一哩路。

下集預告:【Part 3】依賴注入 (DI):讓 Hilt 成為 AI 的「接線生」,解決耦合的最後一哩路


Ken Huang

關於作者

Ken Huang

熱衷於嘗試各類新技術的軟體開發者,現階段主力為 Android / iOS 雙平台開發,同時持續深耕前端與後端技術,以成為全端工程師與軟體架構師為目標。

最廣為人知的代表作為 BePTT。開發哲學是「以做身體健康為前提」,致力於在工作與生活平衡的基礎上,打造出擁有絕佳使用體驗的軟體服務。

這裡是用於紀錄與分享開發經驗的空間,希望能透過我的實戰筆記,幫助開發者解決疑難雜症並提升效率。

Android APP DevelopmentiOS APP DevelopmentBePTT CreatorFull Stack Learner