Code Smells bei der XOOPS-Entwicklung
Übersicht
Abschnitt betitelt „Übersicht“Code Smells sind Indikatoren für potenzielle Probleme im Code. Sie bedeuten nicht unbedingt, dass der Code fehlerhaft ist, aber sie deuten auf Bereiche hin, die von einer Umgestaltung profitieren könnten.
Häufige Code Smells
Abschnitt betitelt „Häufige Code Smells“flowchart TD A[Code Smell Detected] --> B{Type?} B --> C[Bloaters] B --> D[Object-Orientation Abusers] B --> E[Change Preventers] B --> F[Dispensables] B --> G[Couplers]
C --> C1[Long Method] C --> C2[Large Class] C --> C3[Long Parameter List]
D --> D1[Switch Statements] D --> D2[Temporary Field]
E --> E1[Divergent Change] E --> E2[Shotgun Surgery]
F --> F1[Dead Code] F --> F2[Duplicate Code]
G --> G1[Feature Envy] G --> G2[Inappropriate Intimacy]Aufbläher
Abschnitt betitelt „Aufbläher“Lange Methode
Abschnitt betitelt „Lange Methode“// Smell: Method does too muchfunction processArticleSubmission($data) { // 100+ lines of validation, saving, notification, etc.}
// Solution: Extract into focused methodsfunction processArticleSubmission(array $data): Article{ $this->validateInput($data); $article = $this->createArticle($data); $this->saveArticle($article); $this->notifySubscribers($article); return $article;}Große Klasse (God Object)
Abschnitt betitelt „Große Klasse (God Object)“// Smell: Class does everythingclass ArticleManager { public function create() { ... } public function delete() { ... } public function sendEmail() { ... } public function generatePDF() { ... } public function exportToExcel() { ... } public function validateUser() { ... } public function checkPermissions() { ... } // ... 50 more methods}
// Solution: Split into focused classesclass ArticleService { ... }class ArticleExporter { ... }class ArticleNotifier { ... }class PermissionChecker { ... }Lange Parameterliste
Abschnitt betitelt „Lange Parameterliste“// Smell: Too many parametersfunction createArticle($title, $content, $author, $category, $tags, $status, $publishDate, $featured, $image) { ... }
// Solution: Use parameter objectclass 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 { ... }Missbrauch der Objektorientierung
Abschnitt betitelt „Missbrauch der Objektorientierung“Switch-Anweisungen
Abschnitt betitelt „Switch-Anweisungen“// Smell: Type checking with switchfunction getDiscount($userType) { switch ($userType) { case 'regular': return 0; case 'premium': return 10; case 'vip': return 20; default: return 0; }}
// Solution: Use polymorphisminterface 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; }}Temporäres Feld
Abschnitt betitelt „Temporäres Feld“// Smell: Fields only used in certain situationsclass Article { private $tempCalculatedScore;
public function search($terms) { $this->tempCalculatedScore = $this->calculateScore($terms); // ... use score }}
// Solution: Pass as parameter or return valueclass Article { public function getSearchScore(array $terms): float { return $this->calculateScore($terms); }}Änderungsverhinderer
Abschnitt betitelt „Änderungsverhinderer“Abweichende Änderung
Abschnitt betitelt „Abweichende Änderung“// Smell: One class changed for many different reasonsclass Article { public function save() { ... } // Database change public function toJson() { ... } // API format change public function validate() { ... } // Business rule change public function render() { ... } // UI change}
// Solution: Separate responsibilitiesclass Article { ... } // Domain object onlyclass ArticleRepository { public function save() { ... } }class ArticleSerializer { public function toJson() { ... } }class ArticleValidator { public function validate() { ... } }Schrotflintenchirurgie
Abschnitt betitelt „Schrotflintenchirurgie“// Smell: One change requires many file edits// Changing date format requires editing:// - ArticleController.php// - ArticleView.php// - ArticleAPI.php// - ArticleExport.php
// Solution: Centralizeclass DateFormatter { public function format(DateTime $date): string { return $date->format($this->config->get('date_format')); }}Verzichtbar
Abschnitt betitelt „Verzichtbar“Toter Code
Abschnitt betitelt „Toter Code“// Smell: Unreachable or unused codefunction processData($data) { if (true) { return $this->handleData($data); } // This never executes return $this->legacyHandler($data);}
// Old unused method still in codebasefunction oldMethod() { // Not called anywhere}
// Solution: Remove dead codefunction processData($data) { return $this->handleData($data);}Duplizierter Code
Abschnitt betitelt „Duplizierter Code“// Smell: Same logic in multiple placesclass 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); }}
// Solution: Extract common behaviortrait ActiveRecordsTrait { public function getActive(): array { $criteria = new CriteriaCompo(); $criteria->add(new Criteria('status', 'active')); return $this->getObjects($criteria); }}Koppler
Abschnitt betitelt „Koppler“Feature Envy
Abschnitt betitelt „Feature Envy“// Smell: Method uses another object's data more than its ownclass Invoice { public function calculateTotal(Customer $customer) { $total = 0; foreach ($this->items as $item) { $total += $item->price; } // Uses customer data extensively if ($customer->isPremium()) { $total *= (1 - $customer->getDiscountRate()); } if ($customer->getCountry() === 'US') { $total *= 1.08; // Tax } return $total; }}
// Solution: Move behavior to the object with the dataclass 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; }}Refactoring-Checkliste
Abschnitt betitelt „Refactoring-Checkliste“Wenn Sie einen Code Smell entdecken:
- Identifizieren - Um welchen Smell handelt es sich?
- Bewerten - Wie schwerwiegend ist die Auswirkung?
- Planen - Welche Refactoring-Technik ist anwendbar?
- Testen - Stellen Sie sicher, dass Tests vorhanden sind, bevor Sie umgestalten
- Refaktorieren - Nehmen Sie kleine, inkrementelle Änderungen vor
- Verifizieren - Führen Sie Tests nach jeder Änderung aus
Verwandte Dokumentation
Abschnitt betitelt „Verwandte Dokumentation“- Clean Code Principles
- Code Organization
- Testing Best Practices