Kian
Kian

Reputation: 65

Implementing factory pattern with different return types

I'm trying to implement a factory pattern and I ran into a problem. I try to make my classes simple here. Basically I have a base Packet class (PacketHeader) with some fields and methods. Also I have so many derived packet classes such as: InfoPacket1011, UsagePacket1011, InfoPacket1014, UsagePacket1014 and they all inherit from PacketHeader base class.

As you can see each packet has a version and my goal is to handle this packets based on their versions. So I should have two derived class, one for 1011 and one for 1014.

The base class (which itself is a derived class!) looks like this:

public abstract class PacketHandlerBase : Engine
{
    public abstract bool SendInfoPacket(int someInt, string someInput);
    public abstract List<???> BuildInfoPacket(string someInput);

    public abstract bool SendUsagePacket(int someInt, string someInput);
    public abstract List<???> BuildUsagePacket(string someInput);
    //...
    //...
    //...
}

My problem is that for methods such as BuildInfoPacket and BuildUsagePacket I have to return a List of that type. So in the derived classes I could have:

public class PacketHandler1011 : PackerHandlerBase
{
    //...
    public override bool SendInfoPacket(int someInt, string someInput);
    {
        // code implementation
        // return true or false
    }

    public override List<InfoPacket1011> BuildInfoPacket(string someInput);
    {
        // code implementation
        // return List<InfoPacket1011>
    }
}

public class PacketHandler1014 : PackerHandlerBase
{
    //...
    public override bool SendInfoPacket(int someInt, string someInput);
    {
        // code implementation
        // return true or false
    }

    public override List<InfoPacket1014> BuildInfoPacket(string someInput);
    {
        // code implementation
        // return List<InfoPacket1014>
    }
}

I don't know what to use in the PacketHandlerBase class to be able to override it in the derived classes. I guess I need generic methods and interface for that, but not sure how to handle that.

[Edit]: I fixed first part of my questions about packet inheritance. Thank you all for your answers, I read them and tell you if they work.

[Answer]: Thank you everyone for your prompt responses. I fixed the problem by passing List and by casting it in the method and in the caller. Well my code was much more complex than what I provided here and I just finished modifying it. I changed the pattern to abstract factory too to fix some other problems.

Any help would be greatly appreciated. Thanks in advance

Upvotes: 4

Views: 6289

Answers (5)

Martin Eden
Martin Eden

Reputation: 6262

How about:

public abstract class PacketHandlerBase<TInfoPacket, TUsagePacket> : Engine
    where TInfoPacket : IInfoPacket
    where TUsagePacket : IUsagePacket
{
    public abstract bool SendInfoPacket(int someInt, string someInput);
    public abstract List<TInfoPacket> BuildInfoPacket(string someInput);

    public abstract bool SendUsagePacket(int someInt, string someInput);
    public abstract List<TUsagePacket > BuildUsagePacket(string someInput);
}

You make sure that your info packet classes implement an IInfoPacket interface. For example:

public InfoPacket1101 : PacketHeader, IInfoPacket
{
    ...
}

Similarly all the usage packet classes implement IUsagePacket. Then you can write a given version of your packet handler like so:

public class PacketHandler1011 : PackerHandlerBase<InfoPacket1101, UsagePacket1101>
{
    ...
}

I think this is the preferred solution, as it means you can have stronger guarantees about the returned objects. With only a single base class, as in the currently accepted answer, you can't call any "info packet"-specific methods on objects returned by BuildInfoPacket without casting. Similarly with usage packets. In this solution the IInfoPacket interface could have methods that you are then able to call without casting.

Upvotes: 0

Euphoric
Euphoric

Reputation: 12849

Do not use generics if you don't understand them and are fully aware of their downsides. In this case, you can just return IEnumerable<PacketHeader> and have the polymorphism resolve rest.

public abstract class PacketHandlerBase : Engine
{
    public abstract bool SendInfoPacket(int someInt, string someInput);
    public abstract IEnumerable<PacketHeader> BuildInfoPacket(string someInput);

    public abstract bool SendUsagePacket(int someInt, string someInput);
    public abstract IEnumerable<PacketHeader> BuildUsagePacket(string someInput);
    //...
    //...
    //...
}

public class PacketHandler1011 : PackerHandlerBase
{
    //...
    public override bool SendInfoPacket(int someInt, string someInput);
    {
        // code implementation
        // return true or false
    }

    public override IEnumerable<PacketHeader> BuildInfoPacket(string someInput);
    {
        yield return new InfoPacket1011(..)
        // code implementation
        // return List<InfoPacket1011>
    }
}

Upvotes: 0

BRAHIM Kamel
BRAHIM Kamel

Reputation: 13755

all your method in the base class should return InfoPacketBase as well as creating a base class for packet usage e.g UsagePacketBase you need to take a look at polymorphism

public abstract class PacketHandlerBase : Engine
{
    public abstract bool SendInfoPacket(int someInt, string someInput);
    public abstract List<PackeHeader> BuildInfoPacket(string someInput);

    public abstract bool SendUsagePacket(int someInt, string someInput);
    public abstract List<PackeHeader> BuildUsagePacket(string someInput);
    //...
    //...
    //...
}

Upvotes: 3

speti43
speti43

Reputation: 3046

Here is an example to use it generic way: Use your entities in type constraint like:

where T : InfoPacketBase and in derived class method: List<InfoPacket1011>;

abstract class A
{
    public abstract List<T> BuildInfoPacket<T>(string someInput) where T : new();
}

class B : A
{
    public override List<T> BuildInfoPacket<T>(string someInput)
    {
        // code implementation
        return new List<T> { new T() };
    }

    public void Test()
    {
        BuildInfoPacket<object>("test");
    }
}

Upvotes: 0

Konrad Kokosa
Konrad Kokosa

Reputation: 16878

You are implementing in fact an Abstract Factory pattern where PackerHandlerBase is an Abstract Factory and it produces/builds an Abstract Product which in your case is an InfoPacket and UsagePacket. Concrete Factory is PacketHandler1011 or PacketHandler1014. And Concrete Product is InfoPacket1011 or InfoPacket1014 and so on.

So it should be:

public abstract class PacketHandlerBase : Engine
{
    public abstract bool SendInfoPacket(int someInt, string someInput);
    public abstract List<InfoPacket> BuildInfoPacket(string someInput);

    public abstract bool SendUsagePacket(int someInt, string someInput);
    public abstract List<UsagePacker> BuildUsagePacket(string someInput);
    //...
}

public class InfoPacket1014 : InfoPacket
{ 
    ///...
}

public class PacketHandler1011 : PackerHandlerBase
{
    //...
    public override List<InfoPacket> BuildInfoPacket(string someInput);
    {
        // code implementation
        return new List<InfoPacket> { new InfoPacket1011(), ... };
    }
}

Upvotes: 3

Related Questions