Tuesday 15 January 2013

How to control the state of several objects ( bitwise operator, flag ), French version

Dans ce post, j'essaye d'apporter une solution générique au problème de gestion des statuts possibles d'un objet.
Ex : Si une barre de menus possède plusieurs boutons (1 à N), on veut qu'à un instant t, on puisse commander facilement lesquels de ces N boutons sont visibles et lesquels sont invisibles.
Beaucoup de cas sont analogues à cette problématique.


1-Pour ces N objets , créons un Enum ( ayant N valeurs possibles ):
    [Flags]
    public enum Status
    {
        Status1 = 1,
        Status2 = 2,
        Status4 = 4,
        Status8 = 8    }

Ici j'ai pris le cas de N = 4, la solution reste valable par récurrence.
Notez que les valeurs ne sont pas prises au hasard. (1,2,4,8....) est une suite géometrique de raison 2, le premier élément étant Math.Pow(2,0) = 1.
L'attribut [Flags] permet de faire des opérations "Binaire" sur  ces valeurs ( AND, OR, NOT,...).

2-Sur les N objets, utiliser le pattern "Decorateur" pour décorer ces objets avec une propriété supplémentaire permettant de stocker le statut associé à l'objet.
Pour simplifier , je vais simplement ajouter directement cette propriété directement à l'objet.

    public class Element
    {  

        //propriete standart
        public int Id { get; set; }   
        
        //propriétés supplémentaires
        public Status Status { get; set; }        //permet d'attribuer un statut
à l'objet
          public bool ToBeDisplay { get; set; }    //état de l'objet
à l'instant T
    }

3- Sur les N objets, associez un status sur chaque objet ( relation One-To-One )

            Element e1 = new Element() { Id = 1,  Status = Status.Status1 };
            Element e2 = new Element() { Id = 2,  Status = Status.Status2 };
            Element e4 = new Element() { Id = 4,   Status = Status.Status4 };
            Element e8 = new Element() { Id = 8,  Status = Status.Status8 };


4- Je regroupe ensuite l'objet dans une liste
            List<Element> elements = new List<Element>();
            elements.Add(e1);
            elements.Add(e2);
            elements.Add(e4);
            elements.Add(e8); 


5- Voici la fonction Configure qui va permettre de positionner la valeur de la propriété ToBeDisplay en fonction d'un flag entré en paramètre :

        private static List<Element>  Configure(List<Element> elts,int flag)
        {
            Status f = (Status)(Enum.Parse(typeof(Status),flag.ToString()));           
            foreach (var item in elts)
            {
                bool isToBeDisplayed = false;
                isToBeDisplayed = (item.Status & f ) == item.Status;
                item.ToBeDisplay = isToBeDisplayed;
            }
            return elts;
        }


-flag : variable de type int que l'on va caster en Status afin de pouvoir faire une opération binaire sur cette variable.
-Sur chaque élément, positionnez la propriété isToBeDisplayed .
- la ligne   isToBeDisplayed = (item.Status & f ) == item.Status; permet de jongler sur le champ Status courant de l'objet et le flag.

 Les valeurs de flag qui permettent de faire les combinaisons possibles sont sur l'intervalle: [1,Math.Pow(2,N)-1]

Dans notre cas N = 4, les valeurs de Flag intéressantes sont : 1,2,3,4,5,...15



Voici le programme complet :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    [Flags]
    public enum Status
    {       
        Status1 = 1,
        Status2 = 2,
        Status4 = 4,
        Status8 = 8,
    }

    public class Element
    {       
        public Status Status { get; set; }
        public int Id { get; set; }
        public bool ToBeDisplay { get; set; }
    }
    class Program
    {
        static void Main(string[] args)
        {         
            Element e1 = new Element() { Id = 1,  Status = Status.Status1 };
            Element e2 = new Element() { Id = 2, Status = Status.Status2 };
            Element e4 = new Element() { Id = 4, Status = Status.Status4 };
            Element e8 = new Element() { Id = 8, Status = Status.Status8 };
            List<Element> elements = new List<Element>();        
            elements.Add(e1);
            elements.Add(e2);
            elements.Add(e4);
            elements.Add(e8);


            var r = Configure(elements, 7);
            foreach (var item in r)
            {
                if (item.ToBeDisplay)
                {
                    Console.WriteLine(item.Id);
                }
            }
            Console.ReadLine();
        }
        private static List<Element>  Configure(List<Element> elts,int flag)
        {
            Status f = (Status)(Enum.Parse(typeof(Status),flag.ToString()));  

            Console.WriteLine("status  =  " + f);         
            foreach (var item in elts)
            {
                bool isToBeDisplayed = false;
                isToBeDisplayed = (item.Status & f ) == item.Status;
                item.ToBeDisplay = isToBeDisplayed;
            }
            return elts;
        }
    }
}