83 votes

Pourquoi ne puis-je pas avoir de membres d'interface protégés?

Quel est l'argument contre la déclaration de membres à accès protégé sur les interfaces ? Cela, par exemple, est invalide :

public interface IOrange
{
    public OrangePeel Peel { get; }
    protected OrangePips Seeds { get; }
}

Dans cet exemple, l'interface IOrange garantirait que les implémenteurs fournissent au moins une instance de OrangePips à leurs héritiers. Si l'implémenteur le souhaitait, il pourrait étendre la portée à public :

public class NavelOrange : IOrange
{
    public OrangePeel Peel { get { return new OrangePeel(); } }
    protected OrangePips Seeds { get { return null; } }
}

public class ValenciaOrange : IOrange
{
    public OrangePeel Peel { get { return new OrangePeel(); } }
    public OrangePips Seeds { get { return new OrangePips(6); } }
}

L'intention des membres protected sur les interfaces est de fournir un contrat de support pour les héritiers (sous-classes), par exemple :

public class SpecialNavelOrange : NavelOrange
{
    ...
    // Avoir une valeur de graine m'est utile.
    OrangePips seeds = this.Seeds; 
    ...
}

(Admettons que cela ne fonctionnerait pas pour des struct)

Je ne vois pas vraiment de justification pour les modificateurs private ou internal dans les interfaces, mais soutenir à la fois les modificateurs public et protected semble parfaitement raisonnable.


Je vais essayer d'expliquer l'utilité des membres protected sur les interfaces en les séparant entièrement des interfaces :

Imaginons un nouveau mot-clé en C#, support, pour imposer des contrats d'héritage, de sorte que nous déclarions les choses comme suit :

public support IOrangeSupport
{
    OrangePips Seeds { get; }
}

Cela nous permettrait de contracter des classes pour fournir des membres protégés à leurs héritiers :

public class NavelOrange : IOrange, IOrangeSupport
{
    public OrangePeel Peel { get { return new OrangePeel(); } }
    protected OrangePips Seeds { get { return null; } }
}

Ce n'est pas particulièrement utile, car les classes impliqueraient déjà ce contrat en fournissant les membres protected dès le départ.

Mais alors nous pourrions également faire ceci :

public interface IOrange : IOrangeSupport
{
   ...
}

Appliquant ainsi IOrangeSupport à toutes les classes implémentant IOrange et leur demandant de fournir des membres protected particuliers - ce que nous ne pouvons actuellement pas faire.

0 votes

1 votes

Pour approfondir cette question, considérez ce cas d'utilisation. J'ai une classe dérivée qui hérite d'une classe de base générique. Je veux ajouter un membre protégé qui peut être accédé sur l'une des variantes génériques de la classe dérivée, mais qui n'est pas exposé au monde extérieur. classe Base { } interface IDerived { string Secret { get; set; } } classe Derived : Base, IDerived { protected string Secret; protected void LearnSecret(IDerived other) { var x = other.Secret; } }

0 votes

Je suis assez surpris que aucune des réponses ou des commentaires ne mentionne l'EIMI. L'EIMI rend le membre de l'interface privé lorsqu'il est vu à travers le point de vue du type implémentant.

66voto

Szymon Rozga Points 11277

Je pense que tout le monde a martelé le point selon lequel une interface doit avoir uniquement des membres publics, sans détails d'implémentation. Ce que vous cherchez est une classe abstraite.

public interface IOrange
{
    OrangePeel Peel { get; }
}

public abstract class OrangeBase : IOrange
{
    protected OrangeBase() {}
    protected abstract OrangePips Seeds { get; }
    public abstract OrangePeel Peel { get; }
}

public class NavelOrange : OrangeBase
{
    public override OrangePeel Peel { get { return new OrangePeel(); } }
    protected override OrangePips Seeds { get { return null; } }
}

public class ValenciaOrange : OrangeBase
{
    public override OrangePeel Peel { get { return new OrangePeel(); } }
    protected override OrangePips Seeds { get { return new OrangePips(6); } }
}

Édité: On peut soutenir que si nous avons une classe PlasticOrange qui dérive de la classe Ornement, elle ne peut implémenter que IOrange et non la méthode protégée Seeds. C'est bien. Une interface est par définition un contrat entre un appelant et un objet, pas entre une classe et ses sous-classes. La classe abstraite est aussi proche que nous pouvons aller de ce concept. Et c'est bien. Ce que vous proposez essentiellement est une autre construction dans le langage à travers laquelle nous pouvons passer des sous-classes d'une classe de base à une autre sans casser le code. Pour moi, cela n'a aucun sens.

Si vous créez une sous-classe d'une classe, la sous-classe est une spécialisation de la classe de base. Elle devrait être pleinement consciente des membres protégés de la classe de base. Mais si vous décidez soudainement de changer la classe de base, il n'a aucun sens que la sous-classe fonctionne avec n'importe quel autre IOrange.

Je suppose que vous posez une question pertinente, mais cela semble être un cas particulier et honnêtement, je ne vois aucun avantage à cela.

11 votes

Une classe abstraite ne fonctionne que si il est raisonnable de définir les classes de base de NavelOrange et ValenciaOrange de la même manière. Supposons une classe PlasticOrange qui dérive déjà de Ornament.

5 votes

Ensuite, je douterai que l'orange ait des graines ou puisse être pelée. Sur un ton sérieux, c'est un point valable, mais nous sommes contraints par le modèle d'héritage simple OOP, donc je n'ai pas de bonne réponse. Cela étant dit, cette solution résout le cas original, à moins que vous ne prévoyiez de créer le modèle auquel vous faites allusion. :)

56voto

Anton Gogolev Points 59794

Je ne vois pas pourquoi on voudrait ça. Si vous voulez qu'une classe dérivée fournisse une implémentation d'une méthode particulière, utilisez des classes de base abstraites. Les interfaces sont simplement des interfaces. Un contrat public, rien d'autre. Pensez à une interface comme une spécification qui décrit comment l'implémentation doit apparaître pour le monde extérieur. Une spécification pour une fiche à deux broches ne précise pas (du moins je suppose) à quoi sa structure interne doit ressembler. Elle doit simplement être compatible avec une prise de courant. Fiche
(source : <a href="http://www.made-in-china.com/image/2f0j00MCetlafAYTWcM/Two-Round-pin-Plug-with-Power-Wire.jpg" rel="noreferrer">made-in-china.com</a>)

14 votes

Meilleure explication que j'ai jamais entendue d'une interface. +1

7 votes

Je pense que vous trouverez qu'il est plutôt important de savoir si l'implémentation derrière cette prise nécessite un courant de 110V ou 220V ;-)

5 votes

@mindplay.dk Dans un monde parfait, les prises pour 110 et 220 devraient être différentes.

16voto

annakata Points 42676

Parce que cela n'a aucun sens. Une interface est un contrat publiquement exposé. Je suis un IThing, donc j'exécuterai les méthodes IThing si on me le demande. Vous ne pouvez pas demander à un IThing de confirmer qu'il exécute des méthodes dont il ne peut pas vous informer.

0 votes

Mais un IThing peut informer ses héritiers sur les méthodes non public. Pourquoi ne puis-je pas lui demander d'exécuter des méthodes IThing supplémentaires dans ce contexte?

0 votes

Parce que ce ne sont pas des méthodes IThing, ce sont les méthodes/propriétés de la classe de base. Et si deux implémentations de IThing implémentent cette membre protégé et qu'une troisième ne le fait pas car cela n'a tout simplement aucun sens. Que se passe-t-il alors ?

0 votes

@AJ - Perplexe. Un IThing ne peut pas dire à ses héritiers à propos des éléments non publics, c'est bien ça le point, non ?

10voto

JaredPar Points 333733

Les interfaces existent pour permettre aux gens d'accéder à votre classe sans savoir quelle est l'implémentation concrète. Cela sépare complètement l'implémentation du contrat de passation des données.

Par conséquent, tout ce qui se trouve dans une interface doit être public. Les membres non publics ne sont utiles que si vous avez accès à l'implémentation et ne contribuent donc pas de manière significative à la définition de l'interface.

7voto

Marc Gravell Points 482669

Les membres de l'interface sont une API publique ; des choses comme protected, etc., sont des détails d'implémentation - et les interfaces n'ont pas d'implémentation. Je soupçonne que ce que vous recherchez est une implémentation d'interface explicite :

public class OrangeNavel : IOrange
{
    public OrangePeel Peel { get { return new OrangePeel(); } }
    OrangePips IOrange.Graines { get { return null; } }
}

0 votes

Les implémentations d'interface explicites restent public, donc ce n'est pas vraiment ce que je recherche. Plus précisément, je pense que les membres protected fourniraient une API entre des ensembles de classes de base et leurs classes filles.

0 votes

Cela relève strictement de la classe de base et de l'héritier - cela n'a absolument rien à voir avec l'interface. Si vous souhaitez une méthode protégée dans la classe de base, il suffit d'en déclarer une.

0 votes

D'accord, mais supposons que nous avons une large gamme de classes de base implémentant l'interface. Il semble raisonnable de définir un contrat qui stipule que toute personne dérivant de ces classes peut s'attendre à un certain niveau de support.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X