Tại sao chúng ta cần một lớp Builder khi triển khai mẫu Builder?


31

Tôi đã thấy nhiều triển khai của mẫu Builder (chủ yếu là trong Java). Tất cả chúng đều có một lớp thực thể (giả sử một Personlớp) và một lớp xây dựng PersonBuilder. Trình xây dựng "sắp xếp" một loạt các trường và trả về một new Personvới các đối số được truyền. Tại sao chúng ta rõ ràng cần một lớp xây dựng, thay vì đặt tất cả các phương thức xây dựng trong Personchính lớp đó?

Ví dụ:

class Person {

  private String name;
  private Integer age;

  public Person() {
  }

  Person withName(String name) {
    this.name = name;
    return this;
  }

  Person withAge(int age) {
    this.age = age;
    return this;
  }
}

Tôi chỉ có thể nói Person john = new Person().withName("John");

Tại sao cần một PersonBuilderlớp học?

Lợi ích duy nhất tôi thấy, là chúng ta có thể khai báo các Persontrường như final, do đó đảm bảo tính bất biến.


4
Lưu ý: Tên bình thường của mẫu này là chainable setters: D
Vịt Mooing

4
Nguyên tắc trách nhiệm duy nhất. Nếu bạn đặt logic trong lớp Người thì nó có nhiều việc phải làm
reggaeg Ức

1
Lợi ích của bạn có thể có một trong hai cách: Tôi có thể withNametrả lại một bản sao của Người chỉ với trường tên đã thay đổi. Nói cách khác, Person john = new Person().withName("John");có thể hoạt động ngay cả khi Personkhông thay đổi (và đây là mô hình phổ biến trong lập trình chức năng).
Brian McCutchon

9
@MooingDuck: một thuật ngữ phổ biến khác cho nó là giao diện lưu loát .
Mac

2
@Mac Tôi nghĩ giao diện trôi chảy nói chung chung hơn một chút - bạn tránh có voidphương pháp. Vì vậy, ví dụ, nếu Personcó một phương thức in tên của họ, bạn vẫn có thể xâu chuỗi nó với Giao diện trôi chảy person.setName("Alice").sayName().setName("Bob").sayName(). Nhân tiện, tôi chú thích những người trong JavaDoc với chính xác đề xuất của bạn @return Fluent interface- nó đủ chung chung và rõ ràng khi áp dụng cho bất kỳ phương thức nào thực hiện return thiskhi kết thúc thực thi và nó khá rõ ràng. Vì vậy, một Builder cũng sẽ làm một giao diện trôi chảy.
VLAZ

Câu trả lời:


27

Đó là vì vậy bạn có thể bất biếnmô phỏng các tham số được đặt tên cùng một lúc.

Person p = personBuilder
    .name("Arthur Dent")
    .age(42)
    .build()
;

Điều đó giữ cho găng tay của bạn tắt khỏi người cho đến khi trạng thái được đặt và sau khi được đặt, sẽ không cho phép bạn thay đổi, nhưng mọi trường đều được dán nhãn rõ ràng. Bạn không thể làm điều này chỉ với một lớp trong Java.

Có vẻ như bạn đang nói về Josh Blochs Builder Pattern . Điều này không nên nhầm lẫn với mô hình Gang of Four Builder . Đây là những con thú khác nhau. Cả hai đều giải quyết các vấn đề xây dựng, nhưng theo những cách khá khác nhau.

Tất nhiên bạn có thể xây dựng đối tượng của mình mà không cần sử dụng lớp khác. Nhưng sau đó bạn phải chọn. Bạn mất khả năng mô phỏng các tham số được đặt tên bằng các ngôn ngữ không có chúng (như Java) hoặc bạn mất khả năng duy trì bất biến trong suốt vòng đời của các đối tượng.

Ví dụ bất biến, không có tên cho tham số

Person p = new Person("Arthur Dent", 42);

Ở đây bạn đang xây dựng mọi thứ với một hàm tạo đơn giản. Điều này sẽ cho phép bạn không thay đổi nhưng bạn mất mô phỏng các tham số được đặt tên. Điều đó trở nên khó đọc với nhiều thông số. Máy tính không quan tâm nhưng nó khó với con người.

Ví dụ mô phỏng tham số được đặt tên với setters truyền thống. Không phải bất biến.

Person p = new Person();
p.name("Arthur Dent");
p.age(42);

Ở đây bạn đang xây dựng mọi thứ với setters và đang mô phỏng các tham số được đặt tên nhưng bạn không còn bất biến. Mỗi lần sử dụng một setter thay đổi trạng thái đối tượng.

Vì vậy, những gì bạn nhận được bằng cách thêm lớp là bạn có thể làm cả hai.

Xác nhận có thể được thực hiện trong trường build()hợp nếu lỗi thời gian chạy cho trường tuổi bị thiếu là đủ cho bạn. Bạn có thể nâng cấp nó và thực thi age()được gọi với lỗi trình biên dịch. Chỉ không với mô hình xây dựng Josh Bloch.

Cho rằng bạn cần một ngôn ngữ cụ thể miền nội bộ (iDSL).

Điều này cho phép bạn yêu cầu họ gọi age()name()trước khi gọi build(). Nhưng bạn không thể làm điều đó chỉ bằng cách quay lại thismỗi lần. Mỗi thứ trả về trả về một thứ khác nhau buộc bạn phải gọi thứ tiếp theo.

Sử dụng có thể trông như thế này:

Person p = personBuilder
    .name("Arthur Dent")
    .age(42)
    .build()
;

Nhưng điều này:

Person p = personBuilder
    .age(42)
    .build()
;

gây ra lỗi trình biên dịch vì age()chỉ hợp lệ để gọi loại được trả về bởi name().

Những iDSLs là cực kỳ mạnh mẽ ( JOOQ hoặc Java8 Streams ví dụ) và rất tốt đẹp để sử dụng, đặc biệt là nếu bạn sử dụng một IDE với hoàn thành mã, nhưng họ là một chút công bằng của công việc để thiết lập. Tôi khuyên bạn nên lưu chúng cho những thứ sẽ có một chút mã nguồn được viết chống lại chúng.


3
Và sau đó ai đó hỏi tại sao bạn phải cung cấp tên trước tuổi, và câu trả lời duy nhất là "vì vụ nổ tổ hợp". Tôi khá chắc chắn rằng người ta có thể tránh điều đó trong nguồn nếu một người có các mẫu thích hợp như C ++ hoặc tiếp tục sử dụng một loại khác cho mọi thứ.
Ded repeatator

1
Một sự trừu tượng bị rò rỉ đòi hỏi kiến ​​thức về sự phức tạp tiềm ẩn để có thể biết cách sử dụng nó. Đó là những gì tôi thấy ở đây. Bất kỳ lớp nào không biết cách tự xây dựng nhất thiết phải được kết hợp với Trình tạo có phụ thuộc hơn nữa (đó là mục đích và bản chất của khái niệm Trình tạo). Một lần (không phải ở trại ban nhạc), một nhu cầu đơn giản để khởi tạo một đối tượng cần 3 tháng để hoàn tác một frack cluster như vậy. Tôi không đùa bạn.
radarbob

Một trong những lợi thế của các lớp không biết bất kỳ quy tắc xây dựng nào của họ là khi các quy tắc đó thay đổi, họ không quan tâm. Tôi chỉ có thể thêm một AnonymousPersonBuilderbộ có quy tắc riêng.
candied_orange

Thiết kế lớp / hệ thống hiếm khi cho thấy ứng dụng sâu sắc của "tối đa hóa sự gắn kết và giảm thiểu khớp nối". Các quân đoàn của thiết kế và thời gian chạy có thể mở rộng và các lỗ hổng chỉ có thể sửa được nếu các câu châm ngôn và hướng dẫn OO được áp dụng với ý nghĩa là fractal, hoặc "đó là rùa, tất cả đều đi xuống".
radarbob

1
@radarbob Lớp đang được xây dựng vẫn biết cách tự xây dựng. Constructor của nó có trách nhiệm đảm bảo tính toàn vẹn của đối tượng. Lớp trình xây dựng chỉ đơn thuần là một trình trợ giúp cho hàm tạo, bởi vì hàm tạo thường lấy một số lượng lớn các tham số, điều này không tạo ra một API rất đẹp.
Không có

57

Tại sao sử dụng / cung cấp một lớp xây dựng:

  • Để tạo các đối tượng bất biến - lợi ích bạn đã xác định được. Hữu ích nếu việc xây dựng mất nhiều bước. FWIW, tính bất biến nên được xem là một công cụ quan trọng trong nhiệm vụ của chúng tôi để viết các chương trình không thể bảo trì và không có lỗi.
  • Nếu biểu diễn thời gian chạy của đối tượng cuối cùng (có thể là bất biến) được tối ưu hóa để đọc và / hoặc sử dụng không gian, nhưng không phải để cập nhật. String và StringBuilder là những ví dụ tốt ở đây. Các chuỗi liên kết lặp lại không hiệu quả lắm, do đó, StringBuilder sử dụng một biểu diễn bên trong khác để bổ sung - nhưng không tốt cho việc sử dụng không gian và không tốt cho việc đọc và sử dụng như lớp String thông thường.
  • Để phân tách rõ ràng các đối tượng được xây dựng từ các đối tượng đang xây dựng. Cách tiếp cận này đòi hỏi một sự chuyển đổi rõ ràng từ dưới xây dựng sang xây dựng. Đối với người tiêu dùng, không có cách nào để nhầm lẫn một đối tượng đang xây dựng với một đối tượng được xây dựng: hệ thống loại sẽ thực thi điều này. Điều đó có nghĩa là đôi khi chúng ta có thể sử dụng cách tiếp cận này để "rơi vào hố thành công", như đã từng, và khi tạo ra sự trừu tượng cho người khác (hoặc chính chúng ta) sử dụng (như API hoặc một lớp), điều này có thể rất tốt Điều.

4
Về điểm đạn cuối cùng. Vì vậy, ý tưởng là a PersonBuilderkhông có getters và cách duy nhất để kiểm tra các giá trị hiện tại là gọi .Build()để trả về a Person. Cách này .Build có thể xác nhận hợp lệ một đối tượng được xây dựng đúng không? Tức là cơ chế này nhằm ngăn chặn việc sử dụng một đối tượng "đang xây dựng"?
AaronLS

@AaronLS, vâng, chắc chắn; xác nhận phức tạp có thể được đưa vào một Buildhoạt động để ngăn chặn các đối tượng xấu và / hoặc được xây dựng dưới mức.
Erik Eidt

2
Bạn cũng có thể xác nhận đối tượng của mình trong hàm tạo của nó, tùy chọn có thể là cá nhân hoặc phụ thuộc vào độ phức tạp. Dù được chọn là gì, điểm chính là một khi bạn có đối tượng bất biến của mình, bạn biết nó được xây dựng đúng cách và không có giá trị bất ngờ. Một đối tượng bất biến với một trạng thái không chính xác không nên xảy ra.
Walfrat

2
@AaronLS một người xây dựng có thể có getters; đó không phải là vấn đề Không cần, nhưng nếu người xây dựng có getters, bạn phải lưu ý rằng việc gọi getAgengười xây dựng có thể quay lại nullkhi thuộc tính này chưa được chỉ định. Ngược lại, Personlớp có thể thực thi bất biến mà tuổi không bao giờ có thể xảy ra null, điều này là không thể khi xây dựng và đối tượng thực tế được trộn lẫn, như với new Person() .withName("John")ví dụ của OP , vui vẻ tạo ra Personkhông có tuổi. Điều này áp dụng ngay cả đối với các lớp có thể thay đổi, vì setters có thể thực thi bất biến, nhưng không thể thực thi các giá trị ban đầu.
Holger

1
@Holger điểm của bạn là đúng, nhưng chủ yếu hỗ trợ lập luận rằng Người xây dựng nên tránh getters.
dùng949300

21

Một lý do sẽ là để đảm bảo rằng tất cả các dữ liệu truyền vào tuân theo các quy tắc kinh doanh.

Ví dụ của bạn không xem xét điều này, nhưng hãy nói rằng ai đó đã chuyển qua một chuỗi trống hoặc một chuỗi bao gồm các ký tự đặc biệt. Bạn sẽ muốn thực hiện một số loại logic dựa trên việc đảm bảo rằng tên của họ thực sự là một tên hợp lệ (đây thực sự là một nhiệm vụ rất khó khăn).

Bạn có thể đặt tất cả trong lớp Person của mình, đặc biệt nếu logic rất nhỏ (ví dụ: chỉ cần đảm bảo rằng tuổi không âm) nhưng khi logic phát triển thì việc tách nó ra có ý nghĩa.


7
Ví dụ trong câu hỏi cung cấp một vi phạm quy tắc đơn giản: không có độ tuổi nào được đưa rajohn
Caleth

6
+1 Và rất khó để thực hiện quy tắc cho hai trường đồng thời mà không có lớp trình tạo (các trường X + Y không thể lớn hơn 10).
Reginald Blue

7
@ReginaldBlue thậm chí còn khó hơn nếu chúng bị ràng buộc bởi "x + y chẵn". Điều kiện ban đầu của chúng tôi với (0,0) là OK, Trạng thái (1,1) cũng ổn, nhưng không có chuỗi cập nhật biến duy nhất nào giữ trạng thái hợp lệ và đưa chúng tôi từ (0,0) đến (1) , 1).
Michael Anderson

Tôi tin rằng đây là lợi thế lớn nhất. Tất cả những người khác là những người tốt bụng.
Giacomo Alzetta

5

Một góc khác nhau về điều này so với những gì tôi thấy trong các câu trả lời khác.

Cách withFootiếp cận ở đây có vấn đề vì chúng hoạt động giống như setters nhưng được định nghĩa theo cách làm cho nó xuất hiện lớp hỗ trợ tính bất biến. Trong các lớp Java, nếu một phương thức sửa đổi một thuộc tính, thì thông thường sẽ bắt đầu phương thức với 'set'. Tôi chưa bao giờ yêu nó như một tiêu chuẩn nhưng nếu bạn làm điều gì đó khác, điều đó sẽ khiến mọi người ngạc nhiên và điều đó không tốt. Có một cách khác mà bạn có thể hỗ trợ tính bất biến với API cơ bản bạn có ở đây. Ví dụ:

class Person {
  private final String name;
  private final Integer age;

  private Person(String name, String age) {
    this.name = name;
    this.age = age;
  }  

  public Person() {
    this.name = null;
    this.age = null;
  }

  Person withName(String name) {
    return new Person(name, this.age);
  }

  Person withAge(int age) {
    return new Person(this.name, age);
  }
}

Nó không cung cấp nhiều cách ngăn chặn các đối tượng được xây dựng một phần không đúng cách nhưng không ngăn thay đổi đối với bất kỳ đối tượng hiện có nào. Có lẽ thật ngớ ngẩn cho loại điều này (và JB Builder cũng vậy). Có bạn sẽ tạo ra nhiều đối tượng hơn nhưng điều này không tốn kém như bạn nghĩ.

Bạn sẽ hầu như thấy cách tiếp cận này được sử dụng với các cấu trúc dữ liệu đồng thời như CopyOnWriteArrayList . Và gợi ý này tại sao bất biến là quan trọng. Nếu bạn muốn làm cho chủ đề mã của mình an toàn, tính bất biến hầu như luôn luôn được xem xét. Trong Java, mỗi luồng được phép giữ bộ đệm cục bộ ở trạng thái biến. Để một luồng có thể thấy các thay đổi được thực hiện trong các luồng khác, một khối được đồng bộ hóa hoặc tính năng tương tranh khác cần được sử dụng. Bất kỳ trong số này sẽ thêm một số chi phí vào mã. Nhưng nếu các biến của bạn là cuối cùng, không có gì để làm. Giá trị sẽ luôn là giá trị được khởi tạo và do đó tất cả các luồng đều nhìn thấy cùng một thứ bất kể là gì.


2

Một lý do khác chưa được đề cập rõ ràng ở đây là build()phương pháp có thể xác minh rằng tất cả các trường là 'trường chứa các giá trị hợp lệ (được đặt trực tiếp hoặc xuất phát từ các giá trị khác của các trường khác), có thể là chế độ thất bại rất có thể điều đó sẽ xảy ra.

Một lợi ích khác là Personđối tượng của bạn sẽ có một cuộc sống đơn giản hơn và một tập hợp bất biến đơn giản. Bạn biết rằng một khi bạn có một Person p, bạn có một p.namevà hợp lệ p.age. Không có phương pháp nào của bạn phải được thiết kế để xử lý các tình huống như "tốt, nếu tuổi được đặt nhưng không đặt tên hoặc nếu tên được đặt nhưng không phải tuổi thì sao?" Điều này làm giảm sự phức tạp chung của lớp.


"[...] có thể xác minh rằng tất cả các trường được đặt [...]" Điểm của mẫu Trình tạo là bạn không phải tự đặt tất cả các trường và bạn có thể quay lại mặc định hợp lý. Nếu bạn vẫn cần đặt tất cả các trường, có lẽ bạn không nên sử dụng mẫu đó!
Vincent Savard

@VincentSavard Thật vậy, nhưng theo ước tính của tôi, đó là cách sử dụng phổ biến nhất. Để xác thực rằng một số bộ giá trị "cốt lõi" được đặt và thực hiện một số xử lý ưa thích xung quanh một số giá trị tùy chọn (đặt mặc định, lấy giá trị của chúng từ các giá trị khác, v.v.).
Alexander - Tái lập Monica

Thay vì nói 'xác minh rằng tất cả các trường được đặt', tôi sẽ thay đổi điều này thành 'xác minh rằng tất cả các trường có chứa các giá trị hợp lệ [đôi khi phụ thuộc vào các giá trị của các trường khác]' (nghĩa là một trường chỉ có thể cho phép một số giá trị nhất định tùy thuộc vào giá trị của một lĩnh vực khác). Điều này có thể được thực hiện trong mỗi setter, nhưng nó dễ dàng hơn cho ai đó để thiết lập đối tượng mà không có ngoại lệ được ném cho đến khi đối tượng được hoàn thành và sẵn sàng để được xây dựng.
yitzih

Hàm tạo của lớp đang được xây dựng chịu trách nhiệm cuối cùng về tính hợp lệ của các đối số của nó. Xác nhận trong trình xây dựng là dự phòng tốt nhất và có thể dễ dàng thoát khỏi sự đồng bộ với các yêu cầu của hàm tạo.
Không có

@TKK Thật vậy. Nhưng họ xác nhận những điều khác nhau. Trong nhiều trường hợp tôi đã sử dụng Nhà xây dựng, công việc của nhà xây dựng là xây dựng các đầu vào mà cuối cùng được trao cho nhà xây dựng. Ví dụ: trình xây dựng được cấu hình với a URI, a Filehoặc a FileInputStreamvà sử dụng bất cứ thứ gì được cung cấp để có được một lệnh FileInputStreammà cuối cùng đi vào lệnh gọi của hàm tạo như một đối số
Alexander - Tái lập lại

2

Một trình xây dựng cũng có thể được định nghĩa để trả về một giao diện hoặc một lớp trừu tượng. Bạn có thể sử dụng trình xây dựng để xác định đối tượng và trình xây dựng có thể xác định lớp con cụ thể nào sẽ trả về dựa trên những thuộc tính nào được đặt hoặc ví dụ chúng được đặt thành gì.


2

Mẫu xây dựng được sử dụng để xây dựng / tạo đối tượng từng bước bằng cách đặt các thuộc tính và khi tất cả các trường bắt buộc được đặt thì trả về đối tượng cuối cùng bằng phương thức xây dựng. Các đối tượng mới được tạo ra là bất biến. Điểm chính cần lưu ý ở đây là đối tượng chỉ được trả về khi phương thức xây dựng cuối cùng được gọi. Điều này bảo đảm rằng tất cả các thuộc tính được đặt thành đối tượng và do đó đối tượng không ở trạng thái không nhất quán khi được lớp xây dựng trả về.

Nếu chúng ta không sử dụng lớp trình xây dựng và trực tiếp đặt tất cả các phương thức của lớp trình xây dựng vào chính lớp Người, thì trước tiên chúng ta phải tạo Đối tượng và sau đó gọi các phương thức setter trên đối tượng đã tạo sẽ dẫn đến trạng thái không nhất quán của đối tượng giữa quá trình tạo của đối tượng và thiết lập các thuộc tính.

Do đó, bằng cách sử dụng lớp trình xây dựng (tức là một số thực thể bên ngoài không phải là lớp Người), chúng tôi đảm bảo rằng đối tượng sẽ không bao giờ ở trạng thái không nhất quán.


@DawidO bạn có quan điểm của tôi. Sự nhấn mạnh chính tôi đang cố gắng đặt ở đây là về trạng thái không nhất quán. Và đó là một trong những lợi thế chính của việc sử dụng mẫu Builder.
Shubham Bondarde

2

Sử dụng lại đối tượng xây dựng

Như những người khác đã đề cập, tính bất biến và xác minh logic kinh doanh của tất cả các trường để xác thực đối tượng là những lý do chính cho một đối tượng xây dựng riêng biệt.

Tuy nhiên khả năng sử dụng lại là một lợi ích khác. Nếu tôi muốn khởi tạo nhiều đối tượng rất giống nhau, tôi có thể thực hiện các thay đổi nhỏ cho đối tượng trình tạo và tiếp tục khởi tạo. Không cần phải tạo lại đối tượng xây dựng. Việc sử dụng lại này cho phép người xây dựng hoạt động như một khuôn mẫu để tạo ra nhiều đối tượng bất biến. Đó là một lợi ích nhỏ, nhưng nó có thể là một lợi ích.


1

Trên thực tế, bạn có thể có các phương thức xây dựng trên chính lớp của mình và vẫn có tính bất biến. Nó chỉ có nghĩa là các phương thức xây dựng sẽ trả về các đối tượng mới, thay vì sửa đổi đối tượng hiện có.

Điều này chỉ hoạt động nếu có một cách để lấy một đối tượng ban đầu (hợp lệ / hữu ích) (ví dụ từ một hàm tạo đặt tất cả các trường bắt buộc hoặc một phương thức xuất xưởng đặt các giá trị mặc định) và các phương thức xây dựng bổ sung sau đó trả về các đối tượng được sửa đổi trên cái hiện có. Các phương thức xây dựng đó cần đảm bảo bạn không nhận được các đối tượng không hợp lệ / không nhất quán trên đường đi.

Tất nhiên, điều này có nghĩa là bạn sẽ có rất nhiều đối tượng mới và bạn không nên làm điều này nếu các đối tượng của bạn đắt tiền để tạo.

Tôi đã sử dụng mã đó trong mã thử nghiệm để tạo các đối sánh Hamcrest cho một trong các đối tượng kinh doanh của mình. Tôi không nhớ lại mã chính xác, nhưng nó trông giống như thế này (được đơn giản hóa):

public class CustomerMatcher extends TypeSafeMatcher<Customer> {
    private final Matcher<? super String> nameMatcher;
    private final Matcher<? super LocalDate> birthdayMatcher;

    @Override
    protected boolean matchesSafely(Customer c) {
        return nameMatcher.matches(c.getName()) &&
               birthdayMatcher.matches(c.getBirthday());
    }

    private CustomerMatcher(Matcher<? super String> nameMatcher,
                            Matcher<? super LocalDate> birthdayMatcher) {
        this.nameMatcher = nameMatcher;
        this.birthdayMatcher = birthdayMatcher;
    }

    // builder methods from here on

    public static CustomerMatcher isCustomer() {
        // I could return a static instance here instead
        return new CustomerMatcher(Matchers.anything(), Matchers.anything());
    }

    public CustomerMatcher withBirthday(Matcher<? super LocalDate> birthdayMatcher) {
        return new CustomerMatcher(this.nameMatcher, birthdayMatcher);
    }

    public CustomerMatcher withName(Matcher<? super String> nameMatcher) {
        return new CustomerMatcher(nameMatcher, this.birthdayMatcher);
    }
}

Sau đó tôi sẽ sử dụng nó như thế này trong các thử nghiệm đơn vị của mình (với nhập khẩu tĩnh phù hợp):

assertThat(result, is(customer().withName(startsWith("Paŭlo"))));

1
Điều này đúng - và tôi nghĩ đó là một điểm rất quan trọng, nhưng nó không giải quyết được vấn đề cuối cùng - Nó không cho bạn cơ hội xác nhận rằng đối tượng ở trạng thái nhất quán khi nó được xây dựng. Sau khi được xây dựng, bạn sẽ cần một số mánh khóe như gọi trình xác nhận trước bất kỳ phương thức kinh doanh nào của bạn để đảm bảo người gọi thiết lập tất cả các phương thức (đó sẽ là một thực tiễn khủng khiếp). Tôi nghĩ thật tốt khi suy luận về loại điều này để xem tại sao chúng ta sử dụng mẫu Builder theo cách chúng ta làm.
Bill K

@BillK true - một đối tượng với, về cơ bản, các setters bất biến (trả lại một đối tượng mới mỗi lần) có nghĩa là mỗi đối tượng được trả về phải hợp lệ. Nếu bạn đang trả về các đối tượng không hợp lệ (ví dụ: người có namenhưng không age), thì khái niệm này không hoạt động. Chà, bạn thực sự có thể trả lại một cái gì đó giống như PartiallyBuiltPersontrong khi nó không hợp lệ nhưng có vẻ như là một hack để che giấu Builder.
VLAZ

@BillK đây là ý của tôi với "nếu có cách lấy đối tượng ban đầu (hợp lệ / hữu ích)" - Tôi giả sử rằng cả đối tượng ban đầu và đối tượng được trả về từ mỗi hàm xây dựng đều hợp lệ / nhất quán. Nhiệm vụ của bạn với tư cách là người tạo ra một lớp như vậy là chỉ cung cấp các phương thức xây dựng thực hiện những thay đổi nhất quán đó.
Paŭlo Ebermann
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.