Question Quand utiliseriez-vous le motif Builder? [fermé]


Quels sont certains commun, exemples du monde réel d'utiliser le motif Builder? Qu'est-ce que ça t'achète? Pourquoi ne pas simplement utiliser un motif d'usine?


459
2017-11-30 05:41


origine


Réponses:


La principale différence entre un constructeur et une usine IMHO, est qu'un générateur est utile lorsque vous devez faire beaucoup de choses pour créer un objet. Par exemple, imaginez un DOM. Vous devez créer beaucoup de nœuds et d'attributs pour obtenir votre objet final. Une fabrique est utilisée lorsque l’usine peut facilement créer l’objet entier dans un appel de méthode.

Un exemple d'utilisation d'un générateur est la construction d'un document XML. J'ai utilisé ce modèle lors de la création de fragments HTML. Par exemple, je pourrais avoir un générateur pour construire un type spécifique de table et il pourrait avoir les méthodes suivantes (les paramètres ne sont pas affichés):

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

Ce constructeur cracherait alors le HTML pour moi. C'est beaucoup plus facile à lire que de parcourir une grande méthode procédurale.

Check-out Builder Pattern sur Wikipedia.


228
2017-11-30 05:51



Voici quelques raisons qui militent en faveur de l’utilisation du modèle et de l’exemple de code en Java, mais il s’agit d’une implémentation du modèle de générateur couvert par Gang of Four dans Modèles de conception. Les raisons pour lesquelles vous l'utiliseriez en Java sont également applicables à d'autres langages de programmation.

Comme le dit Joshua Bloch dans Java efficace, 2e édition:

Le modèle de générateur est un bon choix lors de la conception de classes dont les constructeurs ou les fabriques statiques auraient plus que quelques paramètres.

Nous avons tous rencontré à un moment donné une classe avec une liste de constructeurs où chaque ajout ajoute un nouveau paramètre d’option:

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

C'est ce qu'on appelle le modèle de constructeur télescopique. Le problème avec ce modèle est que, une fois que les constructeurs ont 4 ou 5 paramètres, cela devient difficile à retenir Le nécessaire ordre des paramètres ainsi que quel constructeur particulier vous pourriez vouloir dans une situation donnée.

Un alternative vous devez le modèle de constructeur télescopique est le JavaBean Pattern où vous appelez un constructeur avec les paramètres obligatoires, puis appelez les paramètres facultatifs après:

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

Le problème ici est que parce que l'objet est créé sur plusieurs appels, il peut être dans un état incohérent au cours de sa construction. Cela nécessite également beaucoup d'efforts supplémentaires pour assurer la sécurité du filetage.

La meilleure alternative est d'utiliser le pattern 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;
  }
}

Notez que La pizza est immuable et les valeurs des paramètres sont toutes dans un seul endroit. Étant donné que les méthodes setter du générateur renvoient l'objet Builder, elles sont capable d'être enchaîné.

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

Il en résulte un code facile à écrire et très facile à lire et à comprendre. Dans cet exemple, le méthode de construction pourrait être modifiée pour vérifier les paramètres après les avoir copiés du constructeur sur l'objet Pizza et lancer une exception IllegalStateException si une valeur de paramètre non valide a été fournie. Ce modèle est flexible et il est facile d'y ajouter plus de paramètres à l'avenir. Ce n'est vraiment utile que si vous avez plus de 4 ou 5 paramètres pour un constructeur. Cela dit, cela pourrait valoir la peine en premier lieu Si vous pensez que vous ajouterez peut-être plus de paramètres dans le futur.

J'ai beaucoup emprunté à ce sujet dans le livre Java efficace, 2e édition par Joshua Bloch. Pour en savoir plus sur ce modèle et d’autres pratiques Java efficaces Je le recommande fortement. 


917
2017-12-23 15:52



Considérons un restaurant. La création du "repas d'aujourd'hui" est un motif d'usine, parce que vous dites à la cuisine "prenez-moi le repas d'aujourd'hui" et la cuisine (usine) décide quel objet générer, sur la base de critères cachés.

Le constructeur apparaît si vous commandez une pizza personnalisée. Dans ce cas, le serveur dit au chef cuisinier: «J'ai besoin d’une pizza, ajoute du fromage, des oignons et du bacon! Ainsi, le constructeur expose les attributs que l'objet généré devrait avoir, mais cache comment les définir.


296
2017-11-30 19:00



La classe .NET StringBuilder est un excellent exemple de modèle de générateur. Il est principalement utilisé pour créer une chaîne dans une série d'étapes. Le résultat final que vous obtenez sur ToString () est toujours une chaîne, mais la création de cette chaîne varie selon les fonctions de la classe StringBuilder utilisées. Pour résumer, l'idée de base est de construire des objets complexes et de masquer les détails d'implémentation de la construction.


16
2018-03-20 01:46



Pour un problème multi-thread, nous avions besoin d'un objet complexe pour chaque thread. L'objet représentait les données en cours de traitement et pouvait changer en fonction de l'entrée de l'utilisateur.

Pouvons-nous utiliser une usine à la place? Oui

Pourquoi pas nous? Builder a plus de sens, je suppose.

Les usines sont utilisées pour créer différents types d'objets qui sont du même type de base (implémenter la même interface ou la même classe de base).

Les constructeurs construisent le même type d'objet encore et encore, mais la construction est dynamique et peut donc être modifiée au moment de l'exécution.


9
2017-11-30 05:51



Vous l'utilisez lorsque vous avez beaucoup d'options à traiter. Pensez à des choses comme jmock:

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

C'est beaucoup plus naturel et c'est ... possible.

Il y a aussi le bâtiment xml, la construction de cordes et bien d'autres choses. Imagine si java.util.Map avait mis en tant que constructeur. Vous pourriez faire des trucs comme ça:

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

6
2017-11-30 05:50



En passant par le framework Microsoft MVC, j'ai réfléchi à un modèle de générateur. Je suis tombé sur le motif dans la classe ControllerBuilder. Cette classe doit renvoyer la classe de fabrique du contrôleur, qui est ensuite utilisée pour construire un contrôleur concret.

L'avantage que je vois dans l'utilisation du modèle de construction est que vous pouvez créer votre propre usine et la brancher dans le cadre.

@Tetha, il peut y avoir un restaurant (Framework) géré par un italien qui sert des pizzas. Pour préparer une pizza, un gars italien (Object Builder) utilise Owen (Factory) avec une base de pizza (classe de base).

Maintenant, un indien reprend le restaurant du type italien. Restaurant indien (Framework) serveurs dosa au lieu de pizza. Afin de préparer dosa gars indien (constructeur d'objets) utilise poêle à frire (usine) avec un Maida (classe de base)

Si vous regardez le scénario, la nourriture est différente, la façon dont les aliments sont préparés est différente, mais dans le même restaurant (dans le même cadre). Le restaurant doit être construit de manière à pouvoir supporter la cuisine chinoise, mexicaine ou autre. Constructeur d'objets à l'intérieur du cadre facilite de plugin genre de cuisine que vous voulez. par exemple

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

6
2017-11-17 06:45



Un autre avantage du constructeur est que si vous avez une fabrique, il y a encore du couplage dans votre code, car pour que Factory fonctionne, il doit connaître tous les objets qu'il peut éventuellement créer. Si vous ajoutez un autre objet pouvant être créé, vous devrez modifier la classe de fabrique pour l'inclure. Cela se produit également dans la fabrique abstraite.

Avec le constructeur, par contre, il suffit de créer un nouveau constructeur de béton pour cette nouvelle classe. La classe de directeur restera la même, car elle reçoit le générateur dans le constructeur.

En outre, il existe de nombreuses saveurs de constructeur. Kamikaze Mercenary`s en donne un autre.


5
2017-12-24 00:20



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

5
2018-01-16 12:55



S'appuyant sur les réponses précédentes (jeu de mots), un excellent exemple concret est Sensationnelest construit en soutien à Builders.

Voir Constructeurs dans le Documentation Groovy


5
2017-11-30 17:51