سؤال

ما هي بعض شائع, أمثلة من العالم الحقيقي لاستخدام نمط البناء؟ماذا يشتري لك؟لماذا لا تستخدم نمط المصنع فقط؟

هل كانت مفيدة؟

المحلول

والفرق الرئيسي بين البناء وIMHO المصنع، هو أن البناء هو مفيد عندما تحتاج إلى القيام بالكثير من الأشياء لبناء كائن. على سبيل المثال تخيل DOM. لديك لإنشاء الكثير من العقد والصفات للحصول على وجوه النهائي. ويستخدم المصنع عند مصنع يمكن بسهولة إنشاء الكائن بأكمله داخل استدعاء أسلوب واحد.

وأحد الأمثلة على استخدام باني عبارة عن مبنى وثيقة XML، لقد استعملت هذا النموذج عند بناء شظايا HTML على سبيل المثال أنا قد يكون منشئ لبناء نوع معين من الجدول، وأنه قد يكون الأساليب التالية <قوية> (لا تظهر المعلمات) : ل

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

وهذا البناء من شأنه ثم بصق HTML بالنسبة لي. هذا هو أسهل بكثير للقراءة من المشي من خلال طريقة إجرائية كبيرة.

نمط البناء في ويكيبيديا .

نصائح أخرى

فيما يلي بعض الأسباب التي تدعو إلى استخدام النموذج وكود المثال في Java، ولكنه تطبيق لنموذج Builder Pattern الذي تغطيه Gang of Four في أنماط التصميم.تنطبق الأسباب التي قد تدفعك لاستخدامها في Java أيضًا على لغات البرمجة الأخرى.

كما يقول جوشوا بلوخ في جافا الفعالة، الطبعة الثانية:

يعد نمط البناء خيارًا جيدًا عند تصميم الفئات التي تحتوي مُنشئاتها أو مصانعها الثابتة على أكثر من عدد قليل من المعلمات.

لقد واجهنا جميعًا في وقت ما فئة تحتوي على قائمة من المنشئات حيث تضيف كل إضافة معلمة خيار جديدة:

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 معلمات، يصبح الأمر كذلك من الصعب أن نتذكر المطلوب ترتيب المعلمات بالإضافة إلى المُنشئ المعين الذي قد تريده في موقف معين.

واحد بديل عليك أن تصغير نمط المنشئ هو نمط جافا بين حيث تقوم باستدعاء المُنشئ باستخدام المعلمات الإلزامية ثم الاتصال بأي محددات اختيارية بعد:

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

ينتج عن هذا رمز سهل الكتابة وسهل القراءة والفهم. في هذا المثال، يمكن تعديل طريقة البناء للتحقق من المعلمات بعد نسخها من المنشئ إلى كائن Pizza و قم بطرح IllegalStateException إذا تم توفير قيمة معلمة غير صالحة. هذا النمط مرن ومن السهل إضافة المزيد من المعلمات إليه في المستقبل.إنه مفيد حقًا فقط إذا كان لديك أكثر من 4 أو 5 معلمات للمنشئ.ومع ذلك، قد يكون من المفيد في المقام الأول إذا كنت تشك في أنك قد تقوم بإضافة المزيد من المعلمات في المستقبل.

لقد استعارت الكثير من هذا الموضوع من الكتاب جافا الفعالة، الطبعة الثانية بواسطة جوشوا بلوخ.لمعرفة المزيد حول هذا النمط وممارسات Java الفعالة الأخرى انا اوصي بشده به.

والنظر في المطعم. إنشاء "وجبة اليوم" هو نمط المصنع، لأنك تخبر المطبخ "يحصل لي وجبة اليوم" والمطبخ (المصنع) يقرر ما يعترض على توليد، استنادا إلى معايير المخفية.

وباني يبدو إذا كنت من أجل بيتزا المخصصة. في هذه الحالة، النادل يقول رئيس الطباخين (باني) "أحتاج إلى البيتزا!؛ إضافة الجبن والبصل ولحم الخنزير المقدد إليها" وهكذا، فإن البناء يعرض سمات يجب أن يكون الكائن ولدت، ولكن يخفي كيفية تحديدها.

و. NET فئة ب StringBuilder هو مثال عظيم على نمط البناء. غالبا ما يستخدم لإنشاء سلسلة في سلسلة من الخطوات. النتيجة النهائية تحصل على القيام ToString () هي دائما سلسلة وإنما خلق هذه السلسلة يختلف وفقا لما تم استخدام وظائف في فئة ب StringBuilder. وخلاصة القول، فإن الفكرة الأساسية هي لبناء الأجسام المعقدة وإخفاء تفاصيل تنفيذ الكيفية التي يتم بها بناؤه.

لمشكلة متعددة الخيوط، كنا بحاجة إلى كائن معقد إلى أن تراكمت لكل موضوع. الكائن تمثيل البيانات التي يتم معالجتها، ويمكن أن تتغير تبعا لإدخال المستخدم.

ويمكن أن نستخدم مصنع بدلا من ذلك؟ نعم

لماذا لم نحن؟ بناء أكثر منطقية أعتقد.

تستخدم

والمصانع لإنشاء أنواع مختلفة من الكائنات التي هي من نفس النوع الأساسي (تنفيذ نفس واجهة أو قاعدة الدرجة).

وبناة بناء نفس نوع الكائن مرارا وتكرارا، ولكن بناء ديناميكية بحيث يمكن تغييرها في وقت التشغيل.

وبينما يمر الإطار مايكروسوفت MVC، حصلت على الفكر عن نمط البناء. جئت عبر نمط في فئة ControllerBuilder. هذه الفئة هي العودة الى الطبقة مصنع تحكم، الذي يستخدم بعد ذلك لبناء تحكم ملموس.

وميزة أرى في استخدام نمط البناء هو أنه يمكنك إنشاء مصنع خاص بك وتوصيله إلى الإطار.

وTetha، يمكن أن يكون هناك مطعم (الإطار) التي يديرها الرجل الإيطالي، الذي يخدم بيتزا. من أجل إعداد البيتزا الإيطالية الرجل (باني كائن) يستخدم أوين (مصنع) مع قاعدة البيتزا (الفئة الأساسية).

والرجل الآن الهندي تتولى المطعم من الرجل الإيطالي. مطعم هندي خوادم (الإطار) دوسا بدلا من البيتزا. من أجل إعداد دوسا الهندي الرجل (كائن منشئ) يستخدم مقلاة (مصنع) مع المائدة (الفئة الأساسية)

إذا نظرتم السيناريو، والغذاء هو مختلف، ويتم تحضير طريقة الغذاء يختلف، ولكن في نفس مطعم (تحت الإطار نفسه). مطعم يجب بناء في مثل هذه الطريقة التي يمكن أن تدعم الصينية والمكسيكية أو أي المطبخ. بناء الكائن داخل إطار يسهل على المساعد نوع من المأكولات التي تريدها. على سبيل المثال

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، وبناء سلسلة وأشياء أخرى كثيرة. تخيل لو java.util.Map قد وضعت كبناء. هل يمكن أن تفعل مثل هذه الاشياء:

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

بناءً على الإجابات السابقة (المقصود من التورية)، فإن المثال الواقعي الممتاز هو رائعتم تصميمه لدعم 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);
    }
}

لقد كرهت دائمًا نمط Builder باعتباره شيئًا غير عملي ومتطفلًا وغالبًا ما يتم إساءة استخدامه من قبل المبرمجين الأقل خبرة.إنه نمط يكون منطقيًا فقط إذا كنت بحاجة إلى تجميع الكائن من بعض البيانات التي تتطلب ذلك خطوة ما بعد التهيئة (أي.بمجرد جمع كافة البيانات - افعل شيئًا بها).بدلاً من ذلك، في 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");

الآن لدينا فئة واحدة أنيقة تدير عملية التهيئة الخاصة بها وتقوم تقريبًا بنفس وظيفة المنشئ، باستثناء أنها أكثر أناقة بكثير.

وكنت باني في مكتبة مراسلة نابعة من الداخل. كان جوهر مكتبة تلقي البيانات من السلك، وجمع ذلك مع المثال بناء، ثم، مرة واحدة قررت بناء it've حصلت كل ما يحتاج إليه لإنشاء مثيل رسالة، Builder.GetMessage () تم إنشاء مثيل الرسالة باستخدام البيانات التي تم جمعها من السلك.

وعندما أردت استخدام XMLGregorianCalendar معيار لبلدي XML الاعتراض حشد من التاريخ والوقت في جاوة، وسمعت الكثير من التعليقات على كيفية الثقيلة الوزن ومرهقة كان لاستخدامه. كنت أحاول أن comtrol حقول XML في XS: البنيات التاريخ والوقت لإدارة التوقيت، ميلي ثانية، الخ

وهكذا أنا مصمم أداة لبناء تقويم XMLGregorian من تقويم ميلادي أو java.util.Date.

ولأن من حيث أعمل أنا لا يسمح لتقاسمها على الانترنت بدون القانونية، ولكن هنا مثال عن كيفية يستخدم عميل ذلك. ومن خلاصات التفاصيل وبتصفية بعض من تنفيذ XMLGregorianCalendar التي هي أقل استخداما ل XS: التاريخ والوقت

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

ويمنح هذا النمط هو أكثر من مرشح لأنها تحدد الحقول في xmlCalendar كما غير معروف حتى يتم استثناؤها، فإنه لا يزال "يبني" عليه. واضاف لقد بسهولة خيارات أخرى لباني لخلق القيمة xs: التاريخ، و xs: الوقت بنية وأيضا على التعامل مع إزاحة التوقيت عند الحاجة

إذا كنت قد رأيت من أي وقت مضى التعليمات البرمجية التي تخلق ويستخدم XMLGregorianCalendar، هل ترى كيف جعل هذا من الاسهل بكثير للتلاعب.

تحقق من InnerBuilder، وهو IDEA المساعد IntelliJ للأن يضيف العمل على 'بناء' إلى القائمة توليد (البديل + إدراج) الذي يولد فئة باني الداخلية كما هو موضح في جاوة الفعالة

https://github.com/analytically/innerbuilder

وهناك مثال العالم الحقيقي العظيم هو لاستخدامها عند وحدة اختبار الفصول الدراسية. يمكنك استخدام سوت (النظام تحت الاختبار) بناة.

مثال:

والدرجة:

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

وبناء سوت:

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