XOOPS 開發中的程式碼異味
程式碼異味是程式碼中潛在問題的指標。它們不一定意味著程式碼已損壞,但它們表明可能受益於重構的區域。
常見程式碼異味
Section titled “常見程式碼異味”flowchart TD A[偵測到程式碼異味] --> B{類型?} B --> C[膨脹者] B --> D[物件導向濫用者] B --> E[變更預防器] B --> F[可棄置物] B --> G[耦合器]
C --> C1[長方法] C --> C2[大型類別] C --> C3[長參數清單]
D --> D1[Switch 陳述式] D --> D2[暫時欄位]
E --> E1[發散變更] E --> E2[槍口手術]
F --> F1[死代碼] F --> F2[重複程式碼]
G --> G1[功能嫉妒] G --> G2[不適當的親密性]// 異味: 方法做太多事情function processArticleSubmission($data) { // 100+ 行驗證、儲存、通知等}
// 解決方案: 提取到集中方法function processArticleSubmission(array $data): Article{ $this->validateInput($data); $article = $this->createArticle($data); $this->saveArticle($article); $this->notifySubscribers($article); return $article;}大型類別 (神祇物件)
Section titled “大型類別 (神祇物件)”// 異味: 類別做所有事情class ArticleManager { public function create() { ... } public function delete() { ... } public function sendEmail() { ... } public function generatePDF() { ... } public function exportToExcel() { ... } public function validateUser() { ... } public function checkPermissions() { ... } // ... 50 個更多的方法}
// 解決方案: 分割成集中的類別class ArticleService { ... }class ArticleExporter { ... }class ArticleNotifier { ... }class PermissionChecker { ... }// 異味: 過多參數function createArticle($title, $content, $author, $category, $tags, $status, $publishDate, $featured, $image) { ... }
// 解決方案: 使用參數物件class CreateArticleCommand { public string $title; public string $content; public int $authorId; public int $categoryId; public array $tags; public string $status; public ?DateTime $publishDate; public bool $featured; public ?string $image;}
function createArticle(CreateArticleCommand $command): Article { ... }物件導向濫用者
Section titled “物件導向濫用者”Switch 陳述式
Section titled “Switch 陳述式”// 異味: 使用 switch 進行型別檢查function getDiscount($userType) { switch ($userType) { case 'regular': return 0; case 'premium': return 10; case 'vip': return 20; default: return 0; }}
// 解決方案: 使用多型性interface UserType { public function getDiscount(): int;}
class RegularUser implements UserType { public function getDiscount(): int { return 0; }}
class PremiumUser implements UserType { public function getDiscount(): int { return 10; }}
class VipUser implements UserType { public function getDiscount(): int { return 20; }}// 異味: 欄位只在某些情況下使用class Article { private $tempCalculatedScore;
public function search($terms) { $this->tempCalculatedScore = $this->calculateScore($terms); // ... 使用分數 }}
// 解決方案: 作為參數或傳回值傳遞class Article { public function getSearchScore(array $terms): float { return $this->calculateScore($terms); }}// 異味: 一個類別因許多不同原因而改變class Article { public function save() { ... } // 資料庫變更 public function toJson() { ... } // API 格式變更 public function validate() { ... } // 業務規則變更 public function render() { ... } // UI 變更}
// 解決方案: 分離責任class Article { ... } // 領域物件僅class ArticleRepository { public function save() { ... } }class ArticleSerializer { public function toJson() { ... } }class ArticleValidator { public function validate() { ... } }// 異味: 一個變更需要許多檔案編輯// 變更日期格式需要編輯:// - ArticleController.php// - ArticleView.php// - ArticleAPI.php// - ArticleExport.php
// 解決方案: 集中化class DateFormatter { public function format(DateTime $date): string { return $date->format($this->config->get('date_format')); }}// 異味: 無法存取或未使用的程式碼function processData($data) { if (true) { return $this->handleData($data); } // 這永遠不會執行 return $this->legacyHandler($data);}
// 舊的未呼叫方法仍在程式碼庫中function oldMethod() { // 任何地方都未呼叫}
// 解決方案: 移除死代碼function processData($data) { return $this->handleData($data);}// 異味: 相同邏輯在多個地方class ArticleHandler { public function getActive() { $criteria = new CriteriaCompo(); $criteria->add(new Criteria('status', 'active')); return $this->getObjects($criteria); }}
class NewsHandler { public function getActive() { $criteria = new CriteriaCompo(); $criteria->add(new Criteria('status', 'active')); return $this->getObjects($criteria); }}
// 解決方案: 提取通用行為trait ActiveRecordsTrait { public function getActive(): array { $criteria = new CriteriaCompo(); $criteria->add(new Criteria('status', 'active')); return $this->getObjects($criteria); }}// 異味: 方法使用另一個物件的資料多於其本身class Invoice { public function calculateTotal(Customer $customer) { $total = 0; foreach ($this->items as $item) { $total += $item->price; } // 廣泛使用客戶資料 if ($customer->isPremium()) { $total *= (1 - $customer->getDiscountRate()); } if ($customer->getCountry() === 'US') { $total *= 1.08; // 稅 } return $total; }}
// 解決方案: 將行為移至具有資料的物件class Customer { public function applyDiscount(float $amount): float { return $this->isPremium() ? $amount * (1 - $this->discountRate) : $amount; }
public function applyTax(float $amount): float { return $this->country === 'US' ? $amount * 1.08 : $amount; }}重構檢查清單
Section titled “重構檢查清單”當你發現程式碼異味時:
- 識別 - 它是什麼異味?
- 評估 - 影響的嚴重程度是多少?
- 計畫 - 哪種重構技術適用?
- 測試 - 在重構前確保測試存在
- 重構 - 進行小的、增量的變更
- 驗證 - 每次變更後執行測試
- Clean Code Principles
- Code Organization
- Testing Best Practices