Это подходящий способ для реализации функциональности ITaggable? - PullRequest
1 голос
/ 27 октября 2011

Я смотрю на добавление функциональности taggable в проект, который я взял на себя.Проект трехуровневый (mvc3 - Domain - Repositories).

Мне нужно добавить возможность помечать различные объекты в этой системе.Поскольку теги могут быть присоединены ко многим различным совокупным корням, я считаю, что лучше всего иметь теги в качестве собственного корня (rep / ITagManager в домене).

Моя идея заключалась в том, чтобы иметь интерфейс ITaggable, похожий на:*

public interface ITaggable
{
    bool SaveTags(IList<ITag> _tags);
    bool SaveTag(ITag _tag);
    IList<ITag> GetTags();
    bool HasTag(IList<ITag> _tags);
    bool HasTag(ITag _tag);
    bool HasTag(string _tagName);
}

У меня была идея создать ITagManager, в котором есть методы для получения объектов ITaggable и прикрепления к ним тегов сохранения / загрузки (возможно, для создания чего-то вроде String.Concat (typeof (this), this.ID)).уникальный идентификатор объекта, который реализует интерфейс ITaggable).Теперь есть два возможных маршрута: сначала передать любой объект, который реализует интерфейс ITaggable, в сам ITagManager или изменить интерфейс Itaggable примерно так:

public interface ITaggable
{
    bool SaveTags(IList<ITag> _tags, ITagManager _tagManager);
    bool SaveTag(ITag _tag, ITagManager _tagManager);
    IList<ITag> GetTags(ITagManager _tagManager);
    bool HasTag(IList<ITag> _tags, ITagManager _tagManager);
    bool HasTag(ITag _tag, ITagManager _tagManager);
    bool HasTag(string _tagName, ITagManager _tagManager);
}

Первое решение, возможно, пахнет анемичной моделью,но второе решение кажется грязным.Я знаю, что зависимость может быть введена, но я полагал, что наличие ее в качестве параметра функции сделает очевидным, что происходит.Или лучше ввести его в объект?

Подходит ли какое-либо из этих решений?

Буду признателен за любую помощь / совет.

Ответы [ 2 ]

5 голосов
/ 27 октября 2011

Я не думаю, что ваш «ITagable» интерфейс должен быть настолько раздутым.Кроме того, я бы не стал моделировать тег как объединенный корень, поскольку тег сам по себе не имеет смысла.

Вот интерфейс, который мы используем:

public interface ITagable
{
    IEnumerable<Tag> Tags { get; }
    void Tag(Tag tag);
    void Untag(Tag tag);
}

Многие изметоды, которые есть в вашем интерфейсе, могут быть легко реализованы как методы расширения, если вам нужно.

Иногда вы не можете обрабатывать всю логику в объектах вашего домена.Именно здесь доменные службы полезны, и именно это мы используем для обработки «обработки» тегов на объекте «ITagable»:

public interface ITagService
{
    void ProcessTags<TEntity>(TEntity entity, Func<IEnumerable<Tag>> featureTags, 
        string tagString) where TEntity : ITagable;
}

Обратите внимание, что мы передаем список «featureTags».В примере с блогом это будет список тегов для всего блога, поскольку мы не хотим создавать дубликаты тегов.Таким образом, «TagService» не выполняет никаких действий, он просто создает новые теги (в блоге), если это необходимо, и помечает тегами или снимает теги с объекта, если ему необходимо:

public class TagService : ITagService
{      
    public void ProcessTags<TEntity>(TEntity entity, 
        Func<IEnumerable<Tag>> featureTagsFactory, string tagString) where TEntity : ITagable
    {
        var result = new List<Tag>();
        // remove any leading/trailing spaces
        tagString = tagString.Trim();

        if (tagString.IsNotNullOrEmpty())
        {
            result.AddRange(from str in tagString.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Where(t => t.Length > 1).Distinct()
                            join tag in featureTagsFactory() on (Slug)str equals tag.Slug into tags
                            from tag in tags.DefaultIfEmpty()
                            select tag ?? new Tag(str.Trim()));
        }

        // merge tags
        foreach (var tag in entity.Tags.Except(result)) // remove unmatched tags
        {
            entity.Untag(tag);
        }
        foreach (var tag in result) // entity should check if already added
        {
            entity.Tag(tag);
        }
    }
}

Когда мы обновляем тегируемую сущность (обычно передавая разделенный запятыми список тегов из нашего уровня пользовательского интерфейса), мы делаем следующее:

// tags
if (command.TagString.IsNotNullOrEmpty())
{
    tagService.ProcessTags(post, () => blog.Tags, command.TagString);
}

В моем случае мы сопоставляем все теги с нашим родительским объектом блога.Вы, вероятно, не сможете просто скопировать + вставить этот код, но он должен дать вам представление о том, как вы можете обрабатывать теги в ваших сущностях.

1 голос
/ 05 ноября 2011

Почему бы не сделать тегирование своим собственным ограниченным контекстом?Я сомневаюсь, что теги должны соответствовать любому другому поведению в соответствующих агрегатах.

...