문제

일부는 무엇입니까 흔한, 실제 예제 빌더 패턴 사용의? 당신에게 무엇을 사십니까? 공장 패턴 만 사용하지 않는 이유는 무엇입니까?

도움이 되었습니까?

해결책

건축업자와 공장 IMHO의 주요 차이점은 물체를 만들기 위해 많은 일을해야 할 때 건축업자가 유용하다는 것입니다. 예를 들어 Dom을 상상해보십시오. 최종 객체를 얻으려면 많은 노드와 속성을 만들어야합니다. 공장은 공장이 하나의 메소드 호출 내에서 전체 객체를 쉽게 만들 수있을 때 사용됩니다.

빌더를 사용하는 한 가지 예는 XML 문서 빌딩입니다. 예를 들어 HTML 조각을 작성할 때이 모델을 사용했습니다. 예를 들어 특정 유형의 테이블을 구축하기위한 빌더가있을 수 있으며 다음 방법이있을 수 있습니다. (매개 변수가 표시되지 않음):

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

이 건축업자는 나를 위해 HTML을 뱉어 냈습니다. 이것은 큰 절차 방법을 걷는 것보다 읽기가 훨씬 쉽습니다.

체크 아웃 위키 백과의 빌더 패턴.

다른 팁

아래는 Java에서 패턴 및 예제 코드를 사용한다고 주장하는 몇 가지 이유이지만, 4 인치의 갱단이 다루는 빌더 패턴의 구현입니다. 디자인 패턴. Java에서 사용하는 이유는 다른 프로그래밍 언어에도 적용됩니다.

Joshua Bloch가 말한 것처럼 효과적인 Java, 2 판:

빌더 패턴은 생성자 나 정적 공장이 소수의 매개 변수 이상을 가질 수있는 클래스를 설계 할 때 좋은 선택입니다.

우리는 어느 시점에서 각 추가가 새로운 옵션 매개 변수를 추가하는 생성자 목록과 함께 클래스를 만났습니다.

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

이를 텔레 스코핑 생성자 패턴이라고합니다. 이 패턴의 문제점은 일단 생성자가 4 또는 5 매개 변수가 길이라는 것입니다. 기억하기 어렵습니다 필요한 매개 변수의 순서 주어진 상황에서 원하는 특정 생성자.

하나 대안 텔레 스코핑 생성자 패턴은 Javabean 패턴 필수 매개 변수가있는 생성자를 호출 한 다음 다음에 선택 사항 세터를 호출합니다.

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

여기서 문제는 객체가 여러 호출에 걸쳐 생성되기 때문에 건설을 통해 일관되지 않은 상태 일 수 있다는 것입니다. 또한 스레드 안전을 보장하기 위해 많은 노력이 필요합니다.

더 나은 대안은 빌더 패턴을 사용하는 것입니다.

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;
  }
}

주목하십시오 피자는 불변이며 매개 변수 값은 모두 단일 위치에 있습니다.. 빌더의 세터 방법은 빌더 객체를 반환하기 때문에 묶을 수 있습니다.

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

이로 인해 쓰기가 쉽고 읽기 쉬운 코드가 발생합니다. 이 예에서 빌드 방법을 수정할 수 있습니다 빌더에서 피자 개체로 복사 한 후 매개 변수를 확인합니다. 유효하지 않은 매개 변수 값이 제공된 경우 불법 상태 외환을 던지십시오. 이 패턴은 유연하며 향후 더 많은 매개 변수를 추가 할 수 있습니다. 생성자에 대해 4 ~ 5 개 이상의 매개 변수를 갖는 경우에만 유용합니다. 즉, 처음에는 가치가있을 수 있습니다. 앞으로 더 많은 매개 변수를 추가 할 수 있다고 생각되면

나는이 주제에 대해 책에서 크게 빌렸다 효과적인 Java, 2 판 Joshua Bloch. 이 패턴 및 기타 효과적인 Java 관행에 대해 자세히 알아 보려면 나는 그것을 강력히 추천합니다.

식당을 고려하십시오. "오늘의 식사"의 창조는 공장 패턴입니다. 주방에게 "Get Me Me Today Meal"을 알려주고 주방 (공장)은 숨겨진 기준에 따라 어떤 물건을 생성 해야하는지 결정하기 때문입니다.

맞춤형 피자를 주문하면 빌더가 나타납니다. 이 경우 웨이터는 요리사 (Builder)에게 "피자가 필요합니다. 치즈, 양파 및 베이컨을 추가하십시오!" 따라서 빌더는 생성 된 객체에 있어야하는 속성을 노출 시키지만 설정 방법을 숨 깁니다.

.NET StringBuilder 클래스는 빌더 패턴의 훌륭한 예입니다. 주로 일련의 단계에서 문자열을 만드는 데 사용됩니다. ToString ()을 수행 한 최종 결과는 항상 문자열이지만 해당 문자열의 생성은 StringBuilder 클래스의 기능에 따라 다릅니다. 요약하면, 기본 아이디어는 복잡한 개체를 구축하고 구축 방법에 대한 구현 세부 사항을 숨기는 것입니다.

멀티 스레드 문제의 경우 각 스레드마다 복잡한 개체가 필요했습니다. 객체는 처리중인 데이터를 나타내며 사용자 입력에 따라 변경 될 수 있습니다.

대신 공장을 사용할 수 있습니까? 예

왜 우리는하지 않았습니까? 빌더는 내가 추측하는 것이 더 의미가 있습니다.

공장은 동일한 기본 유형 (동일한 인터페이스 또는 기본 클래스 구현) 인 다양한 유형의 객체를 만드는 데 사용됩니다.

빌더는 동일한 유형의 객체를 반복해서 구축하지만 구조는 동적이므로 런타임에 변경할 수 있습니다.

Microsoft MVC 프레임 워크를 살펴 보는 동안 빌더 패턴에 대한 생각을 받았습니다. 컨트롤러 빌더 클래스에서 패턴을 발견했습니다. 이 클래스는 컨트롤러 팩토리 클래스를 반환 한 다음 콘크리트 컨트롤러를 구축하는 데 사용됩니다.

빌더 패턴을 사용하는 데있어 보는 장점은 자신의 공장을 만들어 프레임 워크에 연결할 수 있다는 것입니다.

@Tetha, 피자를 제공하는 이탈리아 사람이 운영하는 식당 (프레임 워크)이있을 수 있습니다. 피자 이탈리아 사람 (객체 빌더)은 피자베이스 (기본 클래스)와 함께 Owen (공장)을 사용합니다.

이제 Indian Guy는 이탈리아 남자로부터 식당을 인수합니다. 피자 대신 인디언 레스토랑 (프레임 워크) 서버 DOSA. Dosa Indian Guy (Object Builder)를 준비하기 위해 Maida (기본 클래스)와 함께 프라이팬 (공장)을 사용합니다.

시나리오를 보면 음식이 다르고 음식이 준비되는 방식은 다르지만 같은 식당에서 (같은 틀 아래). 식당은 중국, 멕시코 또는 요리를 지원할 수있는 방식으로 건축해야합니다. 객체 빌더 내부 프레임 워크는 원하는 요리를 플러그인 종류로 촉진합니다. 예를 들어

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;
   }
}

처리 할 옵션이 많을 때 사용합니다. Jmock과 같은 것들에 대해 생각해보십시오.

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

훨씬 더 자연스럽고 가능합니다.

XML 빌딩, 스트링 빌딩 및 기타 많은 것들도 있습니다. IF를 상상해보십시오 java.util.Map 건축업자로 넣었습니다. 당신은 다음과 같은 일을 할 수 있습니다 :

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

이전 답변 (Pun 의도)을 바탕으로 훌륭한 실제 예는 다음과 같습니다. 그루비지원을 지원합니다 Builders.

보다 건축업자 에서 그루비 문서

건축업자의 또 다른 장점은 공장이 있다면 여전히 코드에 약간의 커플 링이 있다는 것입니다. 공장이 작동하려면 생성 할 수있는 모든 개체를 알아야합니다.. 생성 할 수있는 다른 객체를 추가하면 공장 클래스를 수정하여 그를 포함시켜야합니다. 이것은 추상 공장에서도 발생합니다.

반면에 건축업자를 사용하면이 새로운 클래스를위한 새로운 콘크리트 빌더를 만들면됩니다. 디렉터 클래스는 생성자의 빌더를 수신하기 때문에 동일하게 유지됩니다.

또한 건축업자의 풍미가 많이 있습니다. 카미카제 용병은 또 다른 것을 준다.

/// <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);
    }
}

나는 항상 건축업자 패턴을 다루기 힘들고 눈에 띄지 않으며 덜 숙련 된 프로그래머에 의해 종종 학대를 당했다고 싫어했습니다. 필요한 데이터에서 객체를 조립 해야하는 경우에만 의미가있는 패턴입니다. 초기화 후 단계 (즉, 모든 데이터가 수집되면 - 그것으로 무언가를하십시오). 대신, 99%의 시간에 건축업자는 단순히 클래스 멤버를 초기화하는 데 사용됩니다.

그러한 경우 단순히 선언하는 것이 훨씬 낫습니다. withXyz(...) 클래스 내부에 세터를 입력하고 그 자체에 대한 참조를 반환합니다.

이걸 고려하세요:

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");

이제 우리는 자체 초기화를 관리하고 훨씬 더 우아함을 제외하고는 자체 초기화를 관리하고 건축업자와 거의 같은 작업을 수행하는 깔끔한 단일 클래스를 보유하고 있습니다.

나는 집에서 재배 한 메시징 라이브러리에서 Builder를 사용했습니다. 라이브러리 코어는 와이어로부터 데이터를 수신하여 빌더 인스턴스를 사용하여 수집 한 다음 Builder가 메시지 인스턴스를 작성하는 데 필요한 모든 것을 얻었을 때 Builder.getMessage ()가 철사.

XML에 표준 XMLGregorianCalendar를 사용하여 Java에서 Datetime의 마샬링을 반대하기 위해 XML을 사용하고 싶었을 때, 나는 그것을 사용하는 것이 얼마나 무거운 무게와 번거로운 지에 대한 많은 의견을 들었습니다. XS : DateTime Structs에서 XML 필드를 컴트로하려고 노력했습니다. 시간대, 밀리 초 등을 관리했습니다.

그래서 GregorianCalendar 또는 Java.util.Date에서 XMLGregorian 캘린더를 구축하기위한 유틸리티를 설계했습니다.

내가 일하는 곳 때문에 합법적 인없이 온라인으로 공유 할 수는 없지만 다음은 고객이 어떻게 사용하는지에 대한 예입니다. 세부 사항을 추상화하고 XS : DateTime에 덜 사용되는 XMLGregorianCalendar의 일부 구현을 필터링합니다.

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

이 패턴은 xmlcalendar의 필드를 정의되지 않은 것으로 설정하여 제외되므로 여전히 "빌드 된"필터입니다. 빌더에 다른 옵션을 쉽게 추가하여 XS : DATE : XS : TIME STRUCT 및 필요할 때 시간대 오프셋을 조작하기 위해 쉽게 추가했습니다.

XMLGregorianCalendar를 생성하고 사용하는 코드를 본 적이 있다면 어떻게 조작하기가 훨씬 쉬워 졌는지 알 수 있습니다.

효과적인 Java에 설명 된대로 내부 빌더 클래스를 생성하는 Generate Menu (Alt+Insert)에 '빌더'동작을 추가하는 Intellij 아이디어 플러그인 인 Intellij 아이디어 플러그인 인 InnerBuilder를 확인하십시오.

https://github.com/analytically/innerbuilder

유닛이 클래스를 테스트 할 때 사용하는 것입니다. SUT (테스트중인 시스템) 빌더를 사용합니다.

예시:

수업:

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;

}

테스트:

    [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);
    }

sut builder :

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();
    }
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top