Oluşturucu Desenini ne zaman kullanırsınız? [kapalı]


531

Oluşturucu Desenini kullanmanın bazı yaygın , gerçek dünya örnekleri nelerdir? Seni ne satın alıyor? Neden sadece bir Fabrika Deseni kullanmıyorsunuz?


Stackoverflow.com/questions/35238292/... bazı API'leri belirtilen kullanım oluşturucu model olduğu
Alireza Fattahi

Aaron ve Tetha'nın cevapları gerçekten bilgilendiricidir. İşte bu cevaplarla ilgili makalenin tamamı .
Diablo

Yanıtlar:


262

Bir yapıcı ve fabrika IMHO arasındaki temel fark, bir yapıcıyı bir nesne oluşturmak için birçok şey yapmanız gerektiğinde yararlıdır. Örneğin bir DOM düşünün. Nihai nesnenizi elde etmek için bol miktarda düğüm ve nitelik oluşturmanız gerekir. Fabrika, tek bir yöntem çağrısında tüm nesneyi kolayca oluşturabildiğinde fabrika kullanılır.

Oluşturucu kullanmanın bir örneği, bir XML belgesi oluşturmaktır, HTML parçaları oluştururken bu modeli kullandım, örneğin belirli bir tablo türü oluşturmak için bir Oluşturucuya sahip olabilirim ve aşağıdaki yöntemlere sahip olabilir (parametreler gösterilmiyor) :

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

Bu oluşturucu daha sonra benim için HTML tükürür. Bunu okumak, büyük bir prosedür yöntemiyle yürümekten çok daha kolaydır.

Wikipedia'da Oluşturucu Kalıbı'na göz atın .


1021

Aşağıda, modelin ve örnek kodun Java'da kullanılmasının tartışıldığı bazı nedenler vardır, ancak Tasarım Desenlerinde Dörtlü Çete tarafından kapsanan Oluşturucu Deseni'nin bir uygulamasıdır . Java'da kullanmanızın nedenleri diğer programlama dilleri için de geçerlidir.

Joshua Bloch'un Etkili Java, 2. Baskıda belirttiği gibi :

Oluşturucu deseni, kurucuları veya statik fabrikaları birkaç parametreden daha fazlasına sahip olacak sınıflar tasarlarken iyi bir seçimdir.

Bir noktada, her eklemenin yeni bir seçenek parametresi eklediği kurucular listesiyle bir sınıfla karşılaştık:

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

Buna Teleskop Oluşturucu Deseni denir. Bu modeldeki sorun, kurucular 4 veya 5 parametre uzunluğunda olduğunda, parametrelerin gerekli sırasını ve belirli bir durumda hangi belirli kurucuyu isteyebileceğinizi hatırlamak zorlaşır .

Teleskop Oluşturucu Deseni'ne sahip olmanız gereken alternatiflerden biri , zorunlu parametreleri içeren bir kurucuyu çağırmanız ve sonra isteğe bağlı ayarlayıcıları çağırmanız gereken JavaBean Deseni'dir:

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

Buradaki sorun, nesne birkaç çağrıda yaratıldığı için, yapısının yarıda tutarsız bir durumda olabilmesidir. Bu ayrıca iplik güvenliğini sağlamak için çok fazla çaba gerektirir.

Daha iyi bir alternatif, Oluşturucu Desenini kullanmaktır.

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

Not Pizza değişmez ve bu parametre değerleri tek bir konumda hepsi . Builder'ın setter yöntemleri Builder nesnesini döndürdüğü için zincirlenebilirler .

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

Bu, yazılması ve okunması ve anlaşılması çok kolay olan kodlarla sonuçlanır. Bu örnekte, derleme yöntemi , oluşturucudan Pizza nesnesine kopyalandıktan sonra parametreleri denetlemek ve geçersiz bir parametre değeri sağlandıysa bir IllegalStateException özel durumu atamak için parametreleri değiştirmek üzere değiştirilebilir. Bu desen esnektir ve gelecekte daha fazla parametre eklemek kolaydır. Gerçekten sadece bir kurucu için 4 veya 5'ten fazla parametreye sahip olacaksanız kullanışlıdır. Bununla birlikte, gelecekte daha fazla parametre ekleyeceğinizden şüpheleniyorsanız , ilk etapta faydalı olabilir.

Joshua Bloch'un 2.Baskı Etkili Java kitabından bu konudan büyük ödünç aldım . Bu model ve diğer etkili Java uygulamaları hakkında daha fazla bilgi edinmek için kesinlikle tavsiye ederim.


24
Orijinal GOF oluşturucudan farklı değil mi? Çünkü Yönetmen Sınıfı yok. Benim için neredeyse başka bir desen gibi görünüyor, ama çok yararlı olduğunu kabul ediyorum.
Lino Rosa

194
Bu özel örnek için, boole parametrelerini kaldırmak ve söyleyebilmek daha güzel olmaz mıydınew Pizza.Builder(12).cheese().pepperoni().bacon().build();
Fabian Steeg

46
Bu , bir oluşturucu modelinden çok bir Akıcı Arayüze benziyor .
Robert Harvey

21
@Fabian Steeg, bence insanlar daha güzel görünümlü boolean ayarlayıcılarına aşırı tepki veriyor, bu tür ayarlayıcıların çalışma zamanı değişikliklerine izin vermediğini unutmayın:, Pizza.Builder(12).cheese().pepperoni().bacon().build();kodunuzu yeniden derlemeniz veya yalnızca biraz pepperoni'ye ihtiyacınız varsa gereksiz mantığa sahip olmanız gerekir. pizzalar. En azından @Kamikaze Mercenary gibi önerilen parametreli versiyonları da sunmalısınız. Pizza.Builder(12).cheese(true).pepperoni(false).bacon(false).build();. Sonra tekrar, asla birim test yapmıyoruz, değil mi?
egallardo

23
@JasonC Doğru ve değişmez bir pizza ne işe yarar ki?
Maarten Bodewes

325

Bir restoran düşünün. "Bugünün yemeği" nin oluşturulması bir fabrika modelidir, çünkü mutfağa "bugünkü yemeği al" deyin ve mutfak (fabrika) gizli kriterlere göre hangi nesnenin üretileceğine karar verirsiniz.

Özel bir pizza sipariş ederseniz oluşturucu görünür. Bu durumda, garson şefe (inşaatçı) "Pizzaya ihtiyacım var; peynir, soğan ve pastırma ekleyin!" Böylece, oluşturucu oluşturulan nesnenin sahip olması gereken öznitelikleri ortaya çıkarır, ancak bunların nasıl ayarlanacağını gizler.


Nitin , bu soruya başka bir cevapla mutfak benzetmesini genişletti .
Pops

19

.NET StringBuilder sınıfı, oluşturucu kalıbının harika bir örneğidir. Çoğunlukla bir dizi adımda bir dize oluşturmak için kullanılır. ToString () yöntemini kullanarak elde ettiğiniz son sonuç her zaman bir dizedir ancak bu dizenin oluşturulması StringBuilder sınıfında hangi işlevlerin kullanıldığına göre değişir. Özetle, temel fikir karmaşık nesneler oluşturmak ve nasıl inşa edildiğinin uygulama detaylarını gizlemektir.


9
Bunun yapıcı kalıp olduğunu düşünmüyorum. StringBuilder, bir karakter dizisi sınıfının (yani dize) başka bir uygulamasıdır, ancak dizeler değişmez olduğundan performans ve bellek yönetimini hesaba katar.
Charles Graham

23
Java'daki StringBuilder sınıfı gibi kesinlikle oluşturucu kalıbıdır. Bu sınıfların her ikisinin append () yönteminin StringBuilder b.append(...).append(...)öğesinin kendisini nasıl döndürdüğüne dikkat edin, böylece son olarak aramadan önce zincir oluşturabilirsiniz toString(). Alıntı: infoq.com/articles/internal-dsls-java
pohl

3
@pohl Ya bunun gerçekten bir yapıcı modeli olduğunu düşünmüyorum, bunun daha akıcı bir arayüz olduğunu söyleyebilirim.
Didier A.

"Bu iki sınıfın append () yöntemi nasıl StringBuilder kendisini döndürür dikkat edin" Bu Builder desen değil, bu sadece akıcı bir arayüz. Bu genellikle bir Builder'ın akıcı bir arayüz kullanacağıdır. Bir Builder'ın akıcı bir arayüze sahip olması gerekmez.
bytedev

Ancak, senkronize edilen StringBuffer'ın aksine, Nature tarafından StringBuilder'ın senkronize olmadığını unutmayın.
Alan Deep

11

Çok iş parçacıklı bir sorun için, her iş parçacığı için karmaşık bir nesnenin oluşturulması gerekiyordu. Nesne, işlenmekte olan verileri temsil eder ve kullanıcı girişine bağlı olarak değişebilir.

Bunun yerine bir fabrika kullanabilir miyiz? Evet

Neden yapmadık? Builder sanırım daha mantıklı.

Fabrikalar, aynı temel tipte (aynı arabirimi veya temel sınıfı uygulayan) farklı nesne türleri oluşturmak için kullanılır.

İnşaatçılar aynı türden nesneyi tekrar tekrar inşa ederler, ancak yapı dinamiktir, böylece çalışma zamanında değiştirilebilir.


9

Başa çıkmak için birçok seçeneğiniz olduğunda kullanın. Jmock gibi şeyleri düşünün:

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

Çok daha doğal bir his veriyor ve ... mümkün.

Ayrıca xml bina, dize bina ve daha birçok şey var. java.util.MapBir inşaatçı olarak koyduğunuzu düşünün . Bunun gibi şeyler yapabilirsiniz:

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

3
Ben bir yapıcı desen uyguladı ve orada yapıyı görmek için sürpriz oldu "eğer" harita okumayı unuttum .. :)
sethu

3
:) Bunun için üzgünüm. Birçok dilde geçersiz olmak yerine kendini döndürmek yaygındır. Java ile yapabilirsiniz, ama çok yaygın değil.
Dustin

7
Harita örneği, yöntem zincirlemesinin bir örneğidir.
nogridbag

@nogridbag Aslında basamaklı metoda daha yakın olurdu. Zincirlemeyi basamaklı simüle edecek şekilde kullanıyor olsa da, açıkça zincirleme, ama anlamsal olarak basamaklı gibi davranıyor.
Didier A.

9

Microsoft MVC çerçevesinden geçerken, oluşturucu deseni hakkında bir düşünce aldım. ControllerBuilder sınıfındaki desenle karşılaştım. Bu sınıf, daha sonra beton kontrolörü oluşturmak için kullanılan kontrolör fabrika sınıfını döndürmektir.

Oluşturucu desenini kullanmanın avantajı, kendi fabrikanızı oluşturabilir ve çerçeveye bağlayabilirsiniz.

@Tetha, Pizza yapan İtalyan bir adam tarafından işletilen bir restoran (Çerçeve) olabilir. Pizza hazırlamak için İtalyan adam (Object Builder), pizza tabanı (temel sınıf) olan Owen'ı (Fabrika) kullanır.

Şimdi Hintli adam restoran İtalyan adam alır. Hint restoranı (Framework) sunucuları pizza yerine dosa. Dosa hazırlamak için Hintli adam (nesne oluşturucu) Maida (temel sınıf) ile Kızartma Tavası (Fabrika) kullanır

Senaryoya bakarsanız, yiyecek farklıdır, yemeğin hazırlanma şekli farklıdır, ancak aynı restoranda (aynı çerçeve altında). Restoran, Çin, Meksika veya herhangi bir mutfağı destekleyecek şekilde inşa edilmelidir. Çerçeve içindeki nesne oluşturucu, istediğiniz mutfağı eklenti yapmanızı kolaylaştırır. Örneğin

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

Builder desenini her zaman hantal, rahatsız edici ve daha az deneyimli programcılar tarafından kötüye kullanılan bir şey olarak sevmedim. Yalnızca bir başlatma sonrası adım gerektiren bazı verilerden nesneyi birleştirmeniz gerektiğinde mantıklı olan bir modeldir (yani tüm veriler toplandıktan sonra - onunla bir şeyler yapın). Bunun yerine, zaman oluşturucuların% 99'unda sınıf üyeleri başlatmak için kullanılır.

Bu gibi durumlarda, sadece withXyz(...)sınıf içindeki ayarlayıcıları bildirmek ve kendilerine bir referans döndürmelerini sağlamak daha iyidir .

Bunu düşün:

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

Şimdi kendi başlatmayı yöneten ve çok daha zarif olması dışında, inşaatçı ile hemen hemen aynı işi yapan temiz bir tek sınıfımız var.


Az önce karmaşık XML belgemi JSON olarak oluşturmak istediğime karar verdim. İlk olarak, 'Kompleks' sınıfının XMLable bir ürünü ilk etapta teslim edebileceğini nasıl bilebilirim ve JSONable nesnesi üretmek için onu nasıl değiştirebilirim? Hızlı cevap: Yapamıyorum çünkü inşaatçılar kullanmam gerekiyor. Ve tam bir daire geliyoruz ...
David Barker

1
Toplam bs, Builder değişmez nesneler inşa etmek ve ürün sınıfı dokunmadan gelecekte bina yolunu değiştirmek için tasarlanmıştır
Mickey Tin

6
hmmm? Cevapta Builder'ın ne için tasarlandığını söyleyerek bir yer okudunuz mu? Bu, daha basit bir şeyin işi daha iyi yaptığı, desenin sayısız kötüye kullanım deneyimine dayanan "Oluşturucu Desenini ne zaman kullanırsınız?" Bunları ne zaman ve nasıl kullanacağınızı biliyorsanız tüm modeller yararlıdır - bu, desenleri ilk etapta belgelemenin bütün noktasıdır! Desen aşırı kullanıldığında veya daha kötüsü - yanlış kullanıldığında - kodunuz bağlamında bir anti-desen haline gelir. Alas ...
Pavel Lechev

6

Oluşturucunun bir başka avantajı, bir Fabrikanız varsa, kodunuzda hala bazı bağlantıların bulunmasıdır, çünkü Fabrika'nın çalışması için, oluşturabileceği tüm nesneleri bilmelidir . Oluşturulabilecek başka bir nesne eklerseniz, onu dahil etmek için fabrika sınıfını değiştirmeniz gerekir. Bu, Soyut Fabrika'da da olur.

Inşaatçı ile Öte yandan, sadece bu yeni sınıf için yeni bir beton oluşturucu oluşturmak zorunda. Yönetmen sınıfı aynı kalacaktır, çünkü kurucuya yapıcıyı alır.

Ayrıca, inşaatçının birçok lezzeti vardır. Kamikaze Paralı Asker bir tane daha verir.


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
Yanıtlarınızı iki şekilde geliştirebilirsiniz: 1) SSCCE olun. 2) Bunun soruyu nasıl cevapladığını açıklayın.
james.garriss


3

Ev yapımı mesajlaşma kütüphanesinde inşaatçı kullandım. Kütüphane çekirdeği telden veri alıp Builder örneğiyle topladı, ardından Builder bir Message örneği oluşturmak için gereken her şeye sahip olduğuna karar verdiğinde, Builder.GetMessage (), tel.



2

Java'da DateTime'ın biçimlendirilmesini engellemek için XML'im için standart XMLGregorianCalendar kullanmak istediğimde, onu kullanmanın ne kadar ağır ve hantal olduğuna dair birçok yorum duydum. Ben xs: datetime yapıları saat dilimi, milisaniye, vb yönetmek için XML alanları kontrol etmeye çalışıyordu.

Bu yüzden bir GregorianCalendar veya java.util.Date'den XMLGregorian takvimi oluşturmak için bir yardımcı program tasarladım.

Nerede çalıştığımdan dolayı yasal olmadan çevrimiçi olarak paylaşmama izin verilmiyor, ancak bir müşterinin bunu nasıl kullandığına bir örnek. Ayrıntıları özetler ve xs: datetime için daha az kullanılan XMLGregorianCalendar uygulamasının bazı uygulamalarını filtreler.

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

Bu kalıbın, xmlCalendar içindeki alanları tanımsız olarak ayarladığı için daha fazla filtre olduğu için hariç tutulurlar, yine de "oluşturur". Kolayca xs: date ve xs: time struct oluşturmak ve gerektiğinde saat dilimi ofsetlerini işlemek için oluşturucuya başka seçenekler ekledim.

XMLGregorianCalendar öğesini oluşturan ve kullanan bir kod gördüyseniz, bunun nasıl değiştirileceğini çok daha kolay hale getirirsiniz.


0

Gerçek bir dünya örneği, sınıflarınızı birim test ederken kullanmaktır. Sut (Test Altında Sistem) oluşturucularını kullanırsınız.

Misal:

Sınıf:

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;

}

Ölçek:

    [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 Oluşturucu:

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

Neden bu durumda sadece CustomAuthenticationServicesınıfınıza ayarlayıcılar eklemek yerine bir kurucuya ihtiyacınız var ?
Laur Ivan

Bu güzel bir soru @LaurIvan! Belki benim örneğim biraz zayıftı, ama CustomAuthenticationService sınıfını değiştiremeyeceğinizi düşünün, oluşturucu birim test okumalarınızı iyileştirmek için çekici bir yol olacaktır. Bence sadece testleriniz için kullanılacak alıcılar ve ayarlayıcılar alanlarınızı ortaya çıkaracaktır. Şimdi Sut Builder hakkında daha fazla bilgi edinmek isterseniz, Test Data Builder hakkında okuyabilirsiniz , hemen hemen aynıdır, ancak dikişler için.
Rafael Miceli
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.