Lors de l'héritage de la même interface dans plusieurs classes, cela n'a-t-il pas le problème de mettre à jour toutes les classes avec l'implémentation de l'interface? Par exemple, si j'ai une interface implémentée par 4 classes et peut-être plus à l'avenir et si j'ajoute une méthode à l'interface qui est implémentée par une seule des 4 classes, ne serait-ce pas une tâche fastidieuse et implémenter tout le reste des classes avec une exception non implémentée ou quelque chose ..? Je sais que vous pourriez vous demander pourquoi je ne peux pas ajouter cette méthode à la classe elle-même, la raison en est que j'utilise une usine ici et que je renvoie la référence de l'interface avec l'un des objets de ces classes. Avez-vous des idées sur la manière de procéder pour ce genre d'approche? Existe-t-il une approche alternative à ce genre de situation?

Ajout de l'exemple de code pour le scénario ci-dessus

 public interface ITest
{
    string TestMethod1(string st, int ab);
    int TestMethod2(string st);
    void TestMethod4(int ab);
    float ITest.TestMethod3(string st);
}
public class Class1 : ITest
{
    public string TestMethod1(string st, int ab)
    {
        return string.Empty;
    }
    public void TestMethod4(int ab)
    {
        throw new NotImplementedException();
    }

    public int TestMethod2(string st)
    {
        throw new NotImplementedException();
    }

    public float TestMethod3(string st)
    {
        throw new NotImplementedException();
    }
}
 public class Class2 : ITest
{

    float ITest.TestMethod3(string st)
    {
        return float.Parse("12.4");
    }

    void ITest.TestMethod4(int ab)
    {
        throw new NotImplementedException();
    }
    public string TestMethod1(string st, int ab)
    {
        throw new NotImplementedException();
    }

    public int TestMethod2(string st)
    {
        throw new NotImplementedException();
    }
}
 public class Main
{
    ITest test = null;

    public ITest CreateFactory(TestType testType)
    {
        switch(testType)
        {
            case TestType.Class1:
               test = new Class1();
                break;
            case TestType.Class2:
                test = new Class2();
                break;
        }
        return test;
    }
}

enum TestType
{
    Class1,
    Class2
}
2
NewTech 16 janv. 2017 à 18:43

2 réponses

Meilleure réponse

Par exemple, si j'ai une interface implémentée par 4 classes et peut-être plus à l'avenir et si j'ajoute une méthode à l'interface qui est implémentée par une seule des 4 classes, ne serait-ce pas une tâche fastidieuse et implémenter tout le reste des classes avec une exception non implémentée ou quelque chose ..?

Une interface est un contrat. Si vous prévoyez que certaines classes ne seront pas en mesure de respecter le contrat, elles ne doivent pas déclarer qu'elles le font. L'implémentation de l'interface pour lancer simplement NotSupportedException ou NotImplementedException devrait être un dernier recours; il existe généralement des solutions meilleures et plus propres.

La solution dans votre cas est de simplement déclarer une nouvelle interface:

public interface IFrobbable //legacy version
{
     void Frob()
}

public interface IFrobbableOnSteroids: IFrobbable //the new and improved frobbable wonder
{
     void MegaFrob();
}

Vos nouvelles implémentations améliorées devraient mettre en œuvre IFrobbableOnSteroids. Notez que cette nouvelle interface s’implémente IFrobbable; cela signifie que tout objet implémentant IFrobbableOnSteroids serait utilisable à la fois dans le code "hérité" (uniquement conscient de Frob) et dans le code mis à jour (conscient de MegaFrob). Cela a la belle propriété de ne pas casser le code client existant.

Bien sûr, vous pouvez simplement faire en sorte que la nouvelle classe mise à jour implémente les deux interfaces, mais je préfère personnellement faire en sorte qu'une interface inclue l'autre.

Les classes "héritées" qui n'auront pas la chance d'être améliorées resteront aussi simples et mortelles IFrobbable, mais c'est ainsi que cela devrait être. Si vous n'êtes pas un FrobbableInSteroids, ne prétendez pas en être un; comme le dit l'adage dans mon pays (traduction gratuite), "plus facile d'attraper un menteur qu'un estropié".

MISE À JOUR:

Concernant votre question sur une méthode de fabrique, eh bien, étant donné que vos classes semblent être publiques, il n'y a pas de vraie raison pour un enum. Utilisez simplement les génériques et les types eux-mêmes:

public static T CreateFrobbable<T>()
    where T: IFrobbable, new()
{
    return new T();
}

public static T CreateFrobabbleOnSteroids<T>()
    where T: IFrobbableOnSteroids, new()
    {
        return new T();
    }

Et maintenant, si nous avons une classe SomeFrobbable: IFrobbable, vous en créeriez une en appelant simplement CreateFrobbable<SomeFrobbable>(). Et un SomeFrobbableOnSteroids: IFrobbableOnSteroids pourrait être créé soit en tant que IFrobbable avec CreateFrobbable<SomeFrobbableOnSteroids>(), soit en tant que IFrobbableOnSteroids à part entière avec CreateFrobbableOnSteroids<SomeFrobbableOnSteroids>(). Ce que vous ne pourriez pas faire, et c'est une bonne chose, c'est CreateFrobbableOnSteroids<SomeFrobbable>() //Compile time error.

4
InBetween 16 janv. 2017 à 21:14

Apporter des modifications à une API existante est déjà fastidieux et nécessite de nombreux changements sur les clients existants de votre API. Cependant, ajouter de nouveaux membres est un peu plus facile car vous pouvez simplement introduire une nouvelle interface qui ajoute la nouvelle fonctionnalité:

interface ITheInterface
{
    void DoSomething();
}

Créez maintenant une nouvelle interface dérivant de l'existant:

interface ITheInterface2: ITheInterface
{
    void DoSomething2();
}

Une pratique courante - mais pas forcément la meilleure - est de simplement utiliser un nombre après le nom de l'interface indiquant sa version - ici 2 et de marquer l'ancien Obsolete pour indiquer qu'il existe une version plus récente. IMHO il vaudra mieux ajouter un nom indiquant ce que la nouvelle interface fait réellement, c'est la nouvelle fonctionnalité.

Désormais, toutes vos classes existantes peuvent toujours implémenter ITheInterface, tandis que les nouvelles implémentent la nouvelle interface. Vous n'avez donc pas besoin de modifier le code existant mais pouvez consommer de nouveaux membres si vous implémentez cette nouvelle interface.

2
HimBromBeere 16 janv. 2017 à 16:04