Как сделать объект, чтобы обеспечить изменение функциональности только для определенных пользователей в C#?
Я создаю реализацию структуры данных half-edge в C#. Детали этой структуры не имеют отношения к моему вопросу, поэтому я представлю ее настолько глубоко, насколько это необходимо, но краткое резюме доступно по флип-коду, если кто-то заинтересован.
Мое объектно-ориентированное представление структуры данных состоит из 4 классов: Halfedge
, Edge
, Vertex
а также Face
, которые взаимно содержат некоторые ссылки друг на друга. (В частности: каждый половинку ссылается на вершину, в то время как каждое ребро, вершина и грань ссылаются на половину.) Обработка и поддержание этого низкоуровневого представления - сложная задача, поэтому его изменение не должно быть доступно пользователям кода, чтобы гарантировать согласованность структуры данных.
Чтобы добиться этого, я создал 5-й Graph
класс для обеспечения более высокого уровня абстракции над ними. Идея состоит в том, что пользователи кода / библиотеки должны общаться только с Graph
экземпляр для построения или запроса топологической модели, и не нужно иметь дело с низкоуровневыми отношениями половин, ребер, вершин и граней; поэтому они не могут испортить это.
Теперь к проблеме. Грани, вершины, ребра и половинки должны запрашиваться из Graph
, Структура данных с половиной ребра содержит топологическую информацию о данных, поэтому это ресурсоэффективный запрос, например, для запроса смежных граней для данной грани или ребра. Пример того, как эти классы предназначены для использования:
Graph graph = new Graph();
Face face = graph.AddFace(/* params about the vertexes of the face to create */);
// More data given to the graph ...
// Later:
Face[] faces = face.AdjacentFaces;
Vertex[] vertixes = face.BoundaryVertices;
Для очевидных целей Graph
объект должен манипулировать внутренним состоянием содержимого Halfedge
, Edge
, Vertex
а также Face
объекты, чтобы построить и поддерживать структуру данных из ввода данных, предоставленных пользователем. Однако, если эти 4 класса имеют public
методы (или свойства), чтобы изменить их состояние, тогда вся идея скрытия представления от пользователя испорчена, так как они могут быть вызваны непосредственно пользователем. (Жаль, что в C# сейчас нет концепции класса друзей.)
Если я сделаю Halfedge
, Edge
, Vertex
а также Face
классифицируется вложенным внутри Graph
и не делайте их общедоступными, тогда было бы невозможно получить эти типы для пользователя. Например:
public class Graph
{
private class Halfedge { /* ... */ }
private class Edge { /* ... */ }
private class Vertex { /* ... */ }
private class Face { /* ... */ }
// Cannot return a Face, since it is not public.
public Face AddFace(/* params about the vertexes of the face to create */)
{
/* Algorithm to create halfegdes, edges, vertices and a face */
}
}
Поэтому в основном мой вопрос заключается в том, какой дизайн лучше использовать для реализации такой структуры данных. Halfedge
, Edge
, Vertex
а также Face
классы должны предоставлять интерфейс для изменения их для Graph
класс, но не для других. Internal
доступность не очень хорошая, так как это предоставит эти функции для всей сборки.
Должен ли я создать классы-оболочки "только для чтения", так как многие коллекции в.NET имеют оболочку только для чтения (например, List<T>
) и отображать только те, которые доступны пользователям при возврате объекта (например, AddFace()
)?
2 ответа
Почему бы не использовать интерфейс для каждого типа и показать их внешнему миру:
public interface IHalfedge
{
}
public interface IEdge
{
}
public interface IVertex
{
}
public interface IFace
{
}
public class Graph
{
private class Halfedge : IHalfedge
{ /* ... */
}
private class Edge : IEdge
{ /* ... */
}
private class Vertex : IVertex
{ /* ... */
}
private class Face : IFace
{/* ... */
}
// Cannot return a Face, since it is not public.
public IFace AddFace(/* params about the vertexes of the face to create */)
{
/* Algorithm to create halfegdes, edges, vertices and a face */
}
}
Таким образом, вы можете скрыть свои классы внутри Graph
Рассматривали ли вы, чтобы ваши классы были приватными, но предоставляющими общедоступные интерфейсы, представляющие только то, что вы хотите? Это имеет преимущество неявной реализации (вам не нужно писать много дополнительного кода, кроме самого определения интерфейса, если вы сохраняете все имена членов одинаковыми). Кроме того, помните, что свойство чтения-записи в классе может неявно реализовывать свойство только для чтения в интерфейсе.