4

我有一个接口,可以说ISendOut我从它继承了两个不同的类,例如 TransferViaSerialPort 和 TransferViaWirelessModule(我的意思是在这两个类中实现这个接口)。我如何设计我的软件,让用户能够(在 UI 中)SerialPort在通过或WirelessModule不违反 OCP发送他/她的数据的方法之间进行选择?因为如果我想要一个“Switch Case”或“If/Else”语句,我将违反 OCP。

4

4 回答 4

2

您需要使用工厂模式。为了使工厂模式动态化,您可以使用反射并显示您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);
    }
}

后来我在不修改现有代码的情况下添加 UsbSendOut(所以不会破坏 OCP)

[DisplayName("Usb")]
public class UsbSendOut : ISendOut
{
    public void Send(string data)
    {
        MessageBox.Show("data sent through usb.");
    }
}
于 2012-03-07T08:59:54.617 回答
1

您将您的实现ISendOut作为参数传递给构造函数,并让 C# 的动态调度执行“switch case”,正如您所说的那样。

这就是接口如此有用的原因:你有一个间接并且可以进行依赖注入来满足 OCP。

于 2012-03-06T14:23:42.130 回答
1

创建一个UserConfiguredCommunicationModule类(优先组合而不是继承)

public class UserConfiguredCommunicationModule : ISendOut 
{
    public UserConfiguredUserModule(SerialPort serial, WirelessModule wireless)
    {}

    public void Send(string data)
    {
        if (UserIdentity.Current.PrefersSerial)
            serial.Send(data);
        else
            wireless.Send(data);
    }
}

使用该实现将防止您破坏 OCP(尽管类本身违反了 OCP,但可以通过在其中使用工厂轻松修复)。

更新

你知道那有什么问题吗?我想让用户能够选择在 UI 中发送数据的方法。现在想象一下,我们将有更多的发送方法,即通过红外线发送或......所以通过让用户在不同的方法之间进行选择,我必须在我的 UI 中有一个 if 语句,这将违反 OCP。因为每一种新类型的发送都会迫使我有新的 if/else 条件

我的方法只将违反 OCP 的行为转移到一个类中,而不是使用接口的每个地方ISendOut

我还提到了工厂,我指的是工厂模式(既不是抽象工厂也不是工厂方法)。您可以使用它在配置字符串和具体类之间进行映射,并在其中使用该工厂UserConfiguredCommunicationModule 来创建正确的ISendOut实现。

您还可以在 中使用服务定位器模式UserConfiguredCommunicationModule来解决正确的实现。

那一点就是无论你选择什么,都需要一个UserConfiguredCommunicationModule类似的类来封装选择过程。

于 2012-03-06T15:08:46.377 回答
0

查看策略模式 https://en.wikipedia.org/wiki/Strategy_pattern

http://www.dofactory.com/Patterns/PatternStrategy.aspx#_self1

于 2012-03-06T14:16:57.477 回答