Допустим, у меня есть три класса, Solid
, Face
, а также Edge
которые определены следующим образом:
class Solid{
public:
// perform an action on a single edge.
void addFillet(int edgeNum);
// perform an action on a single face
void addBore(int faceNum);
// perform an action on all faces and edges
void move(Pos newPosition);
private:
std::vector<Edge*> edges;
std::vector<Face*> faces;
};
class Face{
public:
// will modify a subset of edges
virtual void changeHeight(int newHeight) = 0;
private:
int myNum;
std::vector<Edge> edges;
}
class Edge{
public:
virtual void changeLength(int newLength) = 0;
private:
int myNum;
int length;
}
в этом примере Solid
управляет «надмножеством» Edge
s. каждый Face
тот Solid
управляет будет иметь «подмножество» Solid.edges
, Далее любые два Solid.faces
может иметь общее Edge
,
Мой вопрос: есть ли шаблоны проектирования или общие объектно-ориентированные принципы для решения подобных ситуаций? Как я могу управлять отношениями между Solid.edges
а также Face.edges
? Более конкретно
Существует много способов управления такими видами отношений, но если вы хотите повысить эффективность и хотите разделить вершины между ребрами и разделить ребра между гранями, тогда я предлагаю Solid
должен иметь полный список Vertex
а также Edges
,
затем Edge
имеет своего рода несобственную ссылку на свои вершины и Face
имеет своего рода несобственную ссылку на свои ребра. Эти не принадлежащие ссылки могут быть чем-то вроде указателя, но вы должны быть осторожны, чтобы не сделать эти указатели недействительными, перераспределяя основной список вершин или ребер. Безопаснее хранить индексы. Но это означает, что вы должны обратиться к Solid
чтобы узнать, к чему относится индекс вершины / ребра:
class Solid {
std::vector<Vertex> vertices;
std::vector<Edge> edges;
std::vector<Face> faces;
public:
Solid(std::vector<Vertex> vertices) : vertices(std::move(vertices)) {}
void addEdge(int vertex_index1, int vertex_index2) {
edges.emplace_back(vertex_index1, vertex_index2);
}
void addFace(std::vector<int> edge_indices) {
faces.emplace_back(std::move(edge_indices));
}
const Vertex &getVertex(int vertex_index) const { return vertices[vertex_index]; }
const Edge &getEdge(int edge_index) const { return edges[edge_index]; }
};
class Edge {
int vertex_first;
int vertex_second;
public:
Edge(int vertex_first, int vertex_second)
: vertex_first(vertex_first), vertex_second(vertex_second) {}
const Vertex &getVertexFirst(const Solid &solid) const {
return solid.getVertex(vertex_first);
}
const Vertex &getVertexSecond(const Solid &solid) const {
return solid.getVertex(vertex_second);
}
};
class Face {
std::vector<int> edge_indices;
int getEdgeIndex(int face_edge_index) const {
return edge_indices[face_edge_index];
}
public:
Face(std::vector<int> edge_indices) : edge_indices(std::move(edge_indices)) {}
const Edge &getEdge(int face_edge_index, const Solid &solid) const {
return solid.getEdge(getEdgeIndex(face_edge_index));
}
};
Альтернативой является использование std::shared_ptr
за Edge
а также Vertex
но тогда вам придется заплатить за динамическое распределение памяти и более плохую локальность данных.
Заманчиво хранить обратную ссылку на Solid
внутри Face
а также Edge
для лучшей инкапсуляции. Вы могли бы сделать это, но тогда vector
из Face
а также Edge
эффективно содержать много повторяющихся указателей. Если такой вид инкапсуляции важен для вас, я предлагаю вам создать классы-оболочки для работы с ребрами и гранями, которые содержат необработанный объект ребра / грани, а также обратную ссылку на Solid
,
Других решений пока нет …