Implémenter le design pattern Fluent Interface en C#

Auteur du billet de blog : Rudy Spano - Neotech Solutions

Rudy Spano

Consultant .Net
  Publié le mercredi 20 avril 2016

Expert technique .Net passionné par tout ce qui est innovant en environnement Microsoft. De la mobilité, du client lourd (Xaml Applications) mais aussi une petite pointe de Web...

Introduction

Il existe de nombreux design patterns permettant de construire des objets en C# ainsi que dans d’autres langages orientés objets : Constructor, Factory, Abstract factory, Builder, …
Le pattern Fluent interface est un petit peu moins connu et peut s’avérer très efficace.

Qu'est-ce que c'est?

Vous l’utilisez déjà probablement souvent lorsque vous faites du LINQ (en mode « method chain ») ou lorsque vous utilisez la Task Parallel Library, le principe de ce design pattern est de construire et de configurer  une instance de classe avec des méthodes chainées.

Quand l'utiliser?

Comme souvent lorsqu’il s’agit de design applicatif, on a une certaine liberté!
Toutefois, une best practice lorsque l’on souhaite créer une classe, une librairie ou un framework est de se pencher sur l’utilisation que je veux permettre de mon composant avant de partir sur sa création.

Si une approche Fluent vous parait plus simple à utiliser, plus intuitive et permet une meilleur découvrabilité, alors vous pouvez être convaincu que c’est une bonne solution (et non la bonne solution !).
Le code suivant est tout à fait acceptable :

Cependant, en tant qu’intégrateur de cette librairie, je peux me poser les questions suivantes:

  • Les différentes propriétés (Matriculation, Airbag, Airconditioning) sont-elles obligatoires ?
  • Ah bon ? La propriété AirConditioning existe ? Depuis quelle version ? Je ne l’avais pas vu…

 En Fluent, cela donnerait :

Ici, je propose une logique naturelle (même si elle remet en cause des habitudes).
Les capacités de ma classe sont préfixées par With, je les utilise si je le souhaite (non obligatoires).

L’intégrateur pourra aisément être conscient des fonctionnalités utilisées (lisibilité) ainsi que des fonctionnalités disponibles (découvrabilité).

Comment développer une classe Fluent?

Nous avons défini notre objectif en termes d’utilisation, passons donc à la définition !

Etape 1 - Création de la classe contenant les propriétés

Créons une simple classe qui nous permettra de stocker les différentes propriétés ainsi qu’une méthode statique permettant son initialisation:

Etape 2 - Permettre la création de cette classe en Fluent

Afin que la classe Car reste simple, créons la classe CarFluentExtentions fournissant des méthodes d’extensions retournant la classe étendue afin de permettre le chainage des appels :

Etape 3 - Accessibilité

Ici, remarquez que les intégrateurs sont toujours libres de construire eux-même la classe Car en passant par le constructeur et la définition des propriétés.

Si vous voulez vous assurer que tout objet Car sera créé via CarFactory, il vous suffit:

  • D’ isoler vos classes dans un assembly
  • De changer l’accessibilité du constructeur de la classe Car en internal (/ !\Constructeur par défaut implicite)
  • De changer l’accessibilité des Setter de propriétés en internal