Khi Constructor có nhiều tham số, việc sử dụng pattern Builder để tăng khả năng đọc và độ ổn định là hiệu quả, và nó linh hoạt hơn trong việc tạo đối tượng so với Constructor phân cấp hoặc pattern Java Beans.
Pattern Builder hoạt động bằng cách tạo đối tượng Builder với các tham số bắt buộc, thiết lập các tham số tùy chọn thông qua các phương thức setter, và trả về đối tượng cuối cùng thông qua phương thức build().
Khi sử dụng cùng với thiết kế lớp phân cấp, nó hỗ trợ liên kết phương thức mà không cần ép kiểu trong các lớp con, do đó tạo ra việc tạo đối tượng linh hoạt và hiệu quả hơn.
Mô hình Builder theo cấp bậc
Cả hàm tạo tĩnh và hàm tạo đều khó xử lý khi có quá nhiều tham số. Ví dụ, nếu lớp có 6 trường và bạn muốn tạo hàm tạo khi có 2 tham số, 3 tham số,... thì có thể sử dụng mô hình Builder theo cấp bậc như sau.
Tuy nhiên, ngay cả khi sử dụng cách này, nếu số lượng tham số tăng lên, việc đọc mã sẽ trở nên khó khăn hơn khi không biết ý nghĩa của từng giá trị và có thể nhầm lẫn giữa các tham số có cùng kiểu dữ liệu.
Mô hình JavaBeans
Mô hình JavaBeans tạo ra đối tượng bằng hàm tạo không tham số, sau đó gọi các phương thức setter để thiết lập giá trị cho các tham số mong muốn.
Mô hình JavaBeans cho phép tạo đối tượng mà không cần lo lắng về việc nhầm lẫn các tham số khi số lượng tham số tăng lên. Tuy nhiên, để tạo ra một đối tượng, bạn cần gọi nhiều phương thức setter, và tính nhất quán của đối tượng có thể bị phá vỡ trước khi nó được hoàn thiện. Do đó, lớp không thể được thiết kế là bất biến.
Mô hình Builder
Mô hình Builder thường được sử dụng vì nó kết hợp sự ổn định của mô hình Builder theo cấp bậc và khả năng đọc của mô hình JavaBeans.
Thay vì tự tạo đối tượng, khách hàng sẽ gọi hàm tạo với chỉ các tham số bắt buộc để lấy đối tượng Builder. Sau đó, khách hàng sẽ sử dụng các phương thức setter (một dạng) do đối tượng Builder cung cấp để thiết lập các tham số tùy chọn. Cuối cùng, khách hàng sẽ gọi phương thức build() không tham số để lấy đối tượng cần thiết.
Lớp Builder có hàm tạo nhận các tham số bắt buộc, và các tham số tùy chọn khác được thiết lập bằng các phương thức setter. Cuối cùng, phương thức build() được gọi để tạo ra đối tượng NutritionFactsWithBuilderPattern hoàn chỉnh. Lớp NutritionFactsWithBuilderPattern là bất biến, và các phương thức setter của Builder trả về chính Builder, cho phép chúng được gọi liên tiếp. Cách này được gọi là API trôi chảy hoặc phương thức liên tiếp.
Đối với khách hàng, mô hình Builder giúp viết và đọc mã dễ dàng hơn.
Mô hình Builder phù hợp với các lớp được thiết kế theo phân cấp
Lớp Pizza.Builder là kiểu dữ liệu chung sử dụng giới hạn kiểu đệ quy, và thêm phương thức trừu tượng self() để các lớp con có thể hỗ trợ phương thức liên tiếp mà không cần ép kiểu. Trong các lớp con, chỉ cần trả về chính nó cho phương thức trừu tượng này.
Bây giờ, hãy xem xét Pizza New York và Pizza Calzone, các lớp con của Pizza, để trải nghiệm sự linh hoạt của mô hình Builder.
Hàm tạo build() được định nghĩa trong lớp Builder của mỗi lớp con trả về lớp con cụ thể. Khả năng các phương thức trong lớp con trả về kiểu con của kiểu được trả về bởi phương thức trong lớp cha được gọi là ép kiểu trả về đồng biến. Nhờ tính năng này, khách hàng không cần ép kiểu.
Khách hàng có thể kết hợp các enum của Pizza và các enum của mỗi lớp con, và có thể hoàn thiện đối tượng bằng các phương thức phù hợp tương ứng.
Nhược điểm của mô hình Builder
Cần tạo đối tượng Builder.
Mã nguồn dài dòng.
Tóm tắt
Nếu hàm tạo hoặc phương thức tạo tĩnh phải xử lý quá nhiều tham số, hãy cân nhắc sử dụng mô hình Builder.