Le problème des boîtes imbriquées (v. 1)

Ce petit travail pratique s'inscrit à la suite du problème des boîtes imbriquées v. 0. Il met en application le schéma de conception Visiteur de même que le schéma de conception Fabrique.

La première partie de ce travail a demandé de vous la rédaction de classes représentant des boîtes imbriquées contenant du texte arbitraire, et manière à pouvoir représenter leur structure visuellement à l'écran ou sur un autre flux.

La deuxième partie, décrite ici, vous demandera de :

Fabriquer des instances de Boite

Supposons que vous consommiez des données d'un flux (p. ex. : un fichier texte). Dans un flux, vous pourrez trouver :

Ainsi, ceci :

static void TestFabriques()
{
   var p = new FabriqueBoites().Créer("mono J'aime mon \"prof\"");
   Console.WriteLine(new Boite(p));
   p = new FabriqueBoites().Créer("cv\nmono J'aime mon \"prof\"\nmono moi itou");
   Console.WriteLine(new Boite(p));
   p = new FabriqueBoites().Créer("ch\nmono J'aime mon \"prof\"\nmono moi itou");
   Console.WriteLine(new Boite(p));
   p = new FabriqueBoites().Créer(
      "ch\ncv\nmono J'aime mon \"prof\"\nmono moi itou\nmono eh ben");
   Console.WriteLine(new Boite(p));
   p = new FabriqueBoites().Créer(
      "ch\ncv\nmc\nmono J'aime mon \"prof\"\nmono moi itou\nmono eh ben");
   Console.WriteLine(new Boite(p));
}

... devrait afficher cela :

+-----------------+
|J'aime mon "prof"|
+-----------------+
+-----------------+
|J'aime mon "prof"|
|-----------------|
|moi itou         |
+-----------------+
+--------------------------+
|J'aime mon "prof"|moi itou|
+--------------------------+
+------------------------+
|J'aime mon "prof"|eh ben|
|-----------------|      |
|moi itou         |      |
+------------------------+
+--------------------------+
|+-----------------+|eh ben|
||J'aime mon "prof"||      |
|+-----------------+|      |
|-------------------|      |
|moi itou           |      |
+--------------------------+

Visiter des instances de Boite

On voudra aussi pouvoir visiter une Boite, donc y injecter une opération arbitraire qui sera capable d'opérer, justement, sur une Boite et ce qu'elle contient, récursivement.

Dans mon propre code, j'ai utilisé les interfaces suivantes...

L'interface IVisitable<T>, qui permet d'accepter un visiteur (instance d'une classe implémentant IVisiteur<T>; voir ci-dessous).

Accepter un visiteur, pour moi, c'est le faire entrer, le faire visiter, et le faire sortir. Sur cette base, vous comprendrez l'interface IVisiteur<T> ci-dessous.

public interface IVisitable<T>
{
   void Accepter(IVisiteur<T> viz);
}

L'interface IVisiteur<T>, qui permet d'implémenter le code à exécuter lors d'une visite.

Le rôle des méthodes Entrer et Sortir est de permettre de noter, dans le parcours de l'objet visité, qu'on descend plus profondément (Entrer) ou qu'on remonte d'un niveau (Sortir). Dans mon code de test (plus bas), je profite de ces mécanismes pour assurer l'indentation correcte de mon affichage

Dans mon propre code, j'ai ajouté une Action (fonction void sans paramètres) à Visiter. Cela me permet par exemple de passer une petite fonction qui affiche le nom du type de boîte en cours de visite sans avoir à ajouter des propriétés dans mes interfaces. Je vous invite à faire de même dans votre propre implémentation (ça pourrait être utile plus tard!)

public interface IVisiteur<T>
{
   void Entrer();
   void Sortir();
   void Visiter(T elem, Action opt);
}

Enfin, pour simplifier les choses, j'ai déterminé que IBoite serait IVisitable<IBoite>, ce qui permet de la visiter et de visiter ce qu'elle contient.

Évidemment, vous adapterez cette idée à votre propre design (rien ne dit que vous aurez suivi ma proposition, et c'est tout à fait correct).

Dans mon implémentation, IÉnumérateur<T> est une interface dérivée de IEnumerator<T> qui exige d'autres fonctionnalités que j'estimais utiles pour mes énumérateurs. Si IEnumerator<T> vous suffit, alors tant mieux : ça simplifie le tout!

public interface IBoite : IEnumerable<string>, IVisitable<IBoite>
{
   void Redimensionner(int hauteur, int largeur);
   IBoite Cloner();
   IÉnumérateur<string> GetÉnumérateur();
   int Largeur { get; }
   int Hauteur { get; }
   bool EstVide { get; }
}

Un exemple simpliste de classe implémentant IVisiteur<IBoite>, dans ce contexte, serait :

class Blablateux : IVisiteur<IBoite>
{
   public void Entrer() { Console.WriteLine("J'entre"); }
   public void Sortir() { Console.WriteLine("Je sors"); }
   public void Visiter(IBoite p, Action opt)
   {
      Console.Write("... je visite ");
      opt();
   }
}

Je m'attends à au moins deux visiteurs dans votre programme (si vous avez des idées amusantes, ayez du plaisir!) :

Un code de test possible serait :

static void TesterVisiteurs()
{
   static void Tester(Boite b, params IVisiteur<IBoite>[] viz)
   {
      Console.WriteLine(b);
      foreach (var v in viz)
         b.Accepter(v);
   }
   var coul = new Couleureur();
   var mes = new Mesureur();
   Tester(new Boite(), coul, mes);
   Tester(new Boite("yo"), coul, mes);
   string texte = @"Man! Hey!!!
ceci est un test
multiligne";
   string autTexte = "Ceci\nitou, genre";
   Boite b0 = new Boite(texte);
   Boite b1 = new Boite(autTexte);
   Tester(b0, coul, mes);
   Tester(b1, coul, mes);
   ComboVertical cv = new ComboVertical(b0, b1);
   Tester(new Boite(cv), coul, mes);
   ComboHorizontal ch = new ComboHorizontal(b0, b1);
   Tester(new Boite(ch), coul, mes);
   ComboVertical cvplus = new ComboVertical(new Boite(cv), new Boite(ch));
   Tester(new Boite(cvplus), coul, mes);
   ComboHorizontal chplus = new ComboHorizontal(new Boite(cv), new Boite(ch));
   Tester(new Boite(chplus), coul, mes);
   ComboVertical cvv = new ComboVertical(new Boite(chplus), new Boite("coucou"));
   Tester(new Boite(cvv), coul, mes);
   Tester(new Boite(
      new ComboHorizontal(
         new Boite("a\nb\nc\nd\ne"),
            new Boite(
               new ComboVertical(
                  new Boite("allo"), new Boite("yo")
               )
            )
         )
      ), coul, mes
   );
   Tester(
      new Boite(new ComboHorizontal(new Boite("Yo"), new Boite())),
      coul, mes
   );
   Tester(
      new Boite(new ComboHorizontal(new Boite(), new Boite("Ya"))),
      coul, mes
   );
   Tester(
      new Boite(new ComboHorizontal(new Boite(), new Boite())),
      coul, mes
   );
   Tester(
      new Boite(new ComboVertical(new Boite("Yip"), new Boite())),
      coul, mes
   );
   Tester(
      new Boite(new ComboVertical(new Boite(), new Boite("Yap"))),
      coul, mes
   );
   Tester(
      new Boite(new ComboVertical(new Boite(), new Boite())),
      coul, mes
   );
   Tester(new Boite(new MonoCombo(new Boite("allo"))), coul, mes);
   Tester(new Boite(
      new MonoCombo(new Boite(new MonoCombo(new Boite("allo"))))
   ), coul, mes);
   Tester(new Boite(
      new ComboVertical(
         new Boite(new MonoCombo(new Boite(new MonoCombo(new Boite("allo"))))),
         new Boite("Eh ben")
      )
   ), coul, mes);
   Tester(new Boite(
      new ComboHorizontal(new Boite("a\nb\nc\nd"),
                          new Boite(new MonoCombo(new Boite())))
   ), coul, mes);
   Tester(new Boite(
      new ComboHorizontal(new Boite(),
                          new Boite(new MonoCombo(new Boite())))
   ), coul, mes);
   Tester(new Boite(
      new ComboHorizontal(
         new Boite(new MonoCombo(new Boite(new MonoCombo(new Boite("allo"))))),
         new Boite(new ComboVertical(
            new Boite("Eh ben"),
            new Boite(new MonoCombo(new Boite(
               new ComboHorizontal(new Boite("yo"), new Boite("hey"))
            )))
         ))
      )
   ), coul, mes);
   Console.WriteLine($"\n\nLa plus petite boite est :\n{mes.PlusPetite}");
   Console.WriteLine($"\n\nLa plus grande boite est :\n{mes.PlusGrande}");
}

... et cela devrait afficher (je n'ai pas mis les couleurs dans les affichages, mais faites-vous plaisir!) :

++
++
Boite
  Mono 0 x 0
+--+
|yo|
+--+
Boite
  Mono 1 x 2
+----------------+
|Man! Hey!!!     |
|ceci est un test|
|multiligne      |
+----------------+
Boite
  Mono 3 x 16
+-----------+
|Ceci       |
|itou, genre|
+-----------+
Boite
  Mono 2 x 11
+----------------+
|Man! Hey!!!     |
|ceci est un test|
|multiligne      |
|----------------|
|Ceci            |
|itou, genre     |
+----------------+
Boite
  ComboVertical
    Boite
      Mono 3 x 16
    Boite
      Mono 2 x 16
+----------------------------+
|Man! Hey!!!     |Ceci       |
|ceci est un test|itou, genre|
|multiligne      |           |
+----------------------------+
Boite
  ComboHorizontal
    Boite
      Mono 3 x 16
    Boite
      Mono 3 x 11
+----------------------------+
|Man! Hey!!!                 |
|ceci est un test            |
|multiligne                  |
|----------------------------|
|Ceci                        |
|itou, genre                 |
|----------------------------|
|Man! Hey!!!     |Ceci       |
|ceci est un test|itou, genre|
|multiligne      |           |
+----------------------------+
Boite
  ComboVertical
    Boite
      ComboVertical
        Boite
          Mono 3 x 28
        Boite
          Mono 2 x 28
    Boite
      ComboHorizontal
        Boite
          Mono 3 x 16
        Boite
          Mono 3 x 11
+---------------------------------------------+
|Man! Hey!!!     |Man! Hey!!!     |Ceci       |
|ceci est un test|ceci est un test|itou, genre|
|multiligne      |multiligne      |           |
|----------------|                |           |
|Ceci            |                |           |
|itou, genre     |                |           |
+---------------------------------------------+
Boite
  ComboHorizontal
    Boite
      ComboVertical
        Boite
          Mono 3 x 16
        Boite
          Mono 2 x 16
    Boite
      ComboHorizontal
        Boite
          Mono 6 x 16
        Boite
          Mono 6 x 11
+---------------------------------------------+
|Man! Hey!!!     |Man! Hey!!!     |Ceci       |
|ceci est un test|ceci est un test|itou, genre|
|multiligne      |multiligne      |           |
|----------------|                |           |
|Ceci            |                |           |
|itou, genre     |                |           |
|---------------------------------------------|
|coucou                                       |
+---------------------------------------------+
Boite
  ComboVertical
    Boite
      ComboHorizontal
        Boite
          ComboVertical
            Boite
              Mono 3 x 16
            Boite
              Mono 2 x 16
        Boite
          ComboHorizontal
            Boite
              Mono 6 x 16
            Boite
              Mono 6 x 11
    Boite
      Mono 1 x 45
+------+
|a|allo|
|b|----|
|c|yo  |
|d|    |
|e|    |
+------+
Boite
  ComboHorizontal
    Boite
      Mono 5 x 1
    Boite
      ComboVertical
        Boite
          Mono 1 x 4
        Boite
          Mono 3 x 4
+---+
|Yo||
+---+
Boite
  ComboHorizontal
    Boite
      Mono 1 x 2
    Boite
      Mono 1 x 0
+---+
||Ya|
+---+
Boite
  ComboHorizontal
    Boite
      Mono 1 x 0
    Boite
      Mono 1 x 2
+-+
+-+
Boite
  ComboHorizontal
    Boite
      Mono 0 x 0
    Boite
      Mono 0 x 0
+---+
|Yip|
|---|
+---+
Boite
  ComboVertical
    Boite
      Mono 1 x 3
    Boite
      Mono 0 x 3
+---+
|---|
|Yap|
+---+
Boite
  ComboVertical
    Boite
      Mono 0 x 3
    Boite
      Mono 1 x 3
++
||
++
Boite
  ComboVertical
    Boite
      Mono 0 x 0
    Boite
      Mono 0 x 0
+------+
|+----+|
||allo||
|+----+|
+------+
Boite
  MonoCombo
    Boite
      Mono 1 x 4
+--------+
|+------+|
||+----+||
|||allo|||
||+----+||
|+------+|
+--------+
Boite
  MonoCombo
    Boite
      MonoCombo
        Boite
          Mono 1 x 4
+--------+
|+------+|
||+----+||
|||allo|||
||+----+||
|+------+|
|--------|
|Eh ben  |
+--------+
Boite
  ComboVertical
    Boite
      MonoCombo
        Boite
          MonoCombo
            Boite
              Mono 1 x 4
    Boite
      Mono 1 x 8
+----+
|a|++|
|b||||
|c||||
|d|++|
+----+
Boite
  ComboHorizontal
    Boite
      Mono 4 x 1
    Boite
      MonoCombo
        Boite
          Mono 2 x 0
+---+
||++|
||++|
+---+
Boite
  ComboHorizontal
    Boite
      Mono 2 x 0
    Boite
      MonoCombo
        Boite
          Mono 0 x 0
+-----------------+
|+------+|Eh ben  |
||+----+||--------|
|||allo|||+------+|
||+----+|||yo|hey||
|+------+|+------+|
+-----------------+
Boite
  ComboHorizontal
    Boite
      MonoCombo
        Boite
          MonoCombo
            Boite
              Mono 1 x 4
    Boite
      ComboVertical
        Boite
          Mono 1 x 8
        Boite
          MonoCombo
            Boite
              ComboHorizontal
                Boite
                  Mono 1 x 2
                Boite
                  Mono 1 x 3


La plus petite boite est :
++
++


La plus grande boite est :
+---------------------------------------------+
|Man! Hey!!!     |Man! Hey!!!     |Ceci       |
|ceci est un test|ceci est un test|itou, genre|
|multiligne      |multiligne      |           |
|----------------|                |           |
|Ceci            |                |           |
|itou, genre     |                |           |
|---------------------------------------------|
|coucou                                       |
+---------------------------------------------+

Prenez le temps de réfléchir à votre design. C'est un travail pratique amusant, et instructif.

Ce qui est essentiel pour la suite des choses

Il faut que votre code contienne l'interface suivante :

public interface IVisitable<T>
{
   void Accepter(IVisiteur<T> viz);
}

Il faut que tout IBoite (ou autre nom de votre cru, mais qui couvrirait Mono, ComboVertical, ComboHorizontal et – si vous souhaitez le bonus – MonoCombo) soit IVisitable<IBoite>.

Il faut que votre code contienne l'interface suivante :

public interface IVisiteur<T>
{
   void Entrer();
   void Sortir();
   void Visiter(T elem, Action opt);
}

Il faut que votre fabrique lève BonusNonSupportéException si vous rencontrez le préfixe "mc" mais ne supportez pas le bonus. Une bonne implémentation récupèrera de cette erreur, et une bonne fabrique escamotera le texte utilisé pour fabriquer des boîtes et qui aurait pu être utilisé pour fabriquer un MonoCombo.


Valid XHTML 1.0 Transitional

CSS Valide !