C# – What pattern for implementing multiple interfaces on the same type

cdata structuresdesign-patternsinterfaceslegacy

Description of the environment:

  • I am implementing a (hobbyist) modern engine for (an old) PlayStation 1 video game
  • Graphical data is represented as packets where it can represent either a polygon or a sprite,
    (and they are transformed differently to end up as pixels on screen)
  • All those differents type of packets (18 in total) can in fact be reduced to 2 or 3 types at most (what I've done to reduce code surface), with some flags indicating what's present in them

For information purposes,

This format is more or less a variant of the HMD format by Sony, an efficient low-level format that gets consumed directly by the PlayStation 1 GPU, at the expense of being unfriendly to read/parse, etc … (actually on the real PlayStation 1, one would emit few system library calls to load it and that was it)

Description of the problem:

  • I need to find a (simple yet effective) way of realizing their content
  • accounting that they can either be a polygon or a sprite

Long story short,

The following code snippets below probably explains better what's the content and the preliminary approach,

Interfaces that we'll work against:

/// <summary>
///     a graphics primitive.
/// </summary>
public interface IPrimitive
{
    bool IsPolygon { get; }
    bool IsSprite { get; }
}

/// <summary>
///     a polygon, i.e. triangle, quad.
/// </summary>
public interface IPolygon : IPrimitive
{
    void GetMesh();
}

/// <summary>
///     a sprite, (internal representation has different meaning than a polygon).
/// </summary>
public interface ISprite : IPrimitive
{
    void GetMesh();
}

Type present in game data:

/// <summary>
///     a packet representing graphical data : a polygon or a sprite.
/// </summary>
public class GraphicsPacket : IPolygon, ISprite
{
    public GraphicsPacket(Stream stream)
    {
        // read packet ...


        // there will be a flag indicating if it's a sprite
        // and therefore should be treated differently
    }

    public bool IsSprite { get; }

    public bool IsPolygon => !IsSprite;

    void IPolygon.GetMesh()
    {
        if (IsSprite)
            throw new InvalidOperationException("Instance is not a polygon");
    }

    void ISprite.GetMesh()
    {
        if (IsPolygon)
            throw new InvalidOperationException("Instance is not a sprite");
    }
}

Test of that whole logic:

public class Test
{
    public void BuildPrimitive([NotNull] IPrimitive primitive)
    {
        if (primitive == null)
            throw new ArgumentNullException(nameof(primitive));

        // obviously here, 'as' cast would always be true for all cases

        var polygon = primitive.IsPolygon;
        if (polygon)
        {
            // do something with it
            ((IPolygon)polygon).GetMesh();

            return;
        }

        var sprite = primitive.IsSprite;
        if (sprite)
        {
            // do something with it
            ((ISprite)sprite).GetMesh();

            return;
        }

        throw new NotSupportedException(nameof(primitive));
    }
}

Question:

Is my approach for treating a graphics packet depending its content a good one ?

i.e.

  • explicit interface implementation
  • naive bool check and throw on invalid state

Strong arguments about considering splitting the concerns in such case,

  • game data is just like that, why change the logic ?
  • (my) modern, object-oriented approach, summarized this as few types (i.e. DRY concept)
  • problem won't really vanish, as those graphics packet will always be either a polygon or a sprite (i.e. why fight against that logic after all ?)

Hope that makes sense to you, otherwise let me know how I can improve the question.

Best Answer

I believe you only need one interface that classes for Polygon and Sprite implement. Based on what I see I would make an interface for GraphicsPacket and create Polygon and Sprite classes that implement it's methods.

Then throughout your code you just write it to use GraphicsPacket objects and you don't need to decide numerous times whether it's a Polygon or Sprite.

public interface IGraphicsPacket
{
    public GraphicsPacket(Stream stream);

    public GetMesh()
}

public class  Polygon : IGraphicsPacket
{
    public GraphicsPacket(Stream stream)
    {
        //Polygon implementation
    }

    public GetMesh()
    {
        //Polygon implementation
    }
}

public class  Sprite : IGraphicsPacket
{
    public GraphicsPacket(Stream stream)
    {
        //Sprite implementation
    }

    public GetMesh()
    {
        //Sprite implementation
    }
}

TEST

public class Test
{
    public void BuildPrimitive([NotNull] IGraphicsPacket primitive)
    {
        if (primitive == null)
            throw new ArgumentNullException(nameof(primitive));

        primitive.GetMesh();

        return;
    }
}