Antoine Sauvinet — Technology Executive & Entrepreneur
FR | EN
Blog CV Contact

[EnhanceYourCode] : le Builder Pattern

Builder

Bonjour,

Dans cet article, j’aimerais vous présenter le Builder pattern et comment je l’utilise dans mon code C#.

Le Builder Pattern est un Creational design pattern, comme le Factory Method Pattern que j’ai déjà couvert dans cet article précédent. Son objectif principal est de fournir un DSL léger pour construire des objets en définissant des propriétés de démarrage, en séparant le processus de construction d’un objet de l’objet lui-même.

Essentiellement, l’idée est de créer une classe avec des champs mutables, qui seront initialisés avec des méthodes dédiées, et une méthode finale qui crée l’objet lui-même à partir de ces valeurs. Voici un exemple abstrait :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
using System;

namespace Builder
{
    public class MyClass
    {
        private readonly string _aStringProperty;
        private readonly bool _aGivenFlag;
        private readonly bool _anotherFlag;

        public MyClass(string aStringProperty, bool aGivenFlag, bool anotherFlag)
        {
            _aStringProperty = aStringProperty;
            _aGivenFlag = aGivenFlag;
            _anotherFlag = anotherFlag;
        }

        public object DoSomeBehavior()
        {
            dynamic @object = null;
            @object.actionAt = DateTime.UtcNow;
            @object.theName = _aStringProperty;

            if (_aGivenFlag)
                @object.theName = "the property : " + @object.theName;

            if (_anotherFlag)
                @object.theName = @object.theName.ToLower();

            return @object;
        }
    }
  }  

Cette classe est assez simple : c’est un objet immutable qui exécute un certain comportement. Rien de mal ici, mais sa construction ailleurs dans le code peut être un peu pénible, car nous devons savoir comment cette classe fonctionne en interne pour donner les bons paramètres à son constructeur.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
using System;

namespace Builder
{
    public class MyPoorClassClient
    {
        public void ExecuteTheDefaultUseCase()
        {
            var myClass = new MyClass("the name to use", false, false);
            var theResult = myClass.DoSomeBehavior();
        }

        public void ExecuteAnotherUseCase()
        {
            var myClass = var myClass = new MyClass("the first another one", true, true);
            var theResult = myClass.DoSomeBehavior();
        }
    }
}

Comment pouvons-nous améliorer cette construction, comment pouvons-nous rendre ce type de code plus lisible et donc plus maintenable ? C’est le but du Builder Pattern. Il vous permettra de créer une façon agréable et révélatrice d’intention de construire votre objet :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
using System;

namespace Builder
{
    public class MyClassBuilder
    {
        private string _aNameToSet;
        private bool _prependPropertyDescriptor = false;
        private bool _lowerizeOutput = false;

        public MyClassBuilder SetTheName(string aNameToSet)
        {
            _aNameToSet = aNameToSet;
            return this;
        }
        
        public MyClassBuilder PrependPropertyDescriptor()
        {
            _prependPropertyDescriptor = true;
            return this;
        }

        public MyClassBuilder LowerizeOutput()
        {
            _lowerizeOutput = true;
            return this;
        }

        public MyClass Build()
        {
            return new MyClass(_aNameToSet, _prependPropertyDescriptor, _lowerizeOutput);
        }
    }
}

Notez que toutes les méthodes du builder retournent le builder lui-même, donc nous pouvons composer une Fluent Interface qui améliorera encore la lisibilité.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using System;

namespace Builder
{
    public class MyClassClient
    {
        public void ExecuteTheDefaultUseCase()
        {
            var myClass = new MyClassBuilder()
                .SetTheName("the first use case is simple")
                .Build();

            var theResult = myClass.DoSomeBehavior();
        }

        public void ExecuteAnotherUseCase()
        {
            var myClass = new MyClassBuilder()
                .SetTheName("the first another one")
                .PrependPropertyDescriptor()
                .LowerizeOutput()
                .Build();

            var theResult = myClass.DoSomeBehavior();
        }
    }
}

Notre code pourrait maintenant sembler plus long qu’avant, mais nous avons en fait gagné beaucoup de simplicité, et nous n’avons plus besoin de plonger dans la définition de MyClass pour savoir comment elle se comporte.

Je posterai bientôt un article moins théorique sur ce pattern, restez à l’écoute !

Commentaires

💬 Les commentaires sont partagés entre toutes les versions linguistiques de cet article.