La dependency object est un point important dans la POO. Vu que cette programmation est modulaire, la dépendance pourrait nuire à la testabilité, la maintabilité et l’extensibilité des différents objets et classes. Mais cette façon d’aborder le problème même si elle est compréhensible, elle pourrait paraître abstraite et plutôt une méta discussion car comme pour tout programmeur qui se respecte, les seuls sujets dignes de discussion sont des sujets de réalisation pratique. Autrement, sans situation réelle, les termes ne décrivent qu’un désir, une théorie . Si un programme fonctionne et aboutit à des résultats corrects, personne ne doit se poser la question est ce qu’il est en tight coupling ou loose coupling. D’ailleurs la question ne se pose qu’au moment de test ou de changement en extension ou maintenance. Mais en anticipation on est en droit de se la poser pour mesurer la pérennité de notre travail. Donc, le programme modulaire doit être modifiable à moindre coût (coût optimal). Pour cela il faut que la dependency soit mesurée et relativement large dès le design du programme. Ainsi un programme peut avoir des liens très étroits entre ses composants ce qui signifie que les composants se connaissent et dépendent les uns des autres d’une manière forte, un changement de l’un impose des changements dans les autres. La question n’est plus théorique. Prenons un exemple simple


    public class User
    {
        public string Add() // Tight Coupling
        {
            SqlServer sqls = new SqlServer();
            return sqls.Save(this);
        }
    }
    public class SqlServer
    {
        public string Save(User user)
        {
            return "sqlServer";
        }
    }

pour ajouter un user il faut appeler la méthode user.Add(); qui instancie un objet sqlServer pour utiliser la méthode Save(user); si on souhaite modifier le SqlServer en SqlDB (en pratique changer la méthode de sauvegarde)


    public class SqlDB
    {
        public string Save(User user)
        {
            return "sqlDB";
        }
    }
il faut modifier dans ce cas la classe User

    public class User
    {
        public string Add() // Tight Coupling
        {
            SqlDB sqls = new SqlDB();
            return sqls.Save(this);
        }
    }

Il est clair que la dépendance est complète (tight). L’addition d’un utilisateur dépend étroitement de la classe de sauvegarde. Si on veut desserrer le lien qui existe entre ces deux classes. On peut procéder en utilisant la dependency Injection qui peut se faire par Constructor Injection ou Setter Injection. Pour cela on peut utiliser une variable dynamic


        public string Add(dynamic bd) 
        {
            return bd.Save(this);
        }

bd pourrait être de type SqlSave ou SqlDB L'appel de Add se fera avec user.Add(new SqlServer()); ou user.Add(new SqlDB()); Or rien n'oblige le programmeur à ajouter la méthode Save dans les classes de base des données. ce qui pourrait causer une exception en lateBinding. Bien sûr on pourra utiliser la reflection capacity et Dynamic Invocation pour exécuter la méthode de sauvegarde au moment du run time (LateBiding) et assurer l'indépendance de user de la base des données. Mais, cette opération sera coûteuse en termes de performance et inexplicable dans ce contexte, surtout que d'autres moyens sont possibles et aboutissent pratiquement au même résultat. Essayons la méthode de l'independency par dynamic Invocation, l'appelant ouvre la fonction save et appelle directement l'instance objet de la base sélectionné :


                    Type t = typeof(SqlDB);
                    Object o=Activator.CreateInstance(t);
                    MethodInfo mi = t.GetMethod("Save");
                    Object[] parms = new Object[1];
                    parms[0]=user;
                    string s = (string)mi.Invoke(o,parms);

                    t =  typeof(SqlServer);
                    o = Activator.CreateInstance(t);
                    mi = t.GetMethod("Save");
                    s = (string)mi.Invoke(o, parms);

Cette méthode n'est pas justifiée, en plus il faut gérer les mi en cas de non existence de Save dans la classe cible.

Il s'agit de créer une interface qui contient la signature Save et qui sera implémentée par les bases des données utilisables.


    public interface IDal://Interface de Data Access Layer
    {
        string Save(User user);
    }
    public class SqlDB:IDal
    {
        public string Save(User user)
        {
            return "sqlDB";
        }
    }
    public class SqlServer:IDal
    {
        public string Save(User user)
        {
            return "sqlServer";
        }
    }

La classe User dans ce cas devrait soit procéder à une injection par constructeur ou une injection par Setter, i.e.


 int DalType;
 public User(int DalType)
 {
	this.Daltype= Daltype;//Constructor Injection
 }
        public string Add() // DI – loose Coupling
        {
            if (DalType == 1)                dal = new SqlServer(); 
            else if (DalType == 2)           dal = new SqlDB();
            if(dal !=null) return dal.Save(this);
	     return "";
        }

 int DalType;
 public SetBase(int DalType)
 {
	this.Daltype= Daltype;//Setter Injection
 }
        public string Add() // DI – loose Coupling
        {
            if (DalType == 1)                dal = new SqlServer(); 
            else if (DalType == 2)           dal = new SqlDB();
            if(dal !=null) return dal.Save(this);
	     return "";
        }

La dépendance dans les deux cas reste importante vu que l'objet user doit connaître les classes de la base pour les utiliser. Mais certainement elle est moindre que dans la première situation.

IoC

Si on veut desserrer le lien à un niveau supérieur et rendre la classe User complètement indépendante des classes de sauvegarde on pourra changer la méthode Add et éliminer la constructor Injection.


    public class SqlDB:IDal
    {
        public string Save(User user)
        {
            return "sqlDB";
        }
    }
    public class SqlServer:IDal
    {
        public string Save(User user)
        {
            return "sqlServer";
        }
    }
    public interface IDal
    {
        string Save(User user);
    }
    public class User
    {
	 public string Add(IDal dal) // IoC – loose Coupling
        {
            return dal.Save(this);
        }
    }

L’appel de user.Add indique en paramètre la classe sql que l’on utilisera. Il suffit d’invoquer la méthode avec new SqlServer() ou new SqlBD(). user.Add(new SqlServer()) ; Ceci est appelé Inversion of Control (don’t call us, we’ll call you-principe de Hollywood) On peut aussi définir une autre manière d'accèder en IoC à la base:


        public string Add<T>() where T : IDal,new()//IoC by type Contrainte interface et new pour indiquer que le type peut être construit avec constructeur sans paramètres
        {
            T b = new T();
            return b.Save(this);
        }

Les contraintes servent au moment de la compilation pour autoriser ou interdire des définitions. L'appel se fait string s=user.Add<SqlDB>(); ou s=user.Add<SqlServer>();