Chất Lượng Code trong Java

1. Nguyên Tắc Clean Code

1.1 SOLID Principles

  • Single Responsibility Principle (SRP)
  • Open/Closed Principle (OCP)
  • Liskov Substitution Principle (LSP)
  • Interface Segregation Principle (ISP)
  • Dependency Inversion Principle (DIP)

1.2 Clean Code Principles

  • DRY (Don't Repeat Yourself)
  • KISS (Keep It Simple, Stupid)
  • YAGNI (You Aren't Gonna Need It)
  • Composition Over Inheritance
  • Law of Demeter

2. Best Practices và Design Patterns

2.1 Naming Conventions

// Bad
public class data {
    private int x;
    private String s;

    public void calc() {
        // Some calculation
    }
}

// Good
public class CustomerOrder {
    private int quantity;
    private String productName;

    public BigDecimal calculateTotalPrice() {
        return quantity.multiply(getUnitPrice());
    }
}

2.2 Method Design

// Bad: Method does too many things
public void processOrder(Order order) {
    validateOrder(order);
    calculateTotal(order);
    updateInventory(order);
    sendEmail(order);
    updateDatabase(order);
}

// Good: Single Responsibility
public void processOrder(Order order) {
    orderValidator.validate(order);
    orderProcessor.process(order);
}

public class OrderProcessor {
    private final InventoryService inventoryService;
    private final NotificationService notificationService;
    private final OrderRepository orderRepository;

    public void process(Order order) {
        order.calculateTotal();
        inventoryService.update(order);
        notificationService.sendConfirmation(order);
        orderRepository.save(order);
    }
}

3. Code Smells và Anti-patterns

3.1 Common Code Smells

  • Duplicate Code
  • Long Method
  • Large Class
  • Long Parameter List
  • Data Class
  • Feature Envy
  • Primitive Obsession
  • Switch Statements
  • Temporary Field
  • Refused Bequest

3.2 Anti-patterns Examples

// Bad: God Class
public class OrderManager {
    private List<Order> orders;
    private List<Customer> customers;
    private List<Product> products;

    public void createOrder() { /* ... */ }
    public void updateCustomer() { /* ... */ }
    public void processPayment() { /* ... */ }
    public void generateInvoice() { /* ... */ }
    public void updateInventory() { /* ... */ }
    // Many more methods...
}

// Good: Separated Responsibilities
public class OrderService {
    private final CustomerService customerService;
    private final PaymentService paymentService;
    private final InventoryService inventoryService;

    public Order createOrder(OrderRequest request) {
        Customer customer = customerService.getCustomer(request.getCustomerId());
        Order order = new Order(customer, request.getItems());
        paymentService.process(order);
        inventoryService.update(order);
        return orderRepository.save(order);
    }
}

4. Code Organization

4.1 Package Structure

com.company.project/
├── domain/
│   ├── model/
│   ├── repository/
│   └── service/
├── application/
│   ├── dto/
│   ├── mapper/
│   └── service/
├── infrastructure/
│   ├── config/
│   ├── persistence/
│   └── security/
└── presentation/
    ├── controller/
    ├── advice/
    └── validator/

4.2 Class Organization

public class OrderService {
    // 1. Constants
    private static final int MAX_ITEMS = 100;

    // 2. Fields
    private final OrderRepository orderRepository;
    private final CustomerService customerService;

    // 3. Constructors
    public OrderService(OrderRepository orderRepository,
                       CustomerService customerService) {
        this.orderRepository = orderRepository;
        this.customerService = customerService;
    }

    // 4. Public Methods
    public Order createOrder(OrderRequest request) {
        validateRequest(request);
        return processOrder(request);
    }

    // 5. Private Methods
    private void validateRequest(OrderRequest request) {
        if (request.getItems().size() > MAX_ITEMS) {
            throw new ValidationException("Too many items");
        }
    }

    private Order processOrder(OrderRequest request) {
        // Processing logic
    }
}

5. Testing Quality

5.1 Unit Testing

@ExtendWith(MockitoExtension.class)
public class OrderServiceTest {
    @Mock
    private OrderRepository orderRepository;

    @Mock
    private CustomerService customerService;

    @InjectMocks
    private OrderService orderService;

    @Test
    void whenCreateOrder_thenSuccess() {
        // Given
        OrderRequest request = createValidRequest();
        Customer customer = createCustomer();
        when(customerService.getCustomer(any()))
            .thenReturn(customer);

        // When
        Order result = orderService.createOrder(request);

        // Then
        assertThat(result).isNotNull();
        assertThat(result.getCustomer()).isEqualTo(customer);
        verify(orderRepository).save(any(Order.class));
    }

    @Test
    void whenCreateOrderWithTooManyItems_thenThrowException() {
        // Given
        OrderRequest request = createInvalidRequest();

        // When/Then
        assertThrows(ValidationException.class,
            () -> orderService.createOrder(request));
    }
}

5.2 Integration Testing

@SpringBootTest
public class OrderIntegrationTest {
    @Autowired
    private OrderService orderService;

    @Autowired
    private OrderRepository orderRepository;

    @Test
    @Transactional
    void whenCreateOrder_thenSavedInDatabase() {
        // Given
        OrderRequest request = createValidRequest();

        // When
        Order result = orderService.createOrder(request);

        // Then
        Optional<Order> savedOrder = 
            orderRepository.findById(result.getId());
        assertThat(savedOrder).isPresent();
        assertThat(savedOrder.get().getItems())
            .hasSameSizeAs(request.getItems());
    }
}

6. Code Review Guidelines

6.1 Review Checklist

  1. Code Style và Formatting
  2. Consistent indentation
  3. Proper spacing
  4. Line length limits
  5. Naming conventions

  6. Functionality

  7. Business logic correctness
  8. Edge cases handled
  9. Error handling
  10. Performance considerations

  11. Testing

  12. Unit tests coverage
  13. Integration tests
  14. Edge cases tested
  15. Mocking strategy

  16. Security

  17. Input validation
  18. Authentication/Authorization
  19. Data sanitization
  20. Secure communication

6.2 Review Comments

// Good comment
"Consider using Optional<Customer> return type to explicitly handle null cases"

// Bad comment
"This code is bad"

// Good comment
"This method might throw NullPointerException when customer is null. 
 Consider adding null check or using Optional"

// Bad comment
"Fix this"

7. Tools và Metrics

7.1 Static Analysis Tools

  • SonarQube
  • PMD
  • Checkstyle
  • FindBugs
  • SpotBugs

7.2 Code Coverage Tools

  • JaCoCo
  • Cobertura
  • EclEmma

7.3 Quality Metrics

<!-- pom.xml -->
<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>${jacoco.version}</version>
    <executions>
        <execution>
            <goals>
                <goal>prepare-agent</goal>
            </goals>
        </execution>
        <execution>
            <id>report</id>
            <phase>test</phase>
            <goals>
                <goal>report</goal>
            </goals>
        </execution>
        <execution>
            <id>check</id>
            <goals>
                <goal>check</goal>
            </goals>
            <configuration>
                <rules>
                    <rule>
                        <element>PACKAGE</element>
                        <limits>
                            <limit>
                                <counter>LINE</counter>
                                <value>COVEREDRATIO</value>
                                <minimum>0.80</minimum>
                            </limit>
                        </limits>
                    </rule>
                </rules>
            </configuration>
        </execution>
    </executions>
</plugin>

8. Continuous Improvement

8.1 Code Quality Gates

# sonar-project.properties
sonar.projectKey=my-project
sonar.projectName=My Project
sonar.projectVersion=1.0

sonar.sources=src/main/java
sonar.tests=src/test/java
sonar.java.binaries=target/classes
sonar.java.test.binaries=target/test-classes

sonar.coverage.jacoco.xmlReportPaths=target/site/jacoco/jacoco.xml
sonar.java.coveragePlugin=jacoco

# Quality Gates
sonar.qualitygate.wait=true
sonar.qualitygate=QualityGate
sonar.qualitygate.conditions=[\
  {"metric":"new_coverage","op":"LT","error":"80"},\
  {"metric":"new_duplicated_lines_density","op":"GT","error":"3"},\
  {"metric":"new_maintainability_rating","op":"GT","error":"1"},\
  {"metric":"new_reliability_rating","op":"GT","error":"1"},\
  {"metric":"new_security_rating","op":"GT","error":"1"}\
]

8.2 Automated Quality Checks

# .github/workflows/quality-check.yml
name: Code Quality Check

on: [push, pull_request]

jobs:
  quality:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2

    - name: Set up JDK
      uses: actions/setup-java@v2
      with:
        java-version: '17'

    - name: Build and Test
      run: mvn clean verify

    - name: SonarQube Analysis
      run: mvn sonar:sonar
      env:
        SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

    - name: Quality Gate Check
      run: |
        STATUS=$(curl -s -u ${{ secrets.SONAR_TOKEN }}: \
          "https://sonarcloud.io/api/qualitygates/project_status?projectKey=my-project" \
          | jq -r '.projectStatus.status')
        if [ "$STATUS" != "OK" ]; then
          echo "Quality Gate failed"
          exit 1
        fi

9. References và Further Reading

9.1 Books

  • Clean Code (Robert C. Martin)
  • Refactoring (Martin Fowler)
  • Effective Java (Joshua Bloch)
  • Test Driven Development (Kent Beck)
  • Working Effectively with Legacy Code (Michael Feathers)

9.2 Online Resources