Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c# - How to do MULTIPLE "mixins" correctly with Interfaces and/or Abstract Classes

I want to be able to define some objects and attach some "behaviors" to that object where the implementation is in the behavior not in the object. Rails-like: acts_as_taggable. As a concrete example, I want to say that Tasks can be Tagged. I don't want to have to code anything in Task about Tags beyond "enabling" the behavior via ... an interface? Therein lies my question. You can't put the implementation in an interface. I don't want to pollute my BaseObject [abstract?] class with all of the possible implementations.

Objects: Task, Note

Behaviors: Taggable, Emailable, Printable, Deferrable (

A Task may be tagged, emailed, printed, and deferred. A Note may be tagged, emailed, printed, but not deferred.

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));
    }

Implementation of ITaggable looks something like

{
    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;
    }
}

So what's the correct/best way to do this?

Jason

like image 446
user166255 Avatar asked Nov 05 '22 17:11

user166255


1 Answers

Well there are lots of ways depending on how you want to implement. In your example it might be better to do something along the lines of this:

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));
   }
}

This variation would allow you to always add more behaviors without having to do any drastic changes to your class structures either like implementing an interface on every class that you want to support the new behavior. You may want to come up with what your common object would be amongst your behavior class to make it easier to use for your scenario though. So you could use generics to allow you to do a more typed definition.

You might want to look at the Decorator pattern as well to give you even more ideas.

like image 111
Joshua Cauble Avatar answered Nov 12 '22 21:11

Joshua Cauble