В поисках лучшего дизайна: механизм кэширования в памяти только для чтения - PullRequest
3 голосов
/ 12 мая 2010

У меня есть объект Category (класс), у которого есть ноль или одна родительская категория и много дочерних категорий - это древовидная структура. Данные категории хранятся в СУБД, поэтому для повышения производительности я хочу загрузить все категории и кэшировать их в памяти при запуске приложения.

В нашей системе могут быть подключаемые модули, и мы разрешаем авторам подключаемых модулей получать доступ к Дереву категорий, но они не должны изменять кэшированные элементы и дерево (я думаю, что дизайн, не предназначенный только для чтения, может вызвать некоторые незначительные ошибки в этом senario) только система знает, когда и как обновить дерево.

Вот несколько демонстрационных кодов:

public interface ITreeNode<T>
    where T : ITreeNode<T>
{
    // No setter
    T Parent { get; }
    IEnumerable<T> ChildNodes { get; }
}

// This class is generated by O/R Mapping tool (e.g. Entity Framework)
public class Category : EntityObject
{
    public string Name { get; set; }
}

// Because Category is not stateless, so I create a cleaner view class for Category.
// And this class is the Node Type of the Category Tree
public class CategoryView : ITreeNode<CategoryView>
{
    public string Name { get; private set; }

    #region ITreeNode Memebers

    public CategoryView Parent { get; private set; }

    private List<CategoryView> _childNodes;
    public IEnumerable<CategoryView> ChildNodes {
        return _childNodes;
    }

    #endregion

    public static CategoryView CreateFrom(Category category) {
        // here I can set the CategoryView.Name property
    }
}

Пока все хорошо. Однако я хочу сделать интерфейс ITreeNode повторно используемым, а для некоторых других типов дерево не должно быть доступно только для чтения . Мы не можем сделать это с указанным выше ITreeNode только для чтения, поэтому я хочу, чтобы ITreeNode был таким:

public interface ITreeNode<T> {
    // has setter
    T Parent { get; set; }
    // use ICollection<T> instead of IEnumerable<T>
    ICollection<T> ChildNodes { get; }
}

Но если мы сделаем ITreeNode доступным для записи, то мы не сможем сделать дерево категорий доступным только для чтения , это нехорошо.

Так что я думаю, что если мы сможем сделать так:

public interface ITreeNode<T> {
    T Parent { get; }
    IEnumerable<T> ChildNodes { get; }
}

public interface IWritableTreeNode<T> : ITreeNode<T> {
    new T Parent { get; set; }
    new ICollection<T> ChildNodes { get; }
}

Это хорошо или плохо? Есть ли лучшие дизайны? Большое спасибо! :)

1 Ответ

1 голос
/ 13 мая 2010

Одна вещь, которую вы можете попробовать - это использовать Список для своих элементов IEnumerable, которые вы хотите только для чтения. Затем, когда вы заполняете свою древовидную структуру, вы можете внутренне вызвать метод AsReadOnly () в вашем списке, который вернет ReadOnlyCollection , и потребители ваших данных не возможность изменять содержимое коллекции.

Этот подход не ReadOnly с точки зрения интерфейса, но попытка вызвать метод, подобный Добавление в коллекцию, потерпит неудачу и вызовет исключение.

Чтобы защитить других участников, вы можете создать собственный закрытый флаг только для чтения в классе реализации ITreeNode внутри класса, а затем установить свой флаг только для чтения на кэшированных элементах.

Как то так ...


public class TreeNode : ITreeNode
{
    private bool _isReadOnly;
    private List<ITreeNode> _childNodes = new List<ITreeNode>();

    public TreeNode Parent { get; private set; }

    public IEnumerable<ITreeNode> ChildNodes
    {
        get
        {
            return _isReadOnly ? _childNodes.AsReadOnly() : _childNodes;
        }
    }
}
...