-1

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())?

mcserep
  • 3,231
  • 21
  • 36
  • 1
    I don't quite understand why do say `Internal` is not good. If this is a library all of this classes will be in separate dll so actually the `Internal` is what you want – Dzior May 06 '14 at 14:13
  • 1
    Why is exposing to the assembly bad? Do you not trust your coworkers? – Eric Lippert May 06 '14 at 14:14
  • C# does have friend classes, via the internal modifier. – Eric Lippert May 06 '14 at 14:15
  • You might consider making the whole thing immutable throughout. Adding an edge oroduces a new graph rather than mutating a graph. – Eric Lippert May 06 '14 at 14:16
  • You say you need to expose an interface, so consider... Wait for it... Using an interface. :) – Eric Lippert May 06 '14 at 14:17
  • @Dzior: `internal` would do as a last resort, but it is really against encapsulation, much more like `friend` in C++. Therefore I would only use it if there is no better solution. – mcserep May 06 '14 at 14:22
  • @EricLippert: for `internal`, see my previous comment. As for creating an interface, yeah... maybe I had the answer in the back of my mind when finishing composing my question :D – mcserep May 06 '14 at 14:24
  • 1
    You might be suffering from Object Happiness Disease, in which developers believe that OO principles like "encapsulation" are good ends in themselves, rather than means to an end. Encapsulation is there to lower your costs. If enforcing some "OO purity" principle is increasing your costs then *the principle is no longer serving your interests*, so feel free to ignore it. – Eric Lippert May 06 '14 at 15:56
  • My guidelines are: (1) if you're not going to have three or more implementations of an interface then its not a good candidate for an interface, and (2) classes that need to know each other's details should use `internal`. – Eric Lippert May 06 '14 at 16:01
  • @EricLippert I have to disagree with you. Using a principle or design pattern does not always serve for lowering your costs immediately. Instead they can make your code more maintainable, so in the long run they will lower the development costs. In an object oriented environment, breaking the rule of encapsulation - when not absolutely required - is a bad decision, because it will always make your code harder to comprehend and maintain. Internal access modifier is a typical example for that, because it does not only go against encapsulation on the needed level, but in a much wider aspect. – mcserep May 06 '14 at 18:18
  • Also, most of .NET itself does not compile to your (1) guideline in using interfaces. – mcserep May 06 '14 at 18:20

2 Answers2

2

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

Alessandro D'Andria
  • 8,663
  • 2
  • 36
  • 32
1

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.

Keith Howie
  • 121
  • 3