Khi nào bạn sẽ sử dụng Mẫu Builder? [đóng cửa]


531

Một số là gì phổ biến , ví dụ thế giới thực của việc sử dụng các mẫu Builder? Nó mua gì cho bạn? Tại sao không sử dụng Mô hình nhà máy?


Các stackoverflow.com/questions/35238292/... đề cập một số API rằng việc sử dụng xây dựng mô hình
Alireza Fattahi

Câu trả lời từ AaronTetha thực sự có nhiều thông tin. Dưới đây là toàn bộ bài viết liên quan đến những câu trả lời.
Diablo

Câu trả lời:


262

Sự khác biệt chính giữa nhà xây dựng và nhà máy IMHO, là công cụ xây dựng rất hữu ích khi bạn cần làm nhiều việc để xây dựng một đối tượng. Ví dụ, hãy tưởng tượng một DOM. Bạn phải tạo nhiều nút và thuộc tính để có được đối tượng cuối cùng. Một nhà máy được sử dụng khi nhà máy có thể dễ dàng tạo toàn bộ đối tượng trong một cuộc gọi phương thức.

Một ví dụ về việc sử dụng trình xây dựng là xây dựng tài liệu XML, tôi đã sử dụng mô hình này khi xây dựng các đoạn HTML, ví dụ tôi có thể có Trình tạo để xây dựng một loại bảng cụ thể và nó có thể có các phương thức sau (tham số không được hiển thị) :

BuildOrderHeaderRow()
BuildLineItemSubHeaderRow()
BuildOrderRow()
BuildLineItemSubRow()

Trình xây dựng này sau đó sẽ nhổ HTML cho tôi. Điều này dễ đọc hơn nhiều so với việc đi qua một phương pháp thủ tục lớn.

Kiểm tra Builder Model trên Wikipedia .


1021

Dưới đây là một số lý do để tranh luận về việc sử dụng mẫu và mã ví dụ trong Java, nhưng đó là một triển khai của Mẫu xây dựng được bao phủ bởi Gang of Four trong các mẫu thiết kế . Những lý do bạn sẽ sử dụng nó trong Java cũng có thể áp dụng cho các ngôn ngữ lập trình khác.

Như Joshua Bloch tuyên bố trong Java hiệu quả, Phiên bản 2 :

Mẫu trình xây dựng là một lựa chọn tốt khi thiết kế các lớp có các nhà xây dựng hoặc nhà máy tĩnh sẽ có nhiều hơn một số tham số.

Tại một số thời điểm, tất cả chúng ta đã gặp một lớp với một danh sách các hàm tạo trong đó mỗi phép cộng thêm một tham số tùy chọn mới:

Pizza(int size) { ... }        
Pizza(int size, boolean cheese) { ... }    
Pizza(int size, boolean cheese, boolean pepperoni) { ... }    
Pizza(int size, boolean cheese, boolean pepperoni, boolean bacon) { ... }

Đây được gọi là Mô hình xây dựng kính thiên văn. Vấn đề với mẫu này là một khi các hàm tạo có 4 hoặc 5 tham số dài, sẽ khó nhớ thứ tự cần thiết của các tham số cũng như hàm tạo cụ thể mà bạn có thể muốn trong một tình huống cụ thể.

Một lựa chọn khác mà bạn có đối với Mẫu Trình xây dựng Kính thiên văn là Mẫu JavaBean nơi bạn gọi một hàm tạo với các tham số bắt buộc và sau đó gọi bất kỳ setters tùy chọn nào sau:

Pizza pizza = new Pizza(12);
pizza.setCheese(true);
pizza.setPepperoni(true);
pizza.setBacon(true);

Vấn đề ở đây là bởi vì đối tượng được tạo ra qua nhiều cuộc gọi nên nó có thể ở trạng thái không nhất quán giữa quá trình xây dựng. Điều này cũng đòi hỏi rất nhiều nỗ lực để đảm bảo an toàn chủ đề.

Thay thế tốt hơn là sử dụng Mô hình Builder.

public class Pizza {
  private int size;
  private boolean cheese;
  private boolean pepperoni;
  private boolean bacon;

  public static class Builder {
    //required
    private final int size;

    //optional
    private boolean cheese = false;
    private boolean pepperoni = false;
    private boolean bacon = false;

    public Builder(int size) {
      this.size = size;
    }

    public Builder cheese(boolean value) {
      cheese = value;
      return this;
    }

    public Builder pepperoni(boolean value) {
      pepperoni = value;
      return this;
    }

    public Builder bacon(boolean value) {
      bacon = value;
      return this;
    }

    public Pizza build() {
      return new Pizza(this);
    }
  }

  private Pizza(Builder builder) {
    size = builder.size;
    cheese = builder.cheese;
    pepperoni = builder.pepperoni;
    bacon = builder.bacon;
  }
}

Lưu ý rằng Pizza là bất biến và các giá trị tham số đều nằm trong một vị trí . Bởi vì các phương thức setter của Trình xây dựng trả về đối tượng Builder, chúng có thể được nối kết .

Pizza pizza = new Pizza.Builder(12)
                       .cheese(true)
                       .pepperoni(true)
                       .bacon(true)
                       .build();

Điều này dẫn đến mã dễ viết và rất dễ đọc và dễ hiểu. Trong ví dụ này, phương thức xây dựng có thể được sửa đổi để kiểm tra các tham số sau khi chúng được sao chép từ trình tạo vào đối tượng Pizza và ném IllegalStateException nếu giá trị tham số không hợp lệ được cung cấp. Mẫu này rất linh hoạt và thật dễ dàng để thêm nhiều tham số cho nó trong tương lai. Nó thực sự chỉ hữu ích nếu bạn sẽ có nhiều hơn 4 hoặc 5 tham số cho một hàm tạo. Điều đó nói rằng, nó có thể có giá trị ở nơi đầu tiên nếu bạn nghi ngờ bạn có thể sẽ thêm nhiều tham số trong tương lai.

Tôi đã mượn rất nhiều về chủ đề này từ cuốn sách Java hiệu quả, tái bản lần thứ 2 của Joshua Bloch. Để tìm hiểu thêm về mẫu này và các thực tiễn Java hiệu quả khác, tôi đánh giá cao nó.


24
Khác với người xây dựng GOF ban đầu phải không? Vì không có lớp Giám đốc. Có vẻ như là một mô hình khác với tôi, nhưng tôi đồng ý rằng nó rất hữu ích.
Lino Rosa

194
Đối với ví dụ cụ thể này, sẽ không tốt hơn nếu xóa các tham số boolean và có thể nóinew Pizza.Builder(12).cheese().pepperoni().bacon().build();
Fabian Steeg

46
Điều này trông giống như một Giao diện Thông thạo hơn là mẫu xây dựng .
Robert Harvey

21
@Fabian Steeg, tôi nghĩ mọi người đang phản ứng thái quá với các setters boolean trông đẹp hơn, hãy nhớ rằng các loại setters này không cho phép thay đổi thời gian chạy : Pizza.Builder(12).cheese().pepperoni().bacon().build();, bạn sẽ cần biên dịch lại mã của mình hoặc có logic không cần thiết nếu bạn chỉ cần một số pepperoni pizza. Ít nhất, bạn cũng nên cung cấp các phiên bản tham số như @Kamikaze Mercenary đề xuất ban đầu. Pizza.Builder(12).cheese(true).pepperoni(false).bacon(false).build();. Sau đó, một lần nữa, chúng tôi không bao giờ kiểm tra đơn vị, phải không?
egallardo

23
@JasonC Phải, và dù sao thì công dụng của một chiếc bánh pizza là bất biến?
Maarten Bodewes

325

Hãy xem xét một nhà hàng. Việc tạo ra "bữa ăn hôm nay" là một mô hình nhà máy, bởi vì bạn nói với nhà bếp "hãy cho tôi bữa ăn hôm nay" và nhà bếp (nhà máy) quyết định đối tượng nào sẽ tạo ra, dựa trên các tiêu chí ẩn.

Trình xây dựng xuất hiện nếu bạn đặt một chiếc bánh pizza tùy chỉnh. Trong trường hợp này, người phục vụ nói với đầu bếp (người xây dựng) "Tôi cần một chiếc bánh pizza, thêm phô mai, hành tây và thịt xông khói vào nó!" Do đó, trình xây dựng hiển thị các thuộc tính mà đối tượng được tạo nên có, nhưng ẩn cách đặt chúng.


Nitin mở rộng sự tương tự nhà bếp trong một câu trả lời khác cho câu hỏi này .
Pops

19

Lớp .NET StringBuilder là một ví dụ tuyệt vời về mẫu trình xây dựng. Nó chủ yếu được sử dụng để tạo ra một chuỗi trong một loạt các bước. Kết quả cuối cùng bạn nhận được khi thực hiện ToString () luôn là một chuỗi nhưng việc tạo chuỗi đó thay đổi tùy theo chức năng nào trong lớp StringBuilder đã được sử dụng. Tóm lại, ý tưởng cơ bản là xây dựng các đối tượng phức tạp và ẩn các chi tiết triển khai về cách nó được xây dựng.


9
Tôi không nghĩ rằng đó là mô hình xây dựng. StringBuilder chỉ là một triển khai khác của một lớp mảng ký tự (tức là chuỗi), nhưng nó làm cho việc quản lý hiệu năng và bộ nhớ thành tài khoản, bởi vì các chuỗi là bất biến.
Charles Graham

23
Nó hoàn toàn là mẫu xây dựng, cũng như lớp StringBuilder trong Java. Lưu ý cách phương thức append () của cả hai lớp này trả về chính StringBuilder, để người ta có thể xâu chuỗi b.append(...).append(...)trước khi gọi toString(). Trích dẫn: infoq.com/articles/iternal-dsls-java
pohl

3
@pohl Ya Tôi không nghĩ đó thực sự là một mẫu xây dựng, tôi muốn nói rằng đó không chỉ là một giao diện trôi chảy.
Didier A.

"Lưu ý cách phương thức append () của cả hai lớp này trả về chính StringBuilder" đó không phải là mẫu Builder, đó chỉ là một giao diện trôi chảy. Nó thường chỉ là một Builder sẽ sử dụng giao diện trôi chảy. Một Builder không cần phải có giao diện lưu loát.
bytedev 10/03/2016

Nhưng lưu ý rằng StringBuilder về bản chất không được đồng bộ hóa, không giống như StringBuffer được đồng bộ hóa.
Alan Deep

11

Đối với một vấn đề đa luồng, chúng tôi cần một đối tượng phức tạp được xây dựng cho mỗi luồng. Đối tượng thể hiện dữ liệu đang được xử lý và có thể thay đổi tùy thuộc vào đầu vào của người dùng.

Chúng ta có thể sử dụng một nhà máy thay thế? Đúng

Tại sao chúng ta không? Builder có ý nghĩa hơn tôi đoán.

Các nhà máy được sử dụng để tạo các loại đối tượng khác nhau có cùng loại cơ bản (thực hiện cùng một giao diện hoặc lớp cơ sở).

Các nhà xây dựng xây dựng cùng một loại đối tượng nhiều lần, nhưng việc xây dựng là động nên có thể thay đổi khi chạy.


9

Bạn sử dụng nó khi bạn có nhiều lựa chọn để giải quyết. Hãy suy nghĩ về những thứ như jmock:

m.expects(once())
    .method("testMethod")
    .with(eq(1), eq(2))
    .returns("someResponse");

Nó cảm thấy tự nhiên hơn rất nhiều và ... có thể.

Ngoài ra còn có tòa nhà xml, xây dựng chuỗi và nhiều thứ khác. Hãy tưởng tượng nếu java.util.Mapđã đặt như một người xây dựng. Bạn có thể làm những thứ như thế này:

Map<String, Integer> m = new HashMap<String, Integer>()
    .put("a", 1)
    .put("b", 2)
    .put("c", 3);

3
Tôi đã quên đọc bản đồ "nếu" triển khai mẫu xây dựng và được cho là thấy cấu trúc ở đó .. :)
sethu

3
:) Xin lỗi vì điều đó. Nó là phổ biến trong nhiều ngôn ngữ để trở về bản thân thay vì khoảng trống. Bạn có thể làm điều đó trong java, nhưng nó không phổ biến lắm.
Dustin

7
Ví dụ bản đồ chỉ đơn giản là một ví dụ về chuỗi phương thức.
nogridbag

@nogridbag Nó thực sự sẽ gần hơn với phương pháp xếp tầng. Mặc dù nó sử dụng chuỗi theo cách mô phỏng xếp tầng, nhưng rõ ràng đó là chuỗi, nhưng về mặt ngữ nghĩa, nó hoạt động như xếp tầng.
Didier A.

9

Trong khi đi qua Microsoft MVC framework, tôi đã nghĩ về mẫu xây dựng. Tôi đã bắt gặp mô hình trong lớp ControllerBuilder. Lớp này là để trả về lớp nhà máy điều khiển, sau đó được sử dụng để xây dựng bộ điều khiển cụ thể.

Ưu điểm tôi thấy khi sử dụng mẫu xây dựng là, bạn có thể tạo một nhà máy của riêng mình và cắm nó vào khung.

@Tetha, có thể có một nhà hàng (Framework) được điều hành bởi anh chàng người Ý, phục vụ Pizza. Để chuẩn bị pizza, anh chàng người Ý (Object Builder) sử dụng Owen (Factory) với đế bánh pizza (lớp cơ sở).

Bây giờ anh chàng Ấn Độ tiếp quản nhà hàng từ anh chàng người Ý. Nhà hàng Ấn Độ (Framework) máy chủ dosa thay vì pizza. Để chuẩn bị anh chàng người Ấn Độ (người xây dựng đối tượng) sử dụng Frying Pan (Nhà máy) với Maida (lớp cơ sở)

Nếu bạn nhìn vào kịch bản, thực phẩm là khác nhau, cách thức chế biến thức ăn là khác nhau, nhưng trong cùng một nhà hàng (trong cùng một khuôn khổ). Nhà hàng nên được xây dựng theo cách nó có thể hỗ trợ Trung Quốc, Mexico hoặc bất kỳ món ăn nào. Trình xây dựng đối tượng bên trong khung tạo điều kiện cho loại plugin bạn muốn. ví dụ

class RestaurantObjectBuilder
{
   IFactory _factory = new DefaultFoodFactory();

   //This can be used when you want to plugin the 
   public void SetFoodFactory(IFactory customFactory)
   {
        _factory = customFactory;
   }

   public IFactory GetFoodFactory()
   {
      return _factory;
   }
}

7

Tôi luôn không thích mẫu Builder là một thứ gì đó khó sử dụng, gây khó chịu và rất thường bị lạm dụng bởi các lập trình viên ít kinh nghiệm. Đây là một mẫu chỉ có ý nghĩa nếu bạn cần lắp ráp đối tượng từ một số dữ liệu đòi hỏi bước khởi tạo sau (tức là một khi tất cả dữ liệu được thu thập - làm gì đó với nó). Thay vào đó, trong 99% các nhà xây dựng thời gian chỉ đơn giản được sử dụng để khởi tạo các thành viên của lớp.

Trong những trường hợp như vậy, tốt hơn hết là chỉ cần khai báo withXyz(...) kiểu setters bên trong lớp và làm cho chúng trả về một tham chiếu đến chính nó.

Xem xét điều này:

public class Complex {

    private String first;
    private String second;
    private String third;

    public String getFirst(){
       return first; 
    }

    public void setFirst(String first){
       this.first=first; 
    }

    ... 

    public Complex withFirst(String first){
       this.first=first;
       return this; 
    }

    public Complex withSecond(String second){
       this.second=second;
       return this; 
    }

    public Complex withThird(String third){
       this.third=third;
       return this; 
    }

}


Complex complex = new Complex()
     .withFirst("first value")
     .withSecond("second value")
     .withThird("third value");

Bây giờ chúng ta có một lớp duy nhất gọn gàng quản lý việc khởi tạo của riêng nó và thực hiện khá nhiều công việc tương tự như trình xây dựng, ngoại trừ việc nó thanh lịch hơn nhiều.


Tôi vừa quyết định tôi muốn xây dựng tài liệu XML phức tạp của mình dưới dạng JSON. Đầu tiên, làm thế nào để tôi biết rằng lớp 'Complex' có khả năng phân phối một sản phẩm XML ở nơi đầu tiên và làm cách nào để tôi thay đổi nó để tạo ra một đối tượng JSONable? Câu trả lời nhanh: Tôi không thể vì tôi cần sử dụng các nhà xây dựng. Và chúng ta đến vòng tròn đầy đủ ...
David Barker

1
Tổng số bs, Builder được thiết kế để xây dựng các vật thể bất biến và với khả năng thay đổi cách xây dựng trong tương lai mà không cần chạm vào lớp sản phẩm
Mickey Tin

6
hmmm Bạn đã đọc ở đâu đó trong câu trả lời tôi nói Builder được thiết kế để làm gì? Đây là một quan điểm thay thế cho câu hỏi ở đầu "Khi nào bạn sẽ sử dụng Mẫu xây dựng?", Dựa trên kinh nghiệm về vô số lạm dụng mẫu, trong đó công việc đơn giản hơn nhiều sẽ làm tốt hơn. Tất cả các mẫu đều hữu ích nếu bạn biết khi nào và làm thế nào để sử dụng chúng - đó là toàn bộ điểm của tài liệu về các mẫu ở vị trí đầu tiên! Khi mẫu bị lạm dụng hoặc tệ hơn - bị lạm dụng - thì sẽ trở thành một mẫu chống trong ngữ cảnh mã của bạn. Than ôi ...
Pavel Lechev

6

Một ưu điểm khác của trình xây dựng là nếu bạn có Factory, vẫn có một số khớp nối trong mã của bạn, bởi vì để Factory hoạt động, nó phải biết tất cả các đối tượng mà nó có thể tạo ra . Nếu bạn thêm một đối tượng khác có thể được tạo, bạn sẽ phải sửa đổi lớp nhà máy để bao gồm anh ta. Điều này cũng xảy ra trong Nhà máy Trừu tượng.

Mặt khác, với trình xây dựng, bạn chỉ cần tạo một trình xây dựng cụ thể mới cho lớp mới này. Lớp giám đốc sẽ giữ nguyên, bởi vì nó nhận được trình xây dựng trong hàm tạo.

Ngoài ra, có nhiều hương vị của người xây dựng. Kamikaze Mercenary `cho một cái khác.


6
/// <summary>
/// Builder
/// </summary>
public interface IWebRequestBuilder
{
    IWebRequestBuilder BuildHost(string host);

    IWebRequestBuilder BuildPort(int port);

    IWebRequestBuilder BuildPath(string path);

    IWebRequestBuilder BuildQuery(string query);

    IWebRequestBuilder BuildScheme(string scheme);

    IWebRequestBuilder BuildTimeout(int timeout);

    WebRequest Build();
}

/// <summary>
/// ConcreteBuilder #1
/// </summary>
public class HttpWebRequestBuilder : IWebRequestBuilder
{
    private string _host;

    private string _path = string.Empty;

    private string _query = string.Empty;

    private string _scheme = "http";

    private int _port = 80;

    private int _timeout = -1;

    public IWebRequestBuilder BuildHost(string host)
    {
        _host = host;
        return this;
    }

    public IWebRequestBuilder BuildPort(int port)
    {
        _port = port;
        return this;
    }

    public IWebRequestBuilder BuildPath(string path)
    {
        _path = path;
        return this;
    }

    public IWebRequestBuilder BuildQuery(string query)
    {
        _query = query;
        return this;
    }

    public IWebRequestBuilder BuildScheme(string scheme)
    {
        _scheme = scheme;
        return this;
    }

    public IWebRequestBuilder BuildTimeout(int timeout)
    {
        _timeout = timeout;
        return this;
    }

    protected virtual void BeforeBuild(HttpWebRequest httpWebRequest) {
    }

    public WebRequest Build()
    {
        var uri = _scheme + "://" + _host + ":" + _port + "/" + _path + "?" + _query;

        var httpWebRequest = WebRequest.CreateHttp(uri);

        httpWebRequest.Timeout = _timeout;

        BeforeBuild(httpWebRequest);

        return httpWebRequest;
    }
}

/// <summary>
/// ConcreteBuilder #2
/// </summary>
public class ProxyHttpWebRequestBuilder : HttpWebRequestBuilder
{
    private string _proxy = null;

    public ProxyHttpWebRequestBuilder(string proxy)
    {
        _proxy = proxy;
    }

    protected override void BeforeBuild(HttpWebRequest httpWebRequest)
    {
        httpWebRequest.Proxy = new WebProxy(_proxy);
    }
}

/// <summary>
/// Director
/// </summary>
public class SearchRequest
{

    private IWebRequestBuilder _requestBuilder;

    public SearchRequest(IWebRequestBuilder requestBuilder)
    {
        _requestBuilder = requestBuilder;
    }

    public WebRequest Construct(string searchQuery)
    {
        return _requestBuilder
        .BuildHost("ajax.googleapis.com")
        .BuildPort(80)
        .BuildPath("ajax/services/search/web")
        .BuildQuery("v=1.0&q=" + HttpUtility.UrlEncode(searchQuery))
        .BuildScheme("http")
        .BuildTimeout(-1)
        .Build();
    }

    public string GetResults(string searchQuery) {
        var request = Construct(searchQuery);
        var resp = request.GetResponse();

        using (StreamReader stream = new StreamReader(resp.GetResponseStream()))
        {
            return stream.ReadToEnd();
        }
    }
}

class Program
{
    /// <summary>
    /// Inside both requests the same SearchRequest.Construct(string) method is used.
    /// But finally different HttpWebRequest objects are built.
    /// </summary>
    static void Main(string[] args)
    {
        var request1 = new SearchRequest(new HttpWebRequestBuilder());
        var results1 = request1.GetResults("IBM");
        Console.WriteLine(results1);

        var request2 = new SearchRequest(new ProxyHttpWebRequestBuilder("localhost:80"));
        var results2 = request2.GetResults("IBM");
        Console.WriteLine(results2);
    }
}

1
Bạn có thể cải thiện câu trả lời của mình theo hai cách: 1) Làm cho nó SSCCE. 2) Giải thích làm thế nào điều này trả lời câu hỏi.
james.garriss


3

Tôi đã sử dụng trình xây dựng trong thư viện nhắn tin trồng tại nhà. Lõi thư viện đã nhận dữ liệu từ dây, thu thập nó bằng cá thể Builder, sau đó, khi Builder quyết định nó có mọi thứ cần thiết để tạo một cá thể Message, Builder.GetMessage () đang xây dựng một thể hiện thông báo bằng cách sử dụng dữ liệu được thu thập từ dây điện.



2

Khi tôi muốn sử dụng XMLGregorianCalWiki tiêu chuẩn cho XML của mình để phản đối việc sắp xếp DateTime trong Java, tôi đã nghe rất nhiều ý kiến ​​về việc sử dụng nó có trọng lượng nặng và cồng kềnh như thế nào. Tôi đã cố gắng kiểm tra các trường XML trong các chuỗi xs: datetime để quản lý múi giờ, mili giây, v.v.

Vì vậy, tôi đã thiết kế một tiện ích để xây dựng lịch XMLGregorian từ GregorianCalWiki hoặc java.util.Date.

Vì nơi tôi làm việc, tôi không được phép chia sẻ trực tuyến mà không hợp pháp, nhưng đây là một ví dụ về cách khách hàng sử dụng nó. Nó trừu tượng hóa các chi tiết và lọc một số việc triển khai XMLGregorianCalWiki ít được sử dụng cho xs: datetime.

XMLGregorianCalendarBuilder builder = XMLGregorianCalendarBuilder.newInstance(jdkDate);
XMLGregorianCalendar xmlCalendar = builder.excludeMillis().excludeOffset().build();

Cấp mẫu này là nhiều bộ lọc hơn vì nó đặt các trường trong xmlCalWiki là không xác định để chúng bị loại trừ, nó vẫn "xây dựng" nó. Tôi đã dễ dàng thêm các tùy chọn khác vào trình tạo để tạo xs: date và xs: time struct và cũng để thao tác bù đắp múi giờ khi cần.

Nếu bạn đã từng thấy mã tạo và sử dụng XMLGregorianCalWiki, bạn sẽ thấy cách này giúp thao tác dễ dàng hơn nhiều.


0

Một ví dụ tuyệt vời trong thế giới thực là sử dụng khi đơn vị kiểm tra các lớp của bạn. Bạn sử dụng các trình xây dựng sut (System Under Test).

Thí dụ:

Lớp học:

public class CustomAuthenticationService
{
    private ICloudService _cloudService;
    private IDatabaseService _databaseService;

    public CustomAuthenticationService(ICloudService cloudService, IDatabaseService databaseService)
    {
        _cloudService = cloudService;
        _databaseService = databaseService;
    }

    public bool IsAuthorized(User user)
    {            
        //Implementation Details
        return true;

}

Kiểm tra:

    [Test]
    public void Given_a_User_With_Permission_When_Verifying_If_Authorized_Then_Authorize_It_Returning_True()
    {
        CustomAuthenticationService sut = new CustomAuthenticationServiceBuilder();
        User userWithAuthorization = null;

        var result = sut.IsAuthorized(userWithAuthorization);

        Assert.That(result, Is.True);
    }

Người xây dựng sut:

public class CustomAuthenticationServiceBuilder
{
    private ICloudService _cloudService;
    private IDatabaseService _databaseService;

    public CustomAuthenticationServiceBuilder()
    {
        _cloudService = new AwsService();
        _databaseService = new SqlServerService();
    }

    public CustomAuthenticationServiceBuilder WithAzureService(AzureService azureService)
    {
        _cloudService = azureService;

        return this;
    }

    public CustomAuthenticationServiceBuilder WithOracleService(OracleService oracleService)
    {
        _databaseService = oracleService;

        return this;
    }

    public CustomAuthenticationService Build()
    {
        return new CustomAuthenticationService(_cloudService, _databaseService);
    }

    public static implicit operator CustomAuthenticationService (CustomAuthenticationServiceBuilder builder)
    {
        return builder.Build();
    }
}

Tại sao bạn cần một người xây dựng trong trường hợp này thay vì chỉ thêm setters vào CustomAuthenticationServicelớp của bạn ?
Laur Ivan

Đó là một câu hỏi hay @LaurIvan! Có thể ví dụ của tôi hơi kém, nhưng hãy tưởng tượng bạn không thể thay đổi lớp CustomAuthenticationService, trình xây dựng sẽ là một cách hấp dẫn để cải thiện việc đọc bài kiểm tra đơn vị của bạn. Và tôi nghĩ rằng việc tạo getters và setters sẽ phơi bày các lĩnh vực của bạn, wich sẽ được sử dụng chỉ cho các bài kiểm tra của bạn. Nếu bây giờ bạn muốn biết nhiều hơn về Trình xây dựng Kinh nghiệm, bạn có thể đọc về Trình tạo dữ liệu thử nghiệm , cũng tương tự như vậy, nhưng đối với các bài viết.
Rafael Miceli
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.