2010-01-07 5 views
5

Je veux être en mesure de définir certains objets et attacher certains "comportements" à cet objet où l'implémentation est dans le comportement pas dans l'objet. Rails-like: acts_as_taggable. À titre d'exemple concret, je veux dire que les tâches peuvent être étiquetées. Je ne veux pas avoir à coder quoi que ce soit dans Tâche sur les balises au-delà de "activer" le comportement via ... une interface? Là réside ma question. Vous ne pouvez pas mettre l'implémentation dans une interface. Je ne veux pas polluer ma classe BaseObject [abstract?] Avec tous des implémentations possibles.C# - Comment faire MULTIPLE "mixins" correctement avec des interfaces et/ou des classes abstraites

Objets: tâche, note

Behaviors:. Tagable, Emailable, imprimable, Deferrable (

Une tâche peut être étiquetée, envoyé par courrier électronique, imprimé et reporté Une note peut être étiquetée, envoyé par courrier électronique, imprimé , mais pas différé.

BaseObject

public class BaseObject 
{ 
    Guid ID { get; set; } 
} 

tag.cs ​​

public class Tag : BaseObject 
{ 
    public Guid Id { get; set; } 
    public String Title { get; set; } 
} 

itaggable.cs

public interface ITaggable 
{ 
    void AddTag(Tag tag); 
    ... other Tag methods ... 
} 

task.cs

public class Task : BaseObject, ITaggable, IEmailable, IPrintable 
{ 
    Task specified functionality... nothing about "taggging" 
} 

note.cs

...

TagCollection.cs

public class TagCollection : List<Tag> 
{ 
    public string[] ToStringArray() 
    { 
     string[] s = new string[this.Count]; 
     for (int i = 0; i < this.Count; i++) 
      s[i] = this[i].TagName; 
     return s; 
    } 

    public override string ToString() 
    { 
     return String.Join(",", this.ToStringArray()); 
    } 

    public void Add(string tagName) 
    { 
     this.Add(new Tag(tagName)); 
    } 

La mise en œuvre de ITaggable ressemble à quelque chose comme

{ 
    private TagCollection _tc; 
    private TagCollection tc 
    { 
     get 
     { 
      if (null == _tc) 
      { 
       _tc = new TagCollection(); 
      } 
      return _tc; 
     } 
     set { _tc = value; } 
    } 

    public void AddTag(Tag tag) 
    { 
     tc.Add(tag); 
    } 

    public void AddTags(TagCollection tags) 
    { 
     tc.AddRange(tags); 
    } 

    public TagCollection GetTags() 
    { 
     return tc; 
    } 
} 

Alors, quelle est la bonne/meilleure façon de le faire?

Jason

Répondre

0

Malheureusement, vous allez devoir ajouter du code à vos classes, soit la classe de base ou le descendant. Cependant, vous pouvez vous en passer lorsque la classe "taggable" est un membre privé, et simplement passer tous les appels de méthode à cet objet membre, mais vous devez toujours implémenter l'interface.

Quelque chose comme ceci:

interface ITaggable { 
    void AddTag(String tag); 
} 

class TaggableImplementation : ITaggable { 
    private Hashset<String> tags = new Hashset<String>(); 
    public void AddTag(String tag) { tags.Add(tag); } 
} 

class TaggableClass : ITaggable { 
    private ITaggable taggable = new TaggableImplementation(); 

    public void AddTag(String tag) { taggable.AddTag(tag); } 
} 
2

Eh bien il y a beaucoup de façons, selon la façon dont vous souhaitez mettre en œuvre.Dans votre exemple, il pourrait être préférable de faire quelque chose le long des lignes de ce:

public interface IBehavior 
{ 
    ... common behavior methods like maybe 
    bool Execute(object value) 
} 

public class Taggable : IBehavior 
{ 
    ... tag specific items 
    public bool Execute(object value) { ... } 
} 

public class Note 
{ 
    public List<IBehavior> Behaviors { get; set; } 
    public void ProcessNote() 
    { 
     this.Behaviors(d=>d.Execute(this)); 
    } 
} 

Cette variation vous permettra de toujours ajouter plus de comportements sans avoir à faire des changements drastiques à vos structures de classe soit comme la mise en œuvre d'une interface sur chaque classe que vous voulez soutenir le nouveau comportement. Vous voudrez peut-être trouver ce que votre objet commun serait dans votre classe de comportement pour le rendre plus facile à utiliser pour votre scénario. Vous pouvez donc utiliser des génériques pour vous permettre de faire une définition plus typée.

Vous voudrez peut-être aussi regarder le motif Décorateur pour vous donner encore plus d'idées.

+0

Intéressant, je vais devoir penser à celui-ci un peu plus. – user166255

0

J'ai implémenté des mixins en C# en utilisant des classes partielles, et en copiant du code source à partir d'un fichier modèle dans des fichiers source de classe partielle. Si vous acceptez quelques limitations, cela fonctionne plutôt bien. J'ai essayé le pré-processeur T4 pour générer automatiquement les fichiers sources, mais c'était un peu trop encombrant, j'ai donc écrit un outil assez minimaliste pour copier du code à partir de fichiers modèles beaucoup plus faciles à générer que des scripts T4. Avec un peu de soin, vous pouvez même utiliser Intellisense lors de l'écriture de votre modèle.

Pour l'outil, jetez un oeil à:

http://myxin.codeplex.com/

Pour un exemple, jetez un oeil au code source pour Streambolics.Gui à:

http://streambolicscore.codeplex.com/

+0

Nice, je vais vous montrer votre approche. ;-) – herzmeister

+0

Est-ce que c'est plus joli que T4. – user166255

Questions connexes