astef
astef

Reputation: 9508

Correct way to encapsulate through generic interfaces

My application consist of server and client, which are independant. They communicate through objects created and modified by server. Client is provided with read-only interfaces of this objects. As far as I know, that's the correct way to keep encapsulation in OOP. See example:

// Client-side

interface IBox<T> where T : ITool
{
    IEnumerable<T> Tools { get; }
}

interface ITool
{
    void Use();
}

// Server-side

class Box : IBox<Tool>
{
    public List<Tool> ToolList = new List<Tool>();

    public IEnumerable<ITool> Tools
    {
        get { return ToolList; }
    }
}

class Tool : ITool
{
    string _msg = "default msg";
    public string Msg
    {
        get { return _msg; }
        set { _msg = value; }
    }

    public void Use()
    {
        Console.WriteLine("Tool used! Msg: {0}", _msg);
    }
}

As you see, I have to use generics, because my objects form a hierarchy.

That looked nice, until I've decided to add a Room class with interface IRoom, which have to generalize not only IBox, but ITool too:

interface IRoom<B, T>
    where B : IBox<T>
    where T : ITool
{
    IEnumerable<B> Boxes { get; }
}

class Room : IRoom<Box, Tool>
{
    public List<Box> BoxList = new List<Box>();

    public IEnumerable<Box> Boxes
    {
        get { return BoxList; }
    }
}

Now, imagine that we have a Room consist of not only boxes. I need at least 3 collections of absolutely different things there, which are collections of several types too. So, there must be a huge tree, and my root class become something like: Room : IRoom<Box, Tool1, Tool2, Tool3, Wardrobe, Coat, Jeans, Hat, Table, Computer, Book, Pen>

I'm not sure, that is right. So, I'm asking, what is true OOP-way of implementing my task? (with no reflection, breaking encapsulation, type casting or other bad tricks)

Upvotes: 2

Views: 2070

Answers (1)

Daniel M&#246;ller
Daniel M&#246;ller

Reputation: 86630

Starting with the .NET Framework 4 and C# 4 you can use IEnumerable's covariance and just avoid using generics.

// Client-side

interface IBox
{
    IEnumerable<ITool> Tools { get; }
}

interface ITool
{
    void Use();
}

// Server-side

class Box : IBox
{
    public List<Tool> ToolList = new List<Tool>();

    public IEnumerable<ITool> Tools
    {
        get { return ToolList; } // With .NET 3.5 and earlier cast here is neccessary to compile
        // Cast to interfaces shouldn't be so much of a performance penalty, I believe.
    }
}

class Tool : ITool
{
    string _msg = "default msg";
    public string Msg
    {
        get { return _msg; }
        set { _msg = value; }
    }

    public void Use()
    {
        Console.WriteLine("Tool used! Msg: {0}", _msg);
    }
}


interface IRoom
{
    IEnumerable<IBox> Boxes { get; }
}

class Room : IRoom
{
    public List<Box> BoxList = new List<Box>();

    public IEnumerable<IBox> Boxes
    {
        get { return BoxList; } // and here...
    }
}

Covariance and contravariance in generics described here: http://msdn.microsoft.com/en-us/library/dd799517.aspx

Upvotes: 2

Related Questions