31 votes

C # a des classes abstraites et des interfaces, devrait-il aussi avoir des "mixins"?

Chaque si souvent, je tombe sur un cas où j'ai envie d'une collection de classes de tous pour posséder la même logique. Par exemple, peut-être que je veux à la fois un Bird et Airplane pouvoir Fly(). Si vous êtes en train de penser "stratégie de modèle", je suis d'accord, mais même avec de la stratégie, il est parfois impossible d'éviter la duplication de code.

Par exemple, disons que les suivants s'appliquent (et ceci est très similaire à une situation réelle j'ai récemment rencontré):

  1. Les deux Bird et Airplane besoin de tenir une instance d'un objet qui implémente IFlyBehavior.
  2. Les deux Bird et Airplane besoin de demander à l' IFlyBehavior exemple d' Fly() lorsque OnReadyToFly() est appelé.
  3. Les deux Bird et Airplane besoin de demander à l' IFlyBehavior exemple d' Land() lorsque OnReadyToLand() est appelé.
  4. OnReadyToFly() et OnReadyToLand() sont privées.
  5. Bird hérite Animal et Airplane hérite PeopleMover.

Maintenant, disons-nous ajouter ultérieurement Moth, HotAirBalloon, et de 16 autres objets, et disons qu'ils suivent tous le même schéma.

Maintenant, nous allons avoir besoin de 20 copies le code suivant:

private IFlyBehavior _flyBehavior;

private void OnReadyToFly()
{
    _flyBehavior.Fly();
}

private void OnReadyToLand()
{
    _flyBehavior.Land();
}

Deux choses que je n'aime pas à propos de ceci:

  1. Ce n'est pas très SEC (même neuf lignes de code sont répétés maintes et maintes fois). Si nous avons découvert un bug ou d'ajout d'un BankRight() de IFlyBehavior, nous aurions besoin de propager les modifications à tous les 20 classes.

  2. Il n'y a pas de toute façon de la faire respecter toutes les 20 classes implémentent cette répétitif logique interne de manière cohérente. Nous ne pouvons pas utiliser une interface parce que les interfaces ne permettent que des membres du public. Nous ne pouvons pas utiliser une classe de base abstraite parce que les objets héritent des classes de base, et C# ne permet pas l'héritage multiple (et même si les classes n'a pas déjà hériter de classes, nous pourrions plus tard souhaitez ajouter un nouveau comportement qui met en œuvre, disons, ICrashable, donc une classe de base abstraite n'est pas toujours va être une solution viable).

Que faire si...?

Que faire si C# a une nouvelle construction, dire pattern ou template ou [remplir votre idée ici], qui a travaillé comme une interface, mais vous a permis de mettre privés ou protégés des modificateurs d'accès sur les membres? Vous auriez encore besoin de fournir une implémentation pour chaque classe, mais si votre classe de mise en œuvre de l' PFlyable modèle, vous avez au moins un moyen d'assurer que chaque classe avait le code réutilisable appel Fly() et Land(). Et, avec un IDE moderne, tels que Visual Studio, vous seriez en mesure de générer automatiquement le code à l'aide de la "mettre en Œuvre Motif de la commande".

Personnellement, je pense qu'il serait plus judicieux d'étendre le sens de l'interface de la couverture de tout contrat, qu'il soit interne (privé/protégé) ou externe (public), mais je suggère d'ajouter une toute nouvelle construisons d'abord parce que les gens semblent être très catégorique sur le sens du mot "interface", et je ne voulais pas la sémantique de devenir le centre de réponses.

Questions:

Peu importe comment vous l'appelez, je voudrais savoir si la fonctionnalité que je suis en train de suggérer un sens. Avons-nous besoin d'une certaine façon de gérer les cas où nous ne pouvons pas nous soustraire autant de code que nous aimerions, en raison de la nécessité pour restreindre les modificateurs d'accès ou pour des raisons en dehors de l'entreprise de programmation de contrôle?

Mise à jour

De AakashM commentaire, je crois qu'il existe déjà un nom pour la fonctionnalité que je suis en demandant: un Mixin. Donc, je suppose que ma question peut être réduit à: "Doit C# permettent de Mixin?"

3voto

Le problème que vous décrivez pourrait être résolu à l'aide du modèle Visiteur (tout peut être résolu à l'aide du modèle Visiteur, alors méfiez-vous! )

Le modèle visiteur vous permet de passer de la logique de mise en œuvre vers une nouvelle classe. De cette façon, vous n'avez pas besoin d'une classe de base, et un visiteur fonctionne très bien sur les différents héritage des arbres.

Pour résumer:

  1. Nouvelle fonctionnalité n'a pas besoin d'être ajouté à tous les différents types
  2. L'appel à la le visiteur peut être tiré jusqu'à la racine de chaque classe de la hiérarchie

Pour référence, voir le modèle Visiteur

2voto

jeroenh Points 12777

Visual Studio propose déjà cette "pauvre mans forme" avec des extraits de code. Aussi, avec le refactoring la ReSharper (et peut-être même le natif de refactoring dans Visual Studio), vous obtiendrez un long chemin en veillant à la cohérence.

[EDIT: je ne pense pas que les méthodes d'Extension, cette approche vous apporte encore plus loin (vous avez seulement besoin de garder le _flyBehaviour comme une variable privée). Cela rend le reste de ma réponse probablement obsolète...]

Cependant, juste pour le plaisir de la discussion: comment cela pourrait-il être amélioré? Voici ma suggestion.

On pourrait imaginer quelque chose comme ce qui suit pour être pris en charge par une future version du compilateur C#:

// keyword 'pattern' marks the code as eligible for inclusion in other classes
pattern WithFlyBehaviour
{
   private IFlyBehavior_flyBehavior;

   private void OnReadyToFly()
   {
      _flyBehavior.Fly();
   }

   [patternmethod]
   private void OnReadyToLand()
   {
      _flyBehavior.Land();
   }     
}

Où l'on pouvait alors quelque chose comme:

 // probably the attribute syntax can not be reused here, but you get the point
 [UsePattern(FlyBehaviour)] 
 class FlyingAnimal
 {
   public void SetReadyToFly(bool ready)
   {
      _readyToFly = ready;
      if (ready) OnReadyToFly(); // OnReadyToFly() callable, although not explicitly present in FlyingAnimal
   }

 }

Cela serait-il une amélioration? Probablement. Est-il vraiment la peine? Peut-être...

2voto

RameshVel Points 24472

Cant nous utilisons des méthodes d'extension pour cette

     public static void OnReadyToFly(this IFlyBehavior flyBehavior)
    {
          _flyBehavior.Fly()
    }
 

Cela imite la fonctionnalité que vous vouliez (ou Mixins)

1voto

Benjamin Podszun Points 5120

Vous venez de décrire la programmation orientée aspects.

Populaire une AOP de la mise en œuvre de C# qui semble être PostSharp (site Principal semble être en panne/pas de travail pour moi, c' est la page "à Propos").


Pour continuer à suivre les commentaires: je ne suis pas sûr si PostSharp le soutient, mais je pense que vous parlez de cette partie de l'AOP:

Inter-déclarations de type fournir un moyen pour exprimer des préoccupations intersectorielles touchant à la structure des modules. Aussi connu comme l'ouverture des classes, ce permet aux programmeurs de se déclarer dans un placer les membres ou les parents de l'autre classe, généralement dans le but de combiner tout le code lié à une préoccupation dans l'un des aspects.

0voto

Sachin Points 566

Je vais utiliser des méthodes d'extension pour implémenter le comportement comme le montre le code.

  1. Laissez les objets Bird et Plane implémenter une propriété pour IFlyBehavior objet pour une interface IFlyer

     public interface IFlyer 
    {  
        public IFlyBehavior FlyBehavior 
    }
    
    public Bird : IFlyer
    {
        public IFlyBehaviour FlyBehavior {get;set;}
    }
    
    public Airplane : IFlyer
    {
        public IFlyBehaviour FlyBehavior {get;set;}
    }
     
  2. Créer une classe d'extension pour IFlyer

     public IFlyerExtensions
    {
    
        public void OnReadyToFly(this IFlyer flyer) 
        {
            flyer.FlyBehavior.Fly(); 
        }
    
        public void OnReadyToLand(this IFlyer flyer) 
        {
            flyer.FlyBehavior.Land(); 
        }
    }
     

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