В поисках лучшего дизайна: механизм кэширования в памяти только для чтения
У меня есть объект категории (класс), который имеет ноль или одну родительскую категорию и много дочерних категорий - это древовидная структура. Данные категорий хранятся в СУБД, поэтому для повышения производительности я хочу загрузить все категории и кэшировать их в памяти при запуске приложения.
Наша система может иметь плагины, и мы разрешаем авторам плагинов получать доступ к дереву категорий, но они не должны изменять кэшированные элементы и дерево (я думаю, что дизайн, не предназначенный только для чтения, может вызвать некоторые незначительные ошибки в этом 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 ответ
Одна вещь, которую вы можете попробовать, это использовать List
Этот подход не является ReadOnly с точки зрения интерфейса, но попытка вызвать метод, подобный Add для коллекции, потерпит неудачу и вызовет исключение.
Чтобы защитить других членов, вы можете создать собственный закрытый флаг только для чтения в классе реализации 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;
}
}
}