c# - 如何使用接口和/或抽象类正确地执行MULTIPLE“mixins”

时间:2010-01-07 20:37:49

标签: c# c#-3.0 interface abstract-class

我希望能够定义一些对象并将一些“行为”附加到该对象,其中实现所处的行为不在对象中。 Rails-like:acts_as_taggable。作为一个具体的例子,我想说任务可以标记。我不想在任务中编写任何代码,除了通过......界面“启用”行为之外?这就是我的问题。您不能将实现放在接口中。我不想污染我的BaseObject [abstract?]类与所有的可能实现。

对象:任务,注意

行为:可标记,可扩展,可打印,可延迟(

可以对任务进行标记,通过电子邮件发送,打印和延期。 注释可以被标记,通过电子邮件发送,打印,但不能延期。

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

ITaggable的实现类似于

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

那么正确/最好的方法是什么?

杰森

5 个答案:

答案 0 :(得分:2)

根据您的实施方式,有很多方法。在你的例子中,最好按照以下方式做一些事情:

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

此变体允许您始终添加更多行为,而无需对类结构进行任何重大更改,例如在要支持新行为的每个类上实现接口。您可能希望在行为类中提出您的常见对象,以便更轻松地使用您的场景。因此,您可以使用泛型来进行更类型的定义。

你可能也想看看Decorator模式,为你提供更多的想法。

答案 1 :(得分:0)

不幸的是,你将不得不在你的类中添加一些代码,无论是基类还是后代。

但是,您可以将“可标记”类作为私有成员,并将所有方法调用传递给该成员对象,但仍需要实现该接口。

这样的事情:

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 :(得分:0)

我通过使用部分类在C#中实现了mixins,并将源代码从模板文件复制到部分类源文件中。如果您接受一些限制,它可以很好地工作。我尝试使用T4预处理器自动生成源文件,但它有点过于繁琐,所以我编写了一个非常简约的工具来复制模板文件中的代码,这些文件比T4脚本更容易生成。稍加注意,您甚至可以在编写模板时使用Intellisense。

对于该工具,请查看:

http://myxin.codeplex.com/

例如,请查看Streambolics.Gui的源代码:

http://streambolicscore.codeplex.com/

答案 3 :(得分:0)

要使用Visual Studio内置的T4代码生成技术创建mixins,您可以查看http://code.logos.com/blog/2009/04/creating_mixins_with_t4_in_visual_studio.html

答案 4 :(得分:0)