前言。为了以某种方式描述问题,我编写了一个控制台应用程序(参见下面的代码)。
问题的本质。假设有两个消息传递协议:St和Gv. 有两种抽象类型:
- 消息
- 消息
需要实现类型消息StMessage到GvMessage. 实现StMessage包含一组唯一的消息类型字段。转换后,您需要在控制台中显示一条消息。
这就是我所做的。代码很多,为了方便分区域,最好在代码编辑器中查看。代码:
using System;
namespace TestHelloWorld.ConsoleApp
{
internal static class Program
{
private static void Main(string[] args)
{
LaunchTestOfStatusMessageConverting();
LaunchTestOfSpeedMessageConverting();
}
private static void LaunchTestOfStatusMessageConverting()
{
var stMessage = new StMessageStatus
{
Status = "Pending"
};
var converter = GetConverterForMessage(stMessage);
var gvMessage = converter.Convert(stMessage);
var printer = new ConsoleMessagePrinter();
printer.PrintMessage(gvMessage);
}
private static void LaunchTestOfSpeedMessageConverting()
{
var stMessage = new StMessageSpeed
{
Speed = 42
};
var converter = GetConverterForMessage(stMessage);
var gvMessage = converter.Convert(stMessage);
var printer = new ConsoleMessagePrinter();
printer.PrintMessage(gvMessage);
}
private static IMessageConverter GetConverterForMessage(StMessage stMessage)
{
switch (stMessage.MessageType)
{
case StMessageType.Speed:
return new SpeedMessageConverter();
case StMessageType.Status:
return new StatusMessageConverter();
default:
throw new ArgumentOutOfRangeException();
}
}
}
#region [ GvMessages ]
public enum GvMessageType
{
Speed,
Status
}
public abstract class GvMessage
{
public abstract GvMessageType MessageType { get; }
}
public class GvMessageSpeed : GvMessage
{
public override GvMessageType MessageType => GvMessageType.Speed;
public int Speed { get; set; }
}
public class GvMessageStatus : GvMessage
{
public override GvMessageType MessageType => GvMessageType.Status;
public string Status { get; set; }
}
#endregion
#region [ StMessages ]
public enum StMessageType
{
Speed,
Status
}
public abstract class StMessage
{
public abstract StMessageType MessageType { get; }
}
public class StMessageSpeed : StMessage
{
public override StMessageType MessageType => StMessageType.Speed;
public int Speed { get; set; }
}
public class StMessageStatus : StMessage
{
public override StMessageType MessageType => StMessageType.Status;
public string Status { get; set; }
}
#endregion
#region [ Converters ]
public interface IMessageConverter
{
GvMessage Convert(StMessage stMessage);
}
public class StatusMessageConverter : IMessageConverter
{
public GvMessage Convert(StMessage stMessage)
{
var stMessageStatus = stMessage as StMessageStatus;
if (stMessageStatus == null)
{
throw new ArgumentException($"Message is not of {nameof(StMessageStatus)} type", nameof(stMessage));
}
var gvMessageStatus = new GvMessageStatus
{
Status = stMessageStatus.Status
};
return gvMessageStatus;
}
}
public class SpeedMessageConverter : IMessageConverter
{
public GvMessage Convert(StMessage stMessage)
{
var stMessageSpeed = stMessage as StMessageSpeed;
if (stMessageSpeed == null)
{
throw new ArgumentException($"Message is not of {nameof(StMessageStatus)} type", nameof(stMessage));
}
var gvMessageSpeed = new GvMessageSpeed
{
Speed = stMessageSpeed.Speed
};
return gvMessageSpeed;
}
}
#endregion
#region [ Message printers ]
public interface IMessagePrinter
{
void PrintMessage(GvMessage message);
}
public class ConsoleMessagePrinter : IMessagePrinter
{
public void PrintMessage(GvMessage message)
{
string messageAsText;
switch (message.MessageType)
{
case GvMessageType.Speed:
var speedMessage = (GvMessageSpeed) message;
messageAsText = $"Gv speed message. Speed={speedMessage.Speed}";
break;
case GvMessageType.Status:
var statusMessage = (GvMessageStatus)message;
messageAsText = $"Gv status message. Status={statusMessage.Status}";
break;
default:
throw new ArgumentOutOfRangeException();
}
Console.WriteLine(messageAsText);
}
}
#endregion
}
我无法理解我使用 C# 语言工具如何正确地解决了这个问题。我认为这里有问题。至少,我不喜欢对象从基本类型到具体类型的逆转换。
请检查我解决此问题的方法,如果有问题,请提供您自己的解决方案。
在我看来,删除该属性是完全可能的
MessageType。毕竟,它实际上复制了类本身的类型。在六个类中删除此属性后,我们更改了一些方法。
两种修改方法的代码。不是按属性进行切换,而是使用对象本身的类型进行检查。
我建议使用调度使用以下选项
dynamic。转换器类:
我们在方法中使用它
LaunchTest*:还有一个建议。重载方法
ToString:这大大简化了打印机类。
原则上,您可以完全删除它。
如果对象
StMessage知道存在不同类型的对象,则获得最简单的选项。然后直接在他们身上分配转换职责是可能的。在这种情况下,我们完全摆脱了额外的转换器类别。如果 St 和 Gv 协议对彼此一无所知,那么,无论您多么聪明,您都无法编写一个在更新任何协议后可以工作的转换器。值得向其中一个协议添加一条新消息 - 转换器也必须更新,这对于任何转换器都是如此。
最简单的转换器版本如下所示:
这种方法的唯一缺点是编译器将无法跟踪您是否处理了所有可能的消息。如果这对您很重要,您将不得不应用访问者模式,在这里您将不得不编写更多代码:
现在,如果您没有考虑所有可能的消息,您的转换器将无法编译。
最后,我提出的最后一种方法。既然您有两个非常相似的协议 - 为什么不尝试隔离主题区域并创建它的模型,使两个协议都依赖于这个模型,反之亦然?
在这种情况下,从传输模型到主要模型的转换将直接发生,反之亦然 - 通过同一个访问者:
使用这种方法,您甚至可能不需要单独的协议类,一个适当的(反)序列化器就足够了。