문제

I am creating an implementation of the half-edge data structure in C#. The details of this structure is not relevant for my question, so I will introduce it only as deep as required, but a brief summary is available on flipcode if someone interested.

My object-oriented representation of the data structure consists 4 classes: Halfedge, Edge, Vertex and Face, which mutually contain some references to each other. (Specifically: every halfedge references a vertex, while every edge, vertex and face reference a halfedge.) Handling and maintaining this low level representation is a hard task, therefore the modification of it should not be exposed to the users of the code, to guarantee the consistency of the data structure.

In order to achieve this, I created a 5th Graph class to provide a higher abstraction level layer above them. The idea is that the users of the code/library only have to communicate with a Graph instance to build or query the topological model, and do not have to deal with the low level relations of halfedges, edges, vertices and faces; therefore they cannot corrupt it.

Now to the problem. The faces, vertices, edges and halfedges are required to be queryable from the Graph. The half-edge data structure contains topological information about the data, so it is a resource-efficient query to ask for e.g. the adjacent faces for the given face or edge. A sample for how these classes are meant to be used:

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;

For obvious purposes a Graph object needs to manipulate the inner state of the contained Halfedge, Edge, Vertex and Face objects, to build up and maintain the data structure from the input given by the user. However, if these 4 classes have public methods (or properties) to alter their state, then the whole idea of hiding the representation from the user is spoiled, since they can be directly called by the user. (It's a pity C# does not have the friend class conception now.)

If I make the Halfedge, Edge, Vertex and Face classed nested inside Graph and do not make them public, then it would be impossible to retrieve these types for the user. E.g.:

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 */
    }
}

So basically my question is what design would be best to follow to implement a data structure like this. The Halfedge, Edge, Vertex and Face classes need to expose an interface to modify them for the Graph class, but not for others. Internal accessibility is not good, since it would expose these functions for the whole assembly.

Shall I create "read-only" wrapper classes for them, as many collections in .NET have a read-only wrapper (like List<T>), and expose only those for the users when returning an object (with e.g. AddFace())?

도움이 되었습니까?

해결책

Why not use an interface for every type and expose them to outside world:

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 */
    }
}

This way you can hide your classes inside of Graph

다른 팁

Have you considered keeping your classes private, but providing public interfaces which only expose what you want to? This has the advantage of implicit implementation (you shouldn't have to write lots of extra code, beyond the interface definition itself, if you keep all the member names the same). Also, remember a read-write property in a class can implicitly implement a read-only property in an interface.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top