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
- Code Style và Formatting
- Consistent indentation
- Proper spacing
- Line length limits
-
Naming conventions
-
Functionality
- Business logic correctness
- Edge cases handled
- Error handling
-
Performance considerations
-
Testing
- Unit tests coverage
- Integration tests
- Edge cases tested
-
Mocking strategy
-
Security
- Input validation
- Authentication/Authorization
- Data sanitization
- 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)