Có bất kỳ điểm nào trong việc sử dụng các trình xây dựng và giao diện chất lỏng với các trình khởi tạo đối tượng không?


10

Trong Java và C #, bạn có thể tạo một đối tượng với các thuộc tính có thể được đặt khi khởi tạo bằng cách xác định hàm tạo với các tham số, xác định từng thuộc tính sau khi xây dựng đối tượng hoặc sử dụng mẫu giao diện của trình xây dựng / chất lỏng. Tuy nhiên, C # 3 đã giới thiệu bộ khởi tạo đối tượng và bộ sưu tập, có nghĩa là mẫu xây dựng phần lớn là vô dụng. Trong một ngôn ngữ không có trình khởi tạo, người ta có thể triển khai trình xây dựng sau đó sử dụng nó như sau:

Vehicle v = new Vehicle.Builder()
                    .manufacturer("Toyota")
                    .model("Camry")
                    .year(1997)
                    .colour(CarColours.Red)
                    .addSpecialFeature(new Feature.CDPlayer())
                    .addSpecialFeature(new Feature.SeatWarmer(4))
                    .build();

Ngược lại, trong C # người ta có thể viết:

var vehicle = new Vehicle {
                Manufacturer = "Toyota",
                Model = "Camry",
                Year = 1997,
                Colour = CarColours.Red,
                SpecialFeatures = new List<SpecialFeature> {
                    new Feature.CDPlayer(),
                    new Feature.SeatWarmer { Seats = 4 }
                }
              }

... Loại bỏ sự cần thiết của một người xây dựng như trong ví dụ trước.

Dựa trên những ví dụ này, các trình xây dựng có còn hữu ích trong C # hay chúng đã được thay thế hoàn toàn bởi các trình khởi tạo?


are builders usefulTại sao tôi cứ nhìn thấy những câu hỏi như vậy? Builder là một mẫu thiết kế - chắc chắn, nó có thể được hiển thị như một tính năng ngôn ngữ nhưng vào cuối ngày nó chỉ là một mẫu thiết kế. Một mẫu thiết kế không thể "sai". Nó có thể hoặc không thể đáp ứng trường hợp sử dụng của bạn nhưng điều đó không làm cho toàn bộ mẫu bị sai, chỉ là ứng dụng của nó. Hãy nhớ rằng vào cuối ngày, các mẫu thiết kế sẽ giải quyết các vấn đề cụ thể - nếu bạn không gặp phải vấn đề này, vậy tại sao bạn lại thử giải quyết nó?
VLAZ

@vlaz Tôi không nói các nhà xây dựng là sai - thực tế câu hỏi của tôi là hỏi liệu có trường hợp sử dụng nào cho các nhà xây dựng được đưa ra là một triển khai trường hợp phổ biến nhất mà bạn sẽ sử dụng các nhà xây dựng không. Rõ ràng, mọi người đã trả lời rằng một người xây dựng vẫn hữu ích cho việc thiết lập các trường riêng tư, chính điều này đã trả lời câu hỏi của tôi.
svbnet

3
@vlaz Để làm rõ: Tôi đang nói rằng các trình khởi tạo đã thay thế phần lớn mô hình giao diện / trình xây dựng "truyền thống" của việc viết một lớp trình tạo bên trong, sau đó viết các phương thức setter chuỗi bên trong lớp đó tạo ra một thể hiện mới của lớp cha đó. Tôi không nói rằng các trình khởi tạo là một sự thay thế cho mẫu xây dựng cụ thể đó; Tôi đang nói rằng các trình khởi tạo lưu các nhà phát triển phải triển khai mẫu trình xây dựng cụ thể đó, lưu cho các trường hợp sử dụng được đề cập trong các câu trả lời, điều này không làm cho mẫu trình xây dựng trở nên vô dụng.
svbnet

5
@vlaz Tôi không hiểu mối quan tâm của bạn. "Bạn đã nói rằng một mẫu thiết kế không hữu ích" - không, Joe đã hỏi liệu một mẫu thiết kế có hữu ích không. Làm thế nào đó là một câu hỏi xấu, sai, hoặc sai? Bạn có nghĩ rằng câu trả lời là rõ ràng? Tôi không nghĩ câu trả lời là rõ ràng; đây có vẻ là một câu hỏi hay với tôi
Tanner Swett

2
@vlaz, các mẫu định vị đơn và dịch vụ đều sai. Ergo mẫu thiết kế có thể sai.
David Arno

Câu trả lời:


12

Như được chạm vào bởi @ user248215, vấn đề thực sự là sự bất biến. Lý do bạn sẽ sử dụng một trình xây dựng trong C # sẽ là để duy trì sự chứng minh của trình khởi tạo mà không phải để lộ các thuộc tính có thể thiết lập được. Đó không phải là một câu hỏi về đóng gói mà đó là lý do tại sao tôi đã viết câu trả lời của riêng mình. Đóng gói là khá trực giao vì việc gọi một setter không ngụ ý những gì setter thực sự làm hoặc ràng buộc bạn với việc thực hiện nó.

Phiên bản tiếp theo của C #, 8.0, có khả năng giới thiệu một withtừ khóa cho phép các đối tượng bất biến được khởi tạo rõ ràng và chính xác mà không cần phải viết trình xây dựng.

Một điều thú vị khác mà bạn có thể làm với các nhà xây dựng, trái ngược với các trình khởi tạo, là chúng có thể dẫn đến các loại đối tượng khác nhau tùy thuộc vào chuỗi các phương thức được gọi.

Ví dụ

value.Match()
    .Case((DateTime d) => Console.WriteLine($"{d: yyyy-mm-dd}"))
    .Case((double d) => Console.WriteLine(Math.Round(d, 4));
    // void

var str = value.Match()
    .Case((DateTime d) => $"{d: yyyy-mm-dd}")
    .Case((double d) => Math.Round(d, 4).ToString())
    .ResultOrDefault(string.Empty);
    // string

Chỉ cần làm rõ ví dụ ở trên, đó là một thư viện khớp mẫu sử dụng mẫu xây dựng để xây dựng một "khớp" bằng cách chỉ định các trường hợp. Các trường hợp được nối bằng cách gọi Casephương thức truyền cho nó một hàm. Nếu valueđược gán cho kiểu tham số của hàm thì nó được gọi. Bạn có thể tìm thấy mã nguồn đầy đủ trên GitHub và, vì các nhận xét XML khó đọc bằng văn bản thuần túy, đây là một liên kết đến tài liệu được xây dựng của SandCastle (xem phần Ghi chú )


Tôi không thấy làm thế nào điều này không thể được thực hiện một trình khởi tạo đối tượng, sử dụng một IEnumerable<Func<T, TResult>>thành viên.
Caleth

@Caleth Đó thực sự là thứ tôi đã thử nghiệm nhưng có một số vấn đề với cách tiếp cận đó. Nó không cho phép các mệnh đề có điều kiện (không được hiển thị nhưng được sử dụng và thể hiện trong tài liệu được liên kết) và nó không cho phép suy luận kiểu TResult. Cuối cùng, loại suy luận có rất nhiều điều để làm với nó. Tôi cũng muốn nó "trông" giống như một cấu trúc điều khiển. Ngoài ra tôi không muốn sử dụng trình khởi tạo vì điều đó sẽ cho phép đột biến.
Aluan Haddad

12

Các trình khởi tạo đối tượng yêu cầu các thuộc tính phải được truy cập bằng mã gọi. Các nhà xây dựng lồng nhau có thể truy cập các thành viên tư nhân của lớp.

Nếu bạn muốn làm cho Vehiclebất biến (thông qua việc đặt tất cả các setters thành riêng tư), thì trình xây dựng lồng nhau có thể được sử dụng để đặt các biến riêng tư.


0

Tất cả đều phục vụ các mục đích khác nhau !!!

Các nhà xây dựng có thể khởi tạo các trường được đánh dấu readonlycũng như các thành viên riêng tư và được bảo vệ. Tuy nhiên, bạn hơi bị hạn chế trong những gì bạn có thể làm trong một hàm tạo; bạn nên tránh chuyển thissang bất kỳ phương thức bên ngoài nào, ví dụ, và bạn nên tránh gọi các thành viên ảo, vì chúng có thể thực thi trong ngữ cảnh của lớp dẫn xuất chưa tự xây dựng. Ngoài ra, các hàm tạo được đảm bảo để chạy (trừ khi người gọi đang làm điều gì đó rất bất thường), vì vậy nếu bạn đặt các trường trong hàm tạo, mã trong phần còn lại của lớp có thể cho rằng các trường đó sẽ không có giá trị.

Khởi tạo chạy sau khi xây dựng. Họ chỉ cho phép bạn gọi tài sản công cộng; Các trường riêng tư và / hoặc chỉ đọc không thể được đặt. Theo quy ước, hành động thiết lập một tài sản nên khá hạn chế, ví dụ như nó là bình thường và có tác dụng phụ hạn chế.

Các phương thức của Builder là các phương thức thực tế và do đó cho phép nhiều hơn một đối số và theo quy ước có thể có các tác dụng phụ bao gồm cả việc tạo đối tượng. Mặc dù họ không thể thiết lập các trường chỉ đọc, nhưng họ có thể làm bất cứ điều gì khác. Ngoài ra, các phương thức có thể được triển khai như các phương thức mở rộng (giống như hầu hết tất cả các chức năng LINQ).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.