Code Smells nello Sviluppo di XOOPS
Panoramica
Sezione intitolata “Panoramica”I code smells sono indicatori di potenziali problemi nel codice. Non significano necessariamente che il codice sia rotto, ma suggeriscono aree che potrebbero beneficiare del refactoring.
Code Smells Comuni
Sezione intitolata “Code Smells Comuni”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]Bloaters
Sezione intitolata “Bloaters”Metodo Lungo
Sezione intitolata “Metodo Lungo”// Smell: Method does too muchfunction processArticleSubmission($data) { // 100+ lines di validazione, salvataggio, notifica, 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;}Classe Grande (God Object)
Sezione intitolata “Classe Grande (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 { ... }Lista Lunga di Parametri
Sezione intitolata “Lista Lunga di Parametri”// 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 { ... }Abusatori di Orientamento agli Oggetti
Sezione intitolata “Abusatori di Orientamento agli Oggetti”Switch Statements
Sezione intitolata “Switch Statements”// 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; }}Campo Temporaneo
Sezione intitolata “Campo Temporaneo”// 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); }}Prevenzione dei Cambiamenti
Sezione intitolata “Prevenzione dei Cambiamenti”Divergent Change
Sezione intitolata “Divergent Change”// 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() { ... } }Intervento Sparato
Sezione intitolata “Intervento Sparato”// 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')); }}Dispensabili
Sezione intitolata “Dispensabili”Codice Morto
Sezione intitolata “Codice Morto”// 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);}Codice Duplicato
Sezione intitolata “Codice Duplicato”// 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); }}Feature Envy
Sezione intitolata “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; }}Checklist di Refactoring
Sezione intitolata “Checklist di Refactoring”Quando noti un code smell:
- Identifica - Quale smell è?
- Valuta - Quale è l’impatto?
- Pianifica - Quale tecnica di refactoring si applica?
- Testa - Assicurati che i test esistano prima del refactoring
- Effettua il Refactoring - Apporta piccoli cambiamenti incrementali
- Verifica - Esegui i test dopo ogni cambio
Documentazione Correlata
Sezione intitolata “Documentazione Correlata”- Clean Code Principles
- Code Organization
- Testing Best Practices