我有一个接口,比如ISendOut我从它继承了两个不同的类
例如transferViaSerialPort和transferViaWirelessmodule(我的意思是在这两个类中实现这个接口)。如何设计我的软件,使用户能够在通过SerialPortWirelessModule发送数据的方法之间进行选择,而不违反ocp?因为如果我想要一个“switch case”或“if/else”语句,我将违反ocp。

最佳答案

你需要使用工厂模式。要使工厂模式动态,您可以使用反射并在UI中显示类的类型,这些类是从ISendOut实现的,您可以使用自定义属性或其他方法,如使用Dictionary

[System.AttributeUsage(System.AttributeTargets.Class)]
public class DisplayNameAttribute : Attribute
{
    public DisplayNameAttribute(string displayName)
    {
        DisplayName = displayName;
    }

    public string DisplayName { get; set; }
}

public interface ISendOut
{
    void Send(string data);
}

[DisplayName("Wireless")]
public class WirelessSendOut : ISendOut
{
    public void Send(string data)
    {
        MessageBox.Show("data sent through wireless.");
    }
}

[DisplayName("Serial")]
public class SerialSendOut : ISendOut
{
    public void Send(string data)
    {
        MessageBox.Show("data sent through serial port.");
    }
}

public static class SendOutFactory
{
    public static ISendOut CreateSendOut(string typeName)
    {
        var types = Assembly.GetExecutingAssembly().GetTypes();
        var sendOutType = types.First(x => (typeof(ISendOut)).IsAssignableFrom(x) && x.Name == typeName);
        return (ISendOut) Activator.CreateInstance(sendOutType);
    }
}

public static class SendOutDiscovery
{
    public static IEnumerable<NameType> Discover()
    {
        var types = Assembly.GetExecutingAssembly().GetTypes();
        var sendOutTypes = types.Where(x => x != typeof(ISendOut) && (typeof(ISendOut)).IsAssignableFrom(x));
        return sendOutTypes.Select(type => GetNameType(type)).ToList();
    }

    private static NameType GetNameType(Type type)
    {
        var nameType = new NameType
                           {
                               DisplayName = GetDisplayName(type),
                               TypeName = type.Name
                           };
        return nameType;
    }

    private static string GetDisplayName(Type type)
    {
        return ((DisplayNameAttribute)type.GetCustomAttributes(typeof (DisplayNameAttribute), false).First()).DisplayName;
    }
}

public class NameType //for binding in UI
{
    public string DisplayName { get; set; }
    public string TypeName { get; set; }
}

public class SendOutViewModel //sample using in wpf (window contains a combobox)
{
    public SendOutViewModel()
    {
        SendTypes = new ObservableCollection<NameType>(SendOutDiscovery.Discover());
    }

    public NameType SelectedSendType { get; set; } //bind to selected item in combobox

    public ObservableCollection<NameType> SendTypes { get; private set; } //bind to item source of combo

    public string Data { get; set; } //data to be sent

    public void Send()
    {
        ISendOut sendOut = SendOutFactory.CreateSendOut(SelectedSendType.TypeName);
        sendOut.Send(Data);
    }
}

后来我在没有修改现有代码的情况下添加了UbsEdOutE(这样就不会破坏OCP)
[DisplayName("Usb")]
public class UsbSendOut : ISendOut
{
    public void Send(string data)
    {
        MessageBox.Show("data sent through usb.");
    }
}

10-07 14:07