Превключване на добри практики за алтернативно/разширяемо програмиране?

Първо, малко предистория на моя въпрос; Пиша модулна програма тип сървър/клиент на C#. Имам система за обработка с нишки, за да обработвам моите пакети от клиентска опашка, пример за това, с което работя:

public delegate object ProcessPacket(object data);
public class QueueHandler
{
    //Awesome functions/variables
    private void Proccess()
    {
        object retPacket = client.ProcessPacket(obj);
        NetworkCommon.Sendpacket(retPacket, _clientSocket);
    }
    //Even more awesome functions.
}
//Client ProcessPacket delegate
private static object ProcessPacket(object packet)
{
    ReturnPacket ret = new ReturnPacket();
    switch (packet.Command)
    {
        case Command.Login:
            //Do login stuff
            break;
        case Command.Foo;
            //Foo stuff
            break;
        case Command.Bar;
            //Bar stuff
            break;
        default:
            ret.Responce = CommandResponce.CommandNotRecognized;
    }
    return ret;
}

Както можете да се досетите, това не е много разширимо и ще бъде трудно да се управлява с добавяне на функции. Въпросът ми е Кой е по-практичен начин за това?

Мислех за нещо като List<string,TProcessCommand>, присвояване на персонализиран атрибут на функцията, след което изграждане на този списък при стартиране с помощта на отражение. Но смятате, че все още може да е тромаво за управление и евентуално ненужна процесорна мощност при всяко стартиране.

Друг вариант, който обмислях, е използването на MEF, но ми е трудно да си обясня как да идентифицирам между командите, без да използвам Linq, с който не съм запознат.

[[РЕДАКТИРАНЕ]] Току-що забелязах в MEF пример, ExportMetadataAttribute е използван. Ще опитам това с моя код.


person Patrick S    schedule 24.05.2013    source източник
comment
Публикацията изглежда по-малко подходяща за linq, но linq определено е инструмент, който трябва да имате като C# програмист.   -  person David    schedule 24.05.2013
comment
Refactrong swtich в по-разширяем код е много често срещан въпрос - потърсете го (т.е. на SO - stackoverflow.com/ search?q=%5Bc%23%5Dswitch+refactoring), за да видите още алтернативи. Наследяване и речник на съпоставянето на команда към действие са често срещани опции.   -  person Alexei Levenkov    schedule 24.05.2013


Отговори (1)


Бих създал интерфейс, да го наречем

public interface ICommandManager
{
    ReturnPacket DoYourStuff();
}

и атрибут като този:

public class HandlesAttribute : Attribute
{
    public Command HandlesCommand { get; private set; }

    public HandlesAttribute(Command cmd)
    {
        this.HandlesCommand = cmd;
    }
}

Вече можете да внедрите класове за обработка на вашите команди и да им дадете необходимия атрибут.

[Handles(Command.Login)]
public class LoginCommandManager : ICommandManager
{
    public ReturnPacket DoYourStuff()
    {
        var ret = new ReturnPacket();
        /* Do some stuff */
        return ret;
    }
}

Сега можете да инициализирате обекти от тези класове с напр. Отражение (просто потърсете типове, които изпълняват ICommandManager), без твърдо кодиране на switch и просто чрез създаване на нова ICommandManager реализация, когато има нова команда.

Или малко по-фантастичен начин, без атрибути с помощта на Ninject:

public class LoginCommandManager : ICommandManager
{
    public ReturnPacket DoYourStuff()
    {
        var ret = new ReturnPacket();
        /* Do some stuff */
        return ret;
    }
}

Сега можете да използвате Ninject, за да разрешите проблема си. Използвайте този код, за да регистрирате вашите обвързвания.

kernel.Bind<ICommandManager>()
    .To<LoginCommandManager>()
    .WithMetadata("Command", Command.Login);
/* ... */

И по-късно можете да разрешите това с:

kernel.Get<ICommandManager>(metadata => 
    packet.Command == metadata.Get<Command>("Command"));
person Jan P.    schedule 24.05.2013