c # - Как правильно сделать НЕСКОЛЬКО «миксинов» с интерфейсами и / или абстрактными классами - PullRequest
5 голосов
/ 07 января 2010

Я хочу иметь возможность определять некоторые объекты и прикреплять некоторые «поведения» к тому объекту, где реализация находится в поведении, а не в объекте. Похожий на Rails: acts_as_taggable. В качестве конкретного примера я хочу сказать, что задачи могут быть помечены. Я не хочу кодировать что-либо в «Задаче о тегах», кроме «включения» поведения через ... интерфейс? В этом и заключается мой вопрос. Вы не можете поместить реализацию в интерфейс. Я не хочу загрязнять мой класс BaseObject [abstract?] С всеми из возможных реализаций.

Объекты: Задача, Примечание

Поведения: Taggable, по электронной почте, для печати, отложенный (

Задача может быть помечена, отправлена ​​по электронной почте, распечатана и отложена. Примечание может быть помечено, отправлено по электронной почте, напечатано, но не отложено.

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 ]

2 голосов
/ 07 января 2010

Ну, есть много способов, в зависимости от того, как вы хотите реализовать. В вашем примере может быть лучше сделать что-то вроде этого:

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, чтобы дать вам еще больше идей.

0 голосов
/ 09 января 2010
0 голосов
/ 09 января 2010

Чтобы создать миксины, используя встроенную технику генерации кода T4 в Visual Studio, вы можете взглянуть на http://code.logos.com/blog/2009/04/creating_mixins_with_t4_in_visual_studio.html

0 голосов
/ 09 января 2010

Я реализовал миксины в C #, используя частичные классы и копируя исходный код из файла шаблона в частичные исходные файлы классов.Если вы принимаете несколько ограничений, это работает довольно хорошо.Я попробовал препроцессор T4 для автоматической генерации исходных файлов, но это было слишком громоздко, поэтому я написал - довольно минималистичный - инструмент для копирования кода из файлов шаблонов, которые гораздо проще генерировать, чем сценарии T4.С небольшой осторожностью вы даже можете использовать Intellisense при написании шаблона.

Для этого инструмента взгляните на:

http://myxin.codeplex.com/

Например,Посмотрите на исходный код Streambolics.Gui по адресу:

http://streambolicscore.codeplex.com/

0 голосов
/ 07 января 2010

К сожалению, вам придется добавить некоторый код в ваши классы, либо базовый класс, либо потомок.

Однако вы можете обойтись без класса «taggable», являющегося закрытым членом, и просто передать все вызовы методов этому объекту-члену, но вам все равно нужно реализовать интерфейс.

Примерно так:

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); }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...