- vừa được xem lúc

Object Calisthenics in Java

0 0 2

Người đăng: Tran Nhan

Theo Viblo Asia

Object Calisthenics?

Object Calisthenics là một tập hợp các nguyên tắc nhằm cải thiện chất lượng code và thúc đẩy tư duy hướng đối tượng (OOP). Nó được giới thiệu bởi Jeff Bay trong cuốn sách "The ThoughtWorks Anthology".

pros and cons

Lợi ích của Object Calisthenics

  • Tăng tính rõ ràng: Code dễ đọc, dễ hiểu và dễ bảo trì.
  • Hướng đến OOP tốt hơn: Áp dụng đúng tư duy hướng đối tượng, giảm lạm dụng kiểu nguyên thủy và getter/setter.
  • Giảm lỗi: Giảm thiểu các lỗi do code phức tạp hoặc không rõ ràng.

Hạn chế

  • Khó áp dụng với dự án cũ: Yêu cầu nhiều thời gian và công sức để refactor.
  • Dễ over-engineering: Nếu áp dụng không phù hợp, code có thể trở nên quá phức tạp.

9 Nguyên tắc

1. Chỉ sử dụng một cấp độ indentation mỗi phương thức

  • Nội dung: mỗi phương thức chỉ nên làm một nhiệm vụ cụ thể.
  • lợi ích: dễ đọc, dễ kiểm tra logic, giảm phức tạp.
  • ví dụ:
public class Grid { public String displayGrid(char[][][] data) { StringBuilder buffer = new StringBuilder(); // level 0 for (int i = 0; i < 10; i++) { // level 1 for (int j = 0; j < 10; j++) { // level 2 buffer.append(data[i][j]); } buffer.append("\\n"); } return buffer.toString(); }
}
  • cải thiện
public class Grid { public String displayGrid(char[][][] data) { StringBuilder buffer = new StringBuilder(); collectRows(buffer,data); return buffer.toString(); } private void collectRows(StringBuilder buffer, char[][][] data) { for (int i = 0; i < 10; i++) { collectRow(buffer, data, i); } } private void collectRow(StringBuilder buffer, char[][][] data, int row) { for (int i = 0; i < 10; i++) { buffer.append(data[row][i]); } buffer.append("\\n"); }
}

2. Không dùng từ khóa else

  • Nội dung: ưu tiên viết code với logic rõ ràng, giảm nesting.
  • ví dụ:
if (isValid(data)) { process(data);
} else { handleError();
}
  • sửa lại:
if (!isValid(data)) { handleError(); return;
}
process(data);

3. Wrapper các kiểu nguyên thủy và Strings.

  • Nội dung: nên gói các kiểu nguyên thủy (primitive) trong các đối tượng, để tránh Primitive Obsession anti-pattern.

If the variable of your primitive type has a behavior, you MUST encapsulate it!

  • ví dụ:
public class Runner { private int meter = 1; private int kilometer = meter * 1000;
}
  • cải thiện:
class Distance { private int value; private String unit; Distance(int value, String unit) { this.value = value; this.unit = unit; } public Distance toKilometer() { return new Distance(this.value * 1000, "kilometer"); }
} // runner mới
public class Runner { private Distance meter = new Distance(1, "meter"); private Distance kilometer = meter.toKilometer();
}

4. First Class Collection

  • Nội dung: dùng một lớp dùng để gói một collection như List, Set, Map và sử lý tất cả các hành vi liên quan của nó giúp tăng tính đóng gói,tập trung sử lý logic của collection tại một chỗ, tăng tính sử dụng code.
  • ví dụ:
public class Classroom { private List<Student> students; public Classroom() { this.students = new ArrayList<>(); } public void addStudent(Student student) { this.students.add(student); } public boolean hasStudent(String name) { return students.stream() .anyMatch(student -> student.getName().equalsIgnoreCase(name)); } public List<Student> getStudents() { return students; // Không an toàn, cho phép bên ngoài chỉnh sửa trực tiếp }
  • Nhận xét:
  1. students được lấy qua getStudents(), dễ thay đổi trạng thái ngoài ý muốn.

  2. Sử lý login liên quan đến student nằm rải rác, không tập trung (ví dụ tìm, thêm).

  3. Khó mở rộng khi cần thêm chức năng mới.

  • Cải tiến bằng First Class Collection:
// tạo một lớp Students để đại diện cho danh sách sinh viên lớp này gói List<Sutdent> và 
// sử lý toàn bộ hành vi liên quan.
// Lớp Students (First Class Collection)
public class Students { private final List<Student> students; public Students() { this.students = new ArrayList<>(); } public void add(Student student) { if (student == null) { throw new IllegalArgumentException("Student cannot be null"); } this.students.add(student); } public boolean contains(String name) { return students.stream() .anyMatch(student -> student.getName().equalsIgnoreCase(name)); } public int size() { return this.students.size(); } public List<Student> asList() { return new ArrayList<>(this.students); // Trả về bản sao an toàn }
}
// Lớp Classroom sử dụng Students:
public class Classroom { private final Students students; public Classroom() { this.students = new Students(); } public void addStudent(Student student) { students.add(student); } public boolean hasStudent(String name) { return students.contains(name); } public int totalStudents() { return students.size(); }
} // Lớp Student:
public class Student { private final String name; public Student(String name) { if (name == null || name.isBlank()) { throw new IllegalArgumentException("Name cannot be null or empty"); } this.name = name; } public String getName() { return name; }
}

5. Dùng một . mỗi dòng

  • ý nghĩa: Khuyến kích làm rõ ràng khi gọi chain method . dựa trên một rule khá nổi tiếng là Law of Demeter . Bạn chỉ nên nói chuyện trực tiếp với bạn của bạn, đừng nên nói chuyện với người lạ. jf4.
  • ví dụ:
order.getCustomer().getAddress().getCity();
  • cải thiện:
Customer customer = order.getCustomer();
Address address = customer.getAddress();
String city = address.getCity();

6. Đừng viết tắt

  • nội dung: Không viết tắt và hãy dùng tên có ý nghĩa giống nguyên tắc đặt tên của code clean.

7. Giữ cho các entities nhỏ

  • Nội dung: Tùy bạn nhưng khuyên là mỗi class không nên quá dài, mỗi package không nên quá nhiều. ý tưởng là file lớn thì khó đọc, khác hiểu, khó bảo trì.

8. Không có lớp với nhiều hơn 2 biến instances

  • Nội dung mỗi lớp chỉ nên có ít instances để nó có thể phân chia trách nhiệm rõ ràng, giảm độ phức tạp, tăng tính đóng gói và dễ duy trì của mã nguồn.
  • ví dụ. (Trong thực tế thì nên tăng giới hạn lên 5 nha).
public class Book { private final String title; private final Author author; private final LocalDate publicationYear; private final String isbn; public Book(String title, Author author, LocalDate publicationYear, String isbn) { this.title = title; this.author = author; this.publicationYear = publicationYear; this.isbn = isbn; } }
class Author { private final String name; private final String bio; private final String birthDate; public Author(String name, String bio, String birthDate) { this.name = name; this.bio = bio; this.birthDate = birthDate; }
}
  • cải tiến:
public class Book { private final String title; private final Author author; public Book(String title, Author author) { this.title = title; this.author = author; } }
class Author { private final String name; private final String birthDate; public Author(String name, String birthDate) { this.name = name; this.birthDate = birthDate; } }

9. Không getter và không setter

  • Là nguyên tắc quan trọng nhất! Có thể sử dụng các hàm accessors để lấy trạng thái của một đối tượng, miễn là bạn không sử dụng kết quả để đưa ra quyết định bên ngoài đối tượng. Mọi quyết định hoàn toàn dựa trên trạng thái của một đối tượng phải được thực hiện bên trong chính đối tượng đó.
  • ví dụ
public class Wallet { private BigDecimal balance; public Wallet(BigDecimal balance) { this.balance = balance; } void setBalance(BigDecimal balance) { this.balance = balance; } public BigDecimal getBalance() { return balance; }
} // USAGE
wallet.setBalance(wallet.getBalance() + NEW_AMOUNT)
  • cải thiện: Wallet instance chịu trách nhiện xác định tăng giá trị lên.
public class Wallet { private BigDecimal balance; public Wallet(BigDecimal balance) { this.balance = balance; } void increaseMoney(BigDecimal balance) { this.balance.add(balance); } public BigDecimal totalAmount() { return balance; }
} // USAGE
wallet.increaseMoney(NEW_AMOUNT)

References

  1. Article and code example: https://medium.com/@kouomeukevin/as-a-java-developer-you-should-master-the-object-calisthenics-130213597d90

Bình luận

Bài viết tương tự

- vừa được xem lúc

4 đặc tính của lập trình hướng đối tượng (Object oriented program)

Lập trình hướng đối tượng quá quen thuộc rồi bạn nào học lập trình đều phải học, đi phỏng vấn cũng vậy hỏi suốt(chắc cái này tùy vào vị trí tuyển dụng chủ yếu junior chắc chắn sẽ hỏi).nó là nền tảng cho hầu hết các design pattern hiện nay.

0 0 46

- vừa được xem lúc

Khác nhau giữa abstract class và interface khi nào dùng chúng

Nhắc đến Interface và abstract class hãy nhớ 2 từ này khá clear rồi, Khi sử dụng Interface là bạn Implement còn sử dụng abstract class là bạn extend. . Interface:. .

0 0 41

- vừa được xem lúc

So sánh Interface và Abstract trong lập trình hướng đối tượng.

Tổng quan. Interface và Abstract class là 2 khái niệm cơ bản trong lập trình OOP.

0 0 63

- vừa được xem lúc

Áp Dụng Nguyên Tắc SOLID Trong Lập Trình

Giới Thiệu. 1. SOLID là gì. SOLID là viết tắt của 5 chữ cái đầu trong 5 nguyên tắc:.

0 0 36

- vừa được xem lúc

Kỹ thuật giải quyết bài toàn về policy và công thức tính toán động cho sản phẩm phần mềm

Dạo này tôi có một mối duyên rất tình cờ với việc làm các phần mềm thuộc lĩnh vực tài chính và ngân hàng. Một số bài toán trong lĩnh vực này làm tôi nhớ đến những ngày đầu làm việc với phần mềm Trinet

0 0 34

- vừa được xem lúc

Object Relational Mapping

Trong cách phát triển ứng dụng web hiện nay chắc hẳn các bạn đã quen với với từ khóa ORM(Object Relational Mapping). Khi mà thời đại của các framework ứng với các ngôn ngữ đang lên ngôi một cách mạnh

0 0 39