การ Review Diagram และ Refactor Design

บทนำ: ทำไมต้อง Review Diagram?

เมื่อเขียนโปรแกรม หลายทีมคิดว่า “code เสร็จแล้ว ก็เสร็จ” แต่ในความเป็นจริง:

text❌ ปัญหา 1: Design ไม่ชัดเจน → Team ไม่เข้าใจ architecture
❌ ปัญหา 2: Dependency ยุ่งเหยิง → ยากต่อการ maintain
❌ ปัญหา 3: Code pattern ไม่สม่ำเสมอ → ยากต่อการ extend
❌ ปัญหา 4: Technical debt สะสม → เปลี่ยนแปลงช้า
❌ ปัญหา 5: Refactoring ไม่มีแผน → ผิดพลาด

Review Diagram & Refactor Design = การ systematically analyze และ improve architecture ของ system


Step 1: Creating Design Diagrams

Class Diagram: Visualize Structure

Purpose: Show classes, relationships, และ dependencies

textNotation:
┌─────────────────────┐
│      ClassName      │
├─────────────────────┤
│  - attribute1: Type │  ← Attributes
│  - attribute2: Type │
├─────────────────────┤
│  + method1(): Type  │  ← Methods
│  + method2(param)   │
└─────────────────────┘

Relationships:
- Inheritance: ▲────
- Implementation: △────
- Association: ────→
- Composition: ◆────
- Dependency: ··→

ตัวอย่างที่ 1: E-Commerce System Class Diagram

textSimple textual representation:

┌──────────────────┐
│     Order        │
├──────────────────┤
│ - id: int        │
│ - date: Date     │
│ - status: String │
├──────────────────┤
│ + getTotal()     │
│ + changeStatus() │
└────────┬─────────┘
         │ contains 1..*
         ▼
┌──────────────────┐
│   OrderItem      │
├──────────────────┤
│ - quantity: int  │
│ - price: double  │
├──────────────────┤
│ + getSubtotal()  │
└────────┬─────────┘
         │ references
         ▼
┌──────────────────┐
│    Product       │
├──────────────────┤
│ - id: int        │
│ - name: String   │
│ - price: double  │
├──────────────────┤
│ + getPrice()     │
└──────────────────┘

         ▲
         │ uses
         │
┌────────┴──────────┐
│  PaymentService   │
├───────────────────┤
│ + process()       │
│ + refund()        │
└───────────────────┘

ตัวอย่างที่ 2: Pattern-Based Diagram

textFactory Pattern Architecture:

┌──────────────────────┐
│ ReportFactory        │  ← Factory (creates objects)
├──────────────────────┤
│ + createReport()     │
└────────┬─────────────┘
         │ creates
         │
    ┌────┴────┬─────────┬──────────┐
    │          │         │          │
    ▼          ▼         ▼          ▼
┌────────┐ ┌──────┐ ┌──────┐ ┌────────┐
│Report  │◄─┤PDF   │ │Excel │ │ CSV    │
│(I)     │  │Report│ │Report│ │Report  │
└────────┘  └──────┘ └──────┘ └────────┘
    ▲
    │ implements
    │
┌─────────────────┐
│ClientCode       │  ← Uses abstraction, not concrete
├─────────────────┤
│+ generate()     │
└─────────────────┘

Step 2: Analyzing the Diagram

ตัวอย่างที่ 3: Identifying Problems in Design

java// Bad design (as a diagram):
┌─────────────────────────────┐
│   MonolithicService         │  ← GOD CLASS!
├─────────────────────────────┤
│ - database: Database        │
│ - emailService: Email       │
│ - logger: Logger            │
│ - paymentGateway: Payment   │
│ - analyticsEngine: Analytics
│ - notificationManager: Notif│
├─────────────────────────────┤
│ + createUser()              │  ← Too many methods
│ + deleteUser()              │
│ + sendEmail()               │
│ + logActivity()             │
│ + processPayment()          │
│ + trackEvent()              │
│ + notifyUser()              │
│ + generateReport()          │
│ + updateInventory()         │
│ + ... (20+ more methods)    │
└─────────────────────────────┘
     │
     ├──→ DatabaseImpl
     ├──→ SMTPEmailService
     ├──→ FileLogger
     ├──→ PaymentGatewayImpl
     └──→ GoogleAnalytics

PROBLEMS:
❌ Single Responsibility Principle violated
❌ Too many dependencies (6+)
❌ Hard to test
❌ Hard to reuse
❌ Too many reasons to change

วิธีแก้: Refactor into Layered Architecture

textGood design (as a diagram):

┌─────────────────────────────────────────┐
│         PRESENTATION LAYER              │
│  ┌────────────────────────────────────┐ │
│  │      UserController                │ │
│  └────────────────────────────────────┘ │
└─────────────┬─────────────────────────┘
              │ uses
              ▼
┌─────────────────────────────────────────┐
│         SERVICE LAYER                   │
│  ┌─────────────────┐  ┌─────────────┐  │
│  │ UserService     │  │EmailService │  │
│  └─────────────────┘  └─────────────┘  │
│  ┌─────────────────┐  ┌─────────────┐  │
│  │PaymentService   │  │LogService   │  │
│  └─────────────────┘  └─────────────┘  │
└─────────────┬──────────────────────────┘
              │ uses
              ▼
┌─────────────────────────────────────────┐
│         REPOSITORY LAYER                │
│  ┌────────────────────────────────────┐ │
│  │    UserRepository (interface)      │ │
│  │    + findById()                    │ │
│  │    + save()                        │ │
│  │    + update()                      │ │
│  └────────────────────────────────────┘ │
│  ┌────────────────────────────────────┐ │
│  │  UserRepositoryImpl (MySQL impl)    │ │
│  └────────────────────────────────────┘ │
└─────────────┬──────────────────────────┘
              │ connects
              ▼
┌─────────────────────────────────────────┐
│         DATABASE LAYER                  │
│           MySQL Database                │
└─────────────────────────────────────────┘

BENEFITS:
✓ Clear separation of concerns
✓ Each layer has single responsibility
✓ Easy to test each layer
✓ Easy to swap implementations
✓ Easier to understand and maintain

Step 3: Identifying Refactoring Opportunities

ตัวอย่างที่ 4: Code Smell → Refactoring Checklist

textRED FLAGS in Code/Diagram:

1. GOD CLASS (Too many responsibilities)
   ❌ One class doing everything
   ✓ Refactor: Split into multiple classes
   
2. TIGHT COUPLING (Too many dependencies)
   ❌ Class A depends on Class B, C, D, E
   ✓ Refactor: Use interfaces and DI
   
3. DUPLICATE CODE (Same logic repeated)
   ❌ calculateDiscount() in 3 different classes
   ✓ Refactor: Extract to common service
   
4. LONG METHOD (Method does too much)
   ❌ processOrder() has 50+ lines
   ✓ Refactor: Break into smaller methods
   
5. FEATURE ENVY (Using other class's data too much)
   ❌ Controller accessing repository directly
   ✓ Refactor: Go through service layer
   
6. INAPPROPRIATE INTIMACY (Classes know too much about each other)
   ❌ UserController directly accessing DatabaseConnection
   ✓ Refactor: Use repository pattern

Real Scenario: Refactoring a Payment System

java// BEFORE: Problematic Design
public class PaymentProcessor {
    // ← Multiple responsibilities
    
    // Responsibility 1: Calculate discount
    public double calculateDiscount(Order order) {
        if (order.getTotal() > 1000) {
            return order.getTotal() * 0.1;
        } else if (order.getTotal() > 500) {
            return order.getTotal() * 0.05;
        }
        return 0;
    }
    
    // Responsibility 2: Process payment
    public boolean processPayment(Order order, PaymentMethod method) {
        // Complex payment logic
    }
    
    // Responsibility 3: Send notification
    public void sendNotification(Order order) {
        // Email logic
    }
    
    // Responsibility 4: Log transaction
    public void logTransaction(Order order) {
        // Logging logic
    }
}

// PROBLEMS:
// ❌ 4 reasons to change
// ❌ Hard to test
// ❌ Hard to reuse discount logic

Refactored Design

java// AFTER: Improved Design with Separation of Concerns

// 1. Discount calculation → separate service
public interface DiscountStrategy {
    double calculateDiscount(Order order);
}

public class PremiumDiscount implements DiscountStrategy {
    @Override
    public double calculateDiscount(Order order) {
        if (order.getTotal() > 1000) return order.getTotal() * 0.1;
        return 0;
    }
}

// 2. Payment processing → separate service
public interface PaymentService {
    boolean charge(Order order, PaymentMethod method);
}

public class CreditCardPaymentService implements PaymentService {
    @Override
    public boolean charge(Order order, PaymentMethod method) {
        // Credit card logic only
    }
}

// 3. Notification → separate service
public interface NotificationService {
    void sendOrderConfirmation(Order order);
}

public class EmailNotificationService implements NotificationService {
    @Override
    public void sendOrderConfirmation(Order order) {
        // Email logic only
    }
}

// 4. Logging → separate service
public interface TransactionLogger {
    void log(Order order, String action);
}

public class FileTransactionLogger implements TransactionLogger {
    @Override
    public void log(Order order, String action) {
        // Logging logic only
    }
}

// 5. Orchestrator: Composes all services
public class OrderProcessor {
    private DiscountStrategy discountStrategy;
    private PaymentService paymentService;
    private NotificationService notificationService;
    private TransactionLogger logger;
    
    // Dependency Injection
    public OrderProcessor(
        DiscountStrategy discountStrategy,
        PaymentService paymentService,
        NotificationService notificationService,
        TransactionLogger logger
    ) {
        this.discountStrategy = discountStrategy;
        this.paymentService = paymentService;
        this.notificationService = notificationService;
        this.logger = logger;
    }
    
    public void processOrder(Order order, PaymentMethod method) {
        try {
            // 1. Calculate discount
            double discount = discountStrategy.calculateDiscount(order);
            order.setDiscount(discount);
            
            // 2. Process payment
            boolean paid = paymentService.charge(order, method);
            if (!paid) {
                throw new RuntimeException("Payment failed");
            }
            
            // 3. Notify customer
            notificationService.sendOrderConfirmation(order);
            
            // 4. Log transaction
            logger.log(order, "COMPLETED");
            
        } catch (Exception e) {
            logger.log(order, "FAILED: " + e.getMessage());
            throw e;
        }
    }
}

// BENEFITS:
// ✓ Each class has ONE responsibility
// ✓ Easy to test each component independently
// ✓ Easy to swap implementations (e.g., different discount strategy)
// ✓ Easy to reuse services (e.g., DiscountStrategy used elsewhere)
// ✓ Only 1 reason to change per class

Step 4: Creating a Refactoring Plan

Refactoring Roadmap Template

text┌─────────────────────────────────────────────────────┐
│          REFACTORING PLAN DOCUMENT                  │
├─────────────────────────────────────────────────────┤
│                                                     │
│ PROJECT: Order Management System                   │
│ DATE: 2025-11-06                                   │
│                                                     │
├─────────────────────────────────────────────────────┤
│ 1. CURRENT STATE ANALYSIS                          │
│                                                     │
│   Problem 1: PaymentProcessor is a God Class       │
│   - 4 different responsibilities                   │
│   - 15+ methods                                    │
│   - High complexity                                │
│   - Hard to test                                   │
│                                                     │
│   Problem 2: Tight coupling                        │
│   - PaymentProcessor depends on:                   │
│     * Database                                     │
│     * EmailService                                 │
│     * Logger                                       │
│     * ExternalPaymentAPI                           │
│   - Difficult to mock for testing                  │
│                                                     │
├─────────────────────────────────────────────────────┤
│ 2. DESIRED STATE (TARGET ARCHITECTURE)             │
│                                                     │
│   - Extract services (Discount, Payment, Email)    │
│   - Use dependency injection                       │
│   - Each class: single responsibility              │
│   - Interfaces for abstraction                     │
│                                                     │
├─────────────────────────────────────────────────────┤
│ 3. REFACTORING STEPS                               │
│                                                     │
│   Phase 1 (Week 1): Extract discount logic         │
│   ├─ Create DiscountStrategy interface             │
│   ├─ Create implementations (Premium, Standard)    │
│   ├─ Update PaymentProcessor to use it             │
│   ├─ Write unit tests                              │
│   └─ Deploy with feature flag                      │
│                                                     │
│   Phase 2 (Week 2): Extract payment service        │
│   ├─ Create PaymentService interface               │
│   ├─ Create implementations (CreditCard, eWallet)  │
│   ├─ Update PaymentProcessor                       │
│   ├─ Write integration tests                       │
│   └─ Deploy with feature flag                      │
│                                                     │
│   Phase 3 (Week 3): Extract notifications         │
│   ├─ Create NotificationService interface          │
│   ├─ Create EmailNotification impl                 │
│   ├─ Update PaymentProcessor                       │
│   ├─ Test end-to-end                               │
│   └─ Deploy                                        │
│                                                     │
│   Phase 4 (Week 4): Extract logging               │
│   ├─ Create TransactionLogger interface            │
│   ├─ Create implementations                        │
│   ├─ Final cleanup                                 │
│   ├─ Remove old code                               │
│   └─ Deploy final version                          │
│                                                     │
├─────────────────────────────────────────────────────┤
│ 4. RISKS & MITIGATION                              │
│                                                     │
│   Risk: Breaking existing code during refactor     │
│   Mitigation: Feature flags + comprehensive tests  │
│                                                     │
│   Risk: Performance regression                     │
│   Mitigation: Load testing before each deploy      │
│                                                     │
│   Risk: Team doesn't understand new design         │
│   Mitigation: Code review + documentation          │
│                                                     │
├─────────────────────────────────────────────────────┤
│ 5. SUCCESS CRITERIA                                │
│                                                     │
│   ✓ All tests pass                                 │
│   ✓ Code coverage > 80%                            │
│   ✓ No performance regression                      │
│   ✓ Team understands new architecture              │
│   ✓ Documentation updated                          │
│   ✓ Zero production incidents                      │
│                                                     │
└─────────────────────────────────────────────────────┘

Step 5: Testing the Refactored Code

ตัวอย่างที่ 5: Before & After Testing

java// BEFORE: Hard to test
@Test
public void testProcessOrder() {
    // Problem: Must use real services
    PaymentProcessor processor = new PaymentProcessor();
    
    // Real database connection
    // Real email service
    // Real payment gateway
    // = Slow, flaky, unreliable tests
    
    Order order = new Order();
    boolean result = processor.processOrder(order, new CreditCard());
    
    assertTrue(result);
    // Can't verify specific behavior
}

// AFTER: Easy to test with mocks
@Test
public void testProcessOrderSuccess() {
    // Mock all dependencies
    DiscountStrategy mockDiscount = mock(DiscountStrategy.class);
    when(mockDiscount.calculateDiscount(any())).thenReturn(10.0);
    
    PaymentService mockPayment = mock(PaymentService.class);
    when(mockPayment.charge(any(), any())).thenReturn(true);
    
    NotificationService mockNotification = mock(NotificationService.class);
    
    TransactionLogger mockLogger = mock(TransactionLogger.class);
    
    // Create processor with mocks (DI)
    OrderProcessor processor = new OrderProcessor(
        mockDiscount,
        mockPayment,
        mockNotification,
        mockLogger
    );
    
    // Test
    Order order = new Order();
    order.setTotal(100);
    processor.processOrder(order, new CreditCard());
    
    // Verify behavior
    verify(mockDiscount).calculateDiscount(order);
    verify(mockPayment).charge(order, new CreditCard());
    verify(mockNotification).sendOrderConfirmation(order);
    verify(mockLogger).log(order, "COMPLETED");
    
    // Fast, reliable, focused test
}

@Test
public void testProcessOrderPaymentFails() {
    // Test specific scenario
    PaymentService mockPayment = mock(PaymentService.class);
    when(mockPayment.charge(any(), any())).thenReturn(false);
    
    NotificationService mockNotification = mock(NotificationService.class);
    TransactionLogger mockLogger = mock(TransactionLogger.class);
    
    OrderProcessor processor = new OrderProcessor(
        mock(DiscountStrategy.class),
        mockPayment,
        mockNotification,
        mockLogger
    );
    
    // Verify payment failure is handled
    assertThrows(RuntimeException.class, () -> {
        processor.processOrder(new Order(), new CreditCard());
    });
    
    // Email should NOT be sent on failure
    verify(mockNotification, never()).sendOrderConfirmation(any());
}

Best Practices: Refactoring Checklist

textBEFORE REFACTORING:
  ☑ Create comprehensive test suite
  ☑ Ensure all tests pass
  ☑ Version control: create feature branch
  ☑ Document current architecture
  ☑ Communicate with team
  ☑ Create refactoring plan
  ☑ Get code review approval

DURING REFACTORING:
  ☑ Refactor in small steps
  ☑ Run tests after each change
  ☑ Use feature flags for gradual rollout
  ☑ Keep commits small and focused
  ☑ Document decisions in commit messages
  ☑ Regular communication with team

AFTER REFACTORING:
  ☑ All tests pass
  ☑ Code review approval
  ☑ Performance testing
  ☑ Update documentation
  ☑ Monitor production
  ☑ Gather team feedback
  ☑ Update architecture diagrams

TOOLS FOR DIAGRAMS:
  ✓ UML Editors: Lucidchart, Draw.io, Visual Paradigm
  ✓ Team Collaboration: Miro, Mural
  ✓ Documentation: ArchiMate, C4 Model

Common Refactoring Patterns

textPATTERN 1: Extract Class
Problem: Class has multiple responsibilities
Solution: Split into multiple classes
  PaymentProcessor → PaymentService + DiscountService

PATTERN 2: Extract Method
Problem: Method is too long
Solution: Break into smaller methods
  processOrder() → calculateTotal() + processPayment() + sendEmail()

PATTERN 3: Extract Interface
Problem: Tight coupling to implementation
Solution: Depend on interface, not concrete class
  Database → DatabaseService (interface)

PATTERN 4: Replace Magic Numbers with Constants
Problem: Hard-coded values scattered
Solution: Define constants
  0.1 → PREMIUM_DISCOUNT_RATE

PATTERN 5: Move Method
Problem: Method should be in different class
Solution: Move to appropriate class
  calculateOrderTotal() from Controller → Order class

PATTERN 6: Replace Conditional with Polymorphism
Problem: Many if-else branches
Solution: Use strategy pattern
  if (type == "pdf") → use PdfStrategy

สรุป

การ Review Diagram และ Refactor Design ไม่ใช่ “optional activity” แต่เป็น critical part ของ software development lifecycle:

Key Activities:

  • Visualize – Draw diagrams เพื่อเข้าใจ architecture
  • Analyze – Identify code smells และ architectural issues
  • Plan – Create refactoring roadmap
  • Execute – Refactor incrementally กับ tests
  • Verify – Test thoroughly ก่อน deploy

ประโยชน์ของ Regular Review & Refactoring:

  • Code quality ยังคง high
  • Technical debt ไม่สะสม
  • New features เพิ่มได้เร็ว
  • Bug ลด
  • Team productivity เพิ่ม

Common Mistakes to Avoid:

  • ✗ Refactor without tests
  • ✗ Refactor too much at once
  • ✗ Ignore team input
  • ✗ Forget to document changes
  • ✗ Deploy without verification

Design review และ refactoring คือ “health checkup” ของระบบ – ถ้าทำอย่างต่อเนื่องและ systematic คุณจะรักษา codebase ให้ healthy, scalable และ maintainable สำหรับปีต่อปีมาได้ สิ่งนี้คือสิ่งที่ distinguish professionals จาก amateurs – ability ที่จะ see beyond “it works” และ envision “how can we make it better”