Создание общего интерфейса связи - PullRequest
0 голосов
/ 21 мая 2018

У меня проблемы с созданием абстрактного интерфейса для нескольких типов связи, которые имеют разные настройки для подключения.Я хотел бы иметь возможность использовать какую-то фабрику для создания экземпляра одного из типов связи (usb, serial, tcp) с соответствующими аргументами соединения, не разбивая его на кучу операторов if для проверки типа и использования.единый интерфейс для управления всем взаимодействием с указанным типом связи.

Ниже приведен код, который поможет проиллюстрировать мою проблему:

public interface ITransport : IDisposable
{
    event EventHandler Connected;
    event EventHandler Disconnected;
    event EventHandler<byte[]> SentData;
    event EventHandler<byte[]> ReceivedData;
    event EventHandler<string> ErrorOccurred;

    event HostCertificateReceivedDelegate HostValidation;

    Task<bool> ConnectAsync(TransportConnectionArgs connectionArgs);
    Task<bool> SendAsync(byte[] buffer);

    void Disconnect();
}

public class TransportConnectionArgs
{
    public static readonly TransportConnectionArgs Empty = new TransportConnectionArgs();
    protected TransportConnectionArgs() { }
}

public class SshTransportConnectionArgs : TransportConnectionArgs
{
    public string Host { get; }
    public int Port { get; }

    public string Username { get; }
    public string Password { get; }

    public SshTransportConnectionArgs(string host, int port, string username = "root", string password = "")
    {
        Host = host;
        Port = port;
        Username = username;
        Password = password;
    }
}

public class SerialTransportConnectionArgs : TransportConnectionArgs
{
    ... does not need password but needs com port, baud, etc...
}

public class UsbTransportConnectionArgs : TransportConnectionArgs
{
    ... does not need address or port or serial connection settings
}

public sealed class SshDebugClient : ITransport
{
    public async Task<bool> ConnectAsync(SshTransportConnectionArgs connectionArgs)
    {
        ... access properties specific to a regular TCP connection
    }
}

public sealed class SerialDebugClient : ITransport
{
    public async Task<bool> ConnectAsync(SerialTransportConnectionArgs connectionArgs)
    {
        ... access properties specific to a regular serial/COM connection
    }
}

public sealed class UsbDebugClient : ITransport
{
    public async Task<bool> ConnectAsync(UsbTransportConnectionArgs connectionArgs)
    {
        ... access properties specific to a regular USB connection
    }
}

Ответы [ 2 ]

0 голосов
/ 21 мая 2018

Просто быстрая и, возможно, не очень чистая идея.

Если вы переместите зависимость TransportConnectionArgs из интерфейса ITransport в конструкторы конкретных реализаций, вы сможете реализовать довольно общий метод создания экземпляров.

ITransport Create(TransportConnectionArgs connectionOptions)
{
    if (connectionOptions is SshTransportConnectionArgs sshOptions) 
    {
        return new SshDebugClient(sshOptions);
    } 
    else if (...)
    ...
    else 
    {
        throw new NotSupportedException("Unknown connection type.");
    }
}

var transport = Create(new SshTransportConnectionArgs { ... });
var connected = await transport.ConnectAsync();

Тем не менее, это не сработает наверняка, если ожидается повторное использование одного экземпляра ITransport с несколькими экземплярами TransportConnectionArgs, то есть ожидается, что будет несколько вызовов ConnectAsync с различными параметрами.

0 голосов
/ 21 мая 2018

Хммм, хорошо, в зависимости от того, что вы ищете, это либо довольно просто, либо невероятно сложно.

Вы ищете фабрику для класса args ?Если это так, у вас будет трудное время - поскольку у них всех есть разные свойства, которые вы должны заполнить.Вам не нужен глобальный конструктор фабрики, который принимает каждое возможное имя поля и решает, какой тип использовать, основываясь на том, какие значения были заполнены.

Но ... если вы просто ищете конструктор дляИТ-транспорт?Тогда вы можете просто сделать:

public class Factory
{
    public static ITransport CreateTransport(TransportConnectionArgs args)
    {
        if (args is SshTransportConnectionArgs)
            return new SshDebugClient((SshTransportConnectionArgs)args);
        if (args is SerialTransportConnectionArgs)
            return new SerialDebugClient((SerialTransportConnectionArgs)args);
        // etc
    }
}

... или, если вы действительно против тех, если линии?

Используйте отражение, чтобы найти все неабстрактные классы, которые реализуют ITransport, добавляют в ITransport дополнительный метод, требующий, чтобы разработчики возвращали тип аргументов, с которыми они работают, и затем делают что-то вроде:

public static ITransport CreateTransport(TransportConnectionArgs args)
{
    List<ITransport> allTranports = GetAllImplementersOf<ITransport>();
    foreach(ITransport transport in allTransports)
    {
        if (transport.NeededArgType == typeof(args))
            return transport;
    }
}

... В конечном счете, хотя, я думаю,одна проблема в том, что между двумя вашими классами нет «крючка».У вас есть транспортный тип, и у вас есть тип аргумента ... но у вас нет никакой связи между ними.Часть меня задается вопросом, должны ли вы иметь аргументы, которые транспорт может создавать / анализировать, вместо того, чтобы иметь отдельный независимый класс для него.

...