Builder Pattern trong Java và triển khai với Filter Machine 🤙

Khi thực hành làm dự án và thiết kế hệ thống với các ngôn ngữ hướng đối tượng như Java, việc áp dụng các design pattern (thiết kế kiểu mẫu) để tối ưu hóa và kiến trúc dễ dàng hơn là rất cần thiết. Nhân tiện kỳ này mình học một môn tên là Software Architecture: Design & Implementation (Kiến trúc hệ thống: Thiết kế và Triển khai), mình sẽ giới thiệu tới một pattern mà mình đánh giá là "đỉnh của chóp" là Builder Pattern và cách mà mình áp dụng để thiết kế một Filter Machine (Máy lọc) cho bài tập ở trường.


Builder Pattern là gì? 🙄

Builder Pattern is a creational design pattern that separates the construction of a complex object from its representation so that the same construction process can create different representations.

Thì theo như định nghĩa ở trên tạm dịch ra là

Builder Patternmột mẫu thiết kế đối tượng thuộc nhóm khởi tạo được tạo ra để xây dựng một đôi tượng phức tạp bằng cách sử dụng các đối tượng đơn giản và sử dụng tiếp cận từng bước, việc xây dựng các đối tượng đôc lập với các đối tượng khác.

Nếu như các bạn chưa biết nhóm khởi tạo là như thế nào thì nhóm khởi tạo là một trong ba nhóm chính của design pattern gồm: Creational Pattern (nhóm khởi tạo), Structural Pattern (nhóm cấu trúc) và Behavioural Pattern (nhóm hành vi). Trong khuông khổ bài viết này, mình sẽ không giải thích cụ thể về cả 3 nhóm design pattern, nếu các bạn muốn tìm hiểu thêm thì có thể đọc qua bài viết trên Viblo.

Thì nôm na nhóm khởi tạo sẽ gồm các mẫu thiết kếđể khởi tạo ra các object và giấu đi các logic để tạo ra nó (encapsulation) thay vì khởi tạo trực tiếp thông qua method new. Điều này giúp việc khởi tạo Object trở nên linh hoạt hơn với những ngôn ngữ OOP nghiêm khắc về đầu ra - đầu vào như Java.


Ứng dụng Builder Pattern như thế nào?

Trước hết, để có thể ứng dụng builder pattern vào dự án thực tế thì chúng ta phải nắm được kiến trúc của builder pattern.

  • Product: đại diện cho đối tượng cần tạo, đối tượng này phức tạp, có nhiều thuộc tính.
  • Builder: là abstract class hoặc interface khai báo phương thức tạo đối tượng.
  • ConcreteBuilder: kế thừa Builder và cài đặt chi tiết cách tạo ra đối tượng. Nó sẽ xác định và nắm giữ các thể hiện mà nó tạo ra, đồng thời nó cũng cung cấp phương thức để trả các các thể hiện mà nó đã tạo ra trước đó.
  • Director/Client: là nơi sẽ gọi tới Builder để tạo ra đối tượng.

Giải thích đơn giản hơn thì abstract class/interface Builder sẽ khai báo các method cần được triển khai bên trong các class kế thừa. Còn method này sẽ trả về chính class Builder đễ quá trình khởi tạo tiếp tục được diễn ra. Bên trong class kế thừa sẽ có một hàm gọi là GetResult() hoặc là build() tương ứng với việc trả về kết quả cuối cùng của quá trình khởi tạo. Để giúp các bạn hình dung dễ hơn thì mình có ví dụ như sau.

Student.java

Student (Class): Là lớp chứa các thuộc tính như địa chỉ id và tên của học sinh - tượng trưng cho một lớp đối tượng học sinh

StudentBuilder.java

StudentBuilder (Interface): Chứa các phương thức cần thiết để khởi tạo nên một Builder

ConcreteStudentBuilder.java

ConcreteStudentBuilder (Class) implements StudentBuilder: Lớp triển khai interface StudentBuilder và thiết lập các trường của lớp Student trong quá trình khởi tạo.

Sau khi đã tạo ra các lớp và interface cần thiết như trên thì chúng ta sẽ thử khởi tạo một lớp Student bằng Builder Pattern như sau

Main.java

Như các bạn đã thấy ở trên thì lợi thế của việc sử dụng Builder Pattern đó là các giá trị đầu vào để khởi tạo nên lớp đối tượng Student không bị ràng buộc và linh hoạt hơn, từ đó chúng ta có thể kiểm soát dữ liệu cần được khởi tạo trong lớp đối tượng ấy một cách tốt hơn.


Filter Machine với Builder Pattern?

Filter Machine được mình gọi là máy lọc và thật ra thì mình cũng không tìm được một định nghĩa cụ thể nào cho cái từ mình tự nghĩ ra này 😁. Nôm na thì mục địch của Filter Machine sẽ giúp chúng ta lọc dữ liệu đầu ra một cách tốt hơn thay vì áp dụng các method lặp đi lặp lại. Filter Machine được lấy cảm hứng từ các ORM Active Record mà mình sử dụng qua. Nều các bạn muốn tìm hiểu thêm về ORM thì xem qua bài viết này của mình nhé

Góc Của Chung | “ORM” LÀ GÌ MÀ NHÀ NHÀ SỬ DỤNG?
Nếu bạn có kiến thức về ORM thì dễ dàng nhận thấy rằng hầu như các dự án ngày nay đều áp dụng các ORM framework vào sử dụng để khiến cho việc quản lý cơ sở dữ liệu và truy vấn trở nên dễ dàng hơn...Trong bài viết này, mình sẽ cùng các bạn tìm hiểu xem ORM là gì nhé

Đây là một ví dụ về ORM ActiveRecord và cách truy vấn, cụ thể là Sequelize

Mình áp dụng Builder Pattern để khởi tạo một Filter Machine trong một bài tập ở trường mà mình sẽ chia sẻ trong các dòng tiếp theo đây. Giả sử với lớp đối tượng Student ở trên, nhưng để truy vấn dữ liệu theo từng điều kiện khác nhau, theo cách truyền thống, sẽ cần phải triển khai các method cụ thể như:

Giả sử trường hợp chúng ta cần tìm một Student theo cả NameId thì chúng ta sẽ phải triển khai thêm một phương thức là findOneByNameAndId

Cách kiến trúc này có thể áp dụng với những lớp đối tượng không yêu cầu sự phức tạp nhưng khi cần truy vấn trên nhiều trường dữ liệu của lớp đối tượng hơn thì sẽ gặp lại sự trùng lặp không cần thiết trong mã nguồn. Vì vậy, mình sử dụng Filter Machine để lọc dữ liệu trong từng trường hợp nhất định.

Trước hết, mình cũng sẽ tạo một interface tên là StudentFilterMachine thay vì StudentBuilder như ở trên

Và theo như các bước thì mình cũng sẽ tạo một ConcreteStudentFilterMachine và implement interface vừa được khởi tạo

Vậy là đã hoàn tất khởi tạo lớp đối tượng FilterMachine, bây giờ thì chúng ta chỉ có việc sử dụng thôi

Như các bạn thấy trong hình trên thì việc triển khai và sử dụng hầu như giống hệt các Builder mình đã đưa ra ví dụ ở trên. Chúng ta vẫn áp dụng quy tắc chia nhỏ các đối tượng nhỏ hơn và tiếp cận theo từng bước, nhưng thay vì sử dụng để xây dựng một lớp đối tượng, mình sử dụng để lọc đối tượng cần thiết.

Lợi ích của việc này các bạn có thể thấy đó là, khi cần tìm kiếm các Student có id và tên nhất định, chúng ta không cần phải viết thêm một method cho việc này. Vì vậy khi lớp đối tượng có nhiều thuộc tính cần truy vấn hơn cũng tiết kiệm được hàng chục dòng code và tối ưu hóa các logic nền. Tuy nhiên, điểm yếu của cách tiếp cận này đó là độ phức tạp của thời gian, hiệu năng sẽ có thể tăng đáng kể vì việc lạm dụng các vòng lặp. Điều này cũng xảy ra tương tự với các ORM khi so sánh với việc truy vấn SQL truyền thống.


Và đó là những gì mình học và áp dụng qua Builder Pattern. Mình nghĩ đây là một design pattern rất cần thiết khi thiết kế hệ thống hướng đối tượng.

Góc Của Chung

Góc Của Chung