Giới thiệu
Viết phương thức (method) trong Java thì dễ nhưng viết phương thức tốt (ngắn gọn, rõ ràng, mạnh mẽ) là một nghệ thuật.
Phương thức sạch sẽ:
- Giúp mã dễ đọc hơn
- Giúp mã dễ kiểm thử hơn
- Giúp toàn bộ dự án dễ bảo trì hơn
1. Giữ phương thức ngắn gọn (Ưu tiên dưới 15 dòng)
Vấn đề:
Phương thức dài thì khó hiểu và khó gỡ lỗi hơn.
Giải pháp:
- Tập trung mỗi phương thức chỉ làm một việc nhỏ
- Nếu phương thức dài ra, hãy tách thành các phương thức phụ riêng tư
Bad:
public void checkoutOrder(Order order) {
if (order == null) {
throw new IllegalArgumentException("Order cannot be null");
}
if (order.getItems() == null || order.getItems().isEmpty()) {
throw new IllegalArgumentException("Order must have at least one item");
}
if (order.getTotalAmount() <= 0) {
throw new IllegalArgumentException("Order total must be greater than zero");
}
paymentService.processPayment(order.getPaymentDetails());
inventoryService.updateStock(order.getItems());
shippingService.prepareShipment(order);
notificationService.sendOrderConfirmation(order.getUserEmail(), order.getOrderId());
}
Good:
public void checkoutOrder(Order order) {
validateOrder(order);
processPaymentAndInventory(order);
finalizeShippingAndNotify(order);
}
private void validateOrder(Order order) {
if (order == null) {
throw new IllegalArgumentException("Order cannot be null");
}
if (order.getItems() == null || order.getItems().isEmpty()) {
throw new IllegalArgumentException("Order must have at least one item");
}
if (order.getTotalAmount() <= 0) {
throw new IllegalArgumentException("Order total must be greater than zero");
}
}
private void processPaymentAndInventory(Order order) {
paymentService.processPayment(order.getPaymentDetails());
inventoryService.updateStock(order.getItems());
}
private void finalizeShippingAndNotify(Order order) {
shippingService.prepareShipment(order);
notificationService.sendOrderConfirmation(order.getUserEmail(), order.getOrderId());
}
- Mỗi phương thức giờ chỉ làm một việc đơn giản
- Dễ đọc hơn, dễ kiểm thử hơn, dễ tái sử dụng hơn
2. Đặt tên phương thức rõ ràng (Động từ + Danh từ)
Vấn đề:
Tên phương thức không rõ ràng khiến người đọc phải đoán phương thức đó thực hiện điều gì.
Giải pháp:
- Dùng từ thể hiện hành động
- Tên phương thức nên là Động từ + Danh từ (ví dụ: sendEmail(), calculateSalary(), updateStock())
Bad:
public void email(User user) { }
public void stuff(List<Item> items) { }
Good:
public void sendConfirmationEmail(User user) { }
public void updateInventoryStock(List<Item> items) { }
- Tên rõ ràng giúp người khác hiểu ngay – kể cả bạn sau này!
3. Giới hạn số lượng tham số (Tốt nhất ≤ 3)
Quá nhiều tham số sẽ:
- Khó đọc
- Dễ nhầm lẫn thứ tự
- Khó bảo trì khi thay đổi yêu cầu
Giải pháp:
- Ưu tiên 2–3 tham số tối đa
- Nếu cần nhiều hơn, hãy nhóm lại thành một đối tượng
- Điều này giữ cho mã của bạn sạch sẽ, an toàn và có thể mở rộng khi dự án phát triển.
Bad:
public void registerUser(String firstName,
String lastName,
String email,
String password,
int age,
String country,
String phoneNumber) {
// Registration logic
}
Good:
public class UserRegistrationRequest {
private String firstName;
private String lastName;
private String email;
private String password;
private int age;
private String country;
private String phoneNumber;
// Constructors, Getters, Setters
}
public void registerUser(UserRegistrationRequest request) {
// Access request.getFirstName(), request.getEmail(), etc.
}
Lợi ích:
- Phương pháp giữ sạch và ngắn – chỉ một tham số.
- Thêm các trường mới (ví dụ: mã giới thiệu) sẽ không phá vỡ chữ ký phương thức.
- Gõ mạnh hơn – Trình biên dịch giúp đảm bảo các giá trị chính xác.
- Dễ dàng hơn để truyền dữ liệu giữa các lớp (Controller → Service → Repository).
4. Trả về kết quả thay vì thay đổi tham số
Bad:
public void capitalizeName(Customer customer) {
customer.setName(customer.getName().toUpperCase());
}
Good:
public Customer capitalizeCustomerName(Customer customer) {
Customer updatedCustomer = new Customer();
updatedCustomer.setId(customer.getId());
updatedCustomer.setEmail(customer.getEmail());
updatedCustomer.setName(customer.getName().toUpperCase());
return updatedCustomer;
}
Customer customer = customerService.findById(1L);
customer = capitalizeCustomerName(customer);
Lợi ịch:
- Nó rõ ràng: Chúng tôi chỉ định rõ ràng đối tượng sửa đổi được trả về.
- Không có sửa đổi im lặng đằng sau hậu trường.
- Dễ dàng hơn để kiểm tra, gỡ lỗi và lý do về.
5. Sử dụng kiểu trả về có ý nghĩa (Optional, List, DTO)
Vấn đề:
- Trả về null hoặc kiểu dữ liệu thô khiến mã:
- Kém rõ ràng
- Khó xử lý lỗi
- Dễ gây NullPointerException
Giải pháp:
- Sử dụng Optional<T> nếu dữ liệu có thể không tồn tại
- Dùng List<T>, Set<T>, thay vì mảng
- Dùng DTO để truyền dữ liệu có cấu trúc
Ví dụ 1: Sử dụng Optional thay vì return null
Bad:
public User findUserByEmail(String email) {
User user = database.findByEmail(email);
if (user != null) {
return user;
}
return null;
}
Good:
public Optional<User> findUserByEmail(String email) {
return Optional.ofNullable(database.findByEmail(email));
}
Ví dụ 2: Trả về List<T> thay thế Array
Bad:
public User[] getAllUsers() {
return database.findAllUsers();
}
Good:
public List<User> getAllUsers() {
return database.findAllUsers();
}
Ví dụ 3: Trả về DTO thay vì Raw Entity
Bad:
public Order getOrderDetails(Long orderId) {
return orderRepository.findById(orderId);
}
Good:
public OrderSummaryDTO getOrderDetails(Long orderId) {
Order order = orderRepository.findById(orderId);
return new OrderSummaryDTO(order.getId(), order.getTotalAmount(), order.getStatus());
}
public class OrderSummaryDTO {
private Long id;
private double totalAmount;
private String status;
public OrderSummaryDTO(Long id, double totalAmount, String status) {
this.id = id;
this.totalAmount = totalAmount;
this.status = status;
}
// Getters
}
- Bạn kiểm soát chính xác những gì bạn phơi bày.
- Hợp đồng API sạch hơn.
- An toàn hơn và hiệu quả hơn.
Ví dụ 4: Trả về mặc định thay vì null
Bad:
public List<Product> getProductsByCategory(String category) {
List<Product> products = database.findByCategory(category);
return products == null ? null : products;
}
Good:
public List<Product> getProductsByCategory(String category) {
List<Product> products = database.findByCategory(category);
return products != null ? products : Collections.emptyList();
}
- Tránh phải kiểm tra null mỗi lần sử dụng
6. Xử lý ngoại lệ đúng cách trong phương thức
Vấn đề:
Để ngoại lệ thô (raw exceptions) tự động lan truyền khắp nơi trong ứng dụng khiến mã của bạn trở nên:
-
Dễ vỡ (fragile)
-
Khó gỡ lỗi
-
Gây khó chịu cho người dùng (hiển thị thông báo lỗi khó hiểu, không thân thiện)
-
Không an toàn cho API và các hệ thống gọi tới (client)
Giải pháp:
- Bắt các ngoại lệ cụ thể ở nơi có thể xử lý được.
- Nếu không xử lý được, hãy gói (wrap) chúng lại trong một ngoại lệ tùy chỉnh có ý nghĩa và ném lại (rethrow).
- Không bao giờ để rò rỉ ngoại lệ kỹ thuật (ví dụ: lỗi SQL, lỗi IO) trực tiếp ra các tầng bên trên.
Bad:
public User findUserByEmail(String email) {
return database.findUser(email); // May throw SQLException directly!
}
Vấn đề:
-
Nếu cơ sở dữ liệu gặp lỗi, phương thức sẽ ném ra các ngoại lệ như
SQLException
một cách ngẫu nhiên. -
Không có cơ chế xử lý lỗi rõ ràng hay thân thiện.
Good:
public User findUserByEmail(String email) {
try {
return userRepository.findByEmail(email);
} catch (DatabaseConnectionException ex) {
throw new ServiceException("Failed to retrieve user by email", ex);
}
}
public class ServiceException extends RuntimeException {
public ServiceException(String message, Throwable cause) {
super(message, cause);
}
}
Tại sao cách này tốt hơn?
- Tầng bên ngoài chỉ nhìn thấy
ServiceException
— nhất quán và dễ hiểu hơn. - Công cụ log hoặc giám sát vẫn có thể truy vết nguyên nhân gốc bên trong (qua
cause
). - Giữ API sạch sẽ, an toàn, và dễ kiểm soát.
Mẹo:
Chỉ nên bắt ngoại lệ ở nơi bạn có thể xử lý hợp lý. Nếu không, hãy gói lại trong một ngoại lệ rõ nghĩa rồi ném lại (rethrow) để xử lý ở tầng cao hơn.
7. Ghi Chú Hành Vi Phương Thức Bằng JavaDoc (Khi Cần Thiết)
Vấn đề:
Nếu bạn không ghi chú đầy đủ cho các phương thức public, sẽ dẫn đến:
-
Lập trình viên mới mất thời gian để hiểu chức năng của phương thức
-
Hành vi mong đợi trở thành suy đoán
-
Quá trình review code trở nên chậm và mệt mỏi hơn
Giải pháp:
Viết JavaDoc ngắn gọn cho các phương thức public, đặc biệt khi:
-
Hành vi của phương thức không hoàn toàn rõ ràng
-
Tham số đầu vào hoặc giá trị trả về có quy tắc đặc biệt
-
Có thể ném ra ngoại lệ
-
Có các trường hợp đặc biệt cần lưu ý
Bad:
public Order applyDiscount(Order order) {
if (order.getTotalAmount() > 1000) {
order.setDiscount(10);
}
return order;
}
Lập trình viên mới sẽ không biết rõ:
-
Ngưỡng giá trị nào sẽ được áp dụng giảm giá?
-
Mức giảm giá là bao nhiêu phần trăm?
Good:
/**
* Applies a 10% discount if the order total exceeds 1000 units.
*
* @param order the order to which the discount should be applied
* @return the updated order with discount applied, if applicable
* @throws IllegalArgumentException if the order is null
*/
public Order applyDiscount(Order order) {
if (order == null) {
throw new IllegalArgumentException("Order cannot be null");
}
if (order.getTotalAmount() > 1000) {
order.setDiscount(10);
}
return order;
}
Tại sao cách này tốt hơn?
– Bất kỳ lập trình viên nào đọc phương thức đều hiểu ngay:
-
Khi nào giảm giá được áp dụng
-
Mức giảm là bao nhiêu
-
Ngoại lệ nào có thể bị ném ra
– Không có điều gì bị ẩn hoặc phải đoán
– Quá trình onboard trở nên suôn sẻ hơn, giảm hiểu lầm và sai sót
Lời kết
– Viết các phương thức ngắn gọn, rõ ràng và mạnh mẽ không chỉ là vấn đề của code sạch mà còn là nền tảng để xây dựng hệ thống bền vững, dễ mở rộng và dễ bảo trì theo thời gian.
– Hãy tập trung vào:
-
Sự rõ ràng
-
Nguyên tắc một nhiệm vụ duy nhất (single-responsibility)
-
Những cải tiến nhỏ nhưng thông minh
-Chính bạn trong tương lai — và đồng đội của bạn — sẽ biết ơn vì điều đó.
💡 Lập trình viên giỏi viết ra mã có thể hoạt động.
🌟 Lập trình viên xuất sắc viết ra phương thức sạch, đơn giản và đầy sức mạnh.
Reference:
https://www.javaguides.net/2025/04/how-to-write-better-java-methods.html
Be First to Comment