【发布时间】:2015-09-14 17:16:08
【问题描述】:
我尝试根据开闭原则重构一些代码,但在应用设计模式时,我似乎无法正确获取以下类。 (对于下面列出的许多类,我深表歉意 - 我已尽可能减少它们,但其余部分需要向您展示我的设计)。
设置由以下类组成:
public interface IPortFactory
{
IPort CreatePort(int id, PortDetails details);
}
public class PtpPortFactory : IPortFactory
{
public IPort CreatePort(int id, PortDetails details)
{
var ptpPortDetails = details as PtpPortDetails;
if (ptpPortDetails == null)
{
throw new ArgumentException("Port details does not match ptp ports", "details");
}
return new PtpPort(id, FiberCapability.FromValue(ptpPortDetails.Capability));
}
}
public interface IPort
{
int Id { get; }
}
public interface IInternetPort : IPort
{
bool OfCapability(FiberCapability capability);
}
public class PtpPort : IInternetPort
{
private readonly FiberCapability _capability;
public PtpPort(int id, FiberCapability capability)
{
_capability = capability;
Id = id;
}
public int Id { get; private set; }
public bool OfCapability(FiberCapability capability)
{
return capability.Equals(_capability);
}
}
除了PtpPort,我还有PonPort,它也实现了IInternetPort,而CatvPort只是实现了IPort。
在这段代码中,我认为有代码异味的迹象。在PtpPortFactory 中的CreatePort 中,漂亮的事情是它接受PtpPortDetails(继承自PortDetails)而不是强制转换。但是,如果这样做,我将无法创建同样实现IPortFactory 的PonPortFactory,因为这些端口需要PonPortDetails。或CatvPortFactory 就此事。
当我使用端口工厂时出现另一种代码气味:
PortType portType = command.PortType;
IPortFactory portFactory = portType.GetPortFactory();
var portsToSelectFrom = ports.Select(port => (IInternetPort) portFactory.CreatePort(port.Id, port.PortDetails)).ToList();
我真的不想从IPort 到IInternetPort 进行向下转换,而只需让CreatePort 返回IInternetPort。
理解上面需要的最后一点信息大概就是下面这个类(基于Jimmy BogardsEnumeration类):
public abstract class PortType : Enumeration<PortType, int>
{
public static readonly PortType Ptp = new PtpPortType();
public static readonly PortType Pon = new PonPortType();
public static readonly PortType Catv = new CatvPortType();
protected PortType(int value, string description)
: base(value, description) { }
public abstract IPortFactory GetPortFactory();
private class CatvPortType : PortType
{
public CatvPortType() : base(2, "catv") { }
public override IPortFactory GetPortFactory()
{
return new CatvPortFactory();
}
}
private class PonPortType : PortType
{
public PonPortType() : base(1, "pon") { }
public override IPortFactory GetPortFactory()
{
throw new NotImplementedException("Pon ports are not supported");
}
}
private class PtpPortType : PortType
{
public PtpPortType() : base(0, "ptp") { }
public override IPortFactory GetPortFactory()
{
return new PtpPortFactory();
}
}
}
我真的希望有人能一路帮助我(我尝试过引入泛型,但似乎总是遇到 C# 不支持返回类型协变的障碍)。
此外,任何其他有助于我编写更好代码的提示和技巧将不胜感激。
更新
由于有评论要求,我在下面添加了更多代码。
public Port Handle(TakeInternetPortCommand command)
{
var portLocatorService = new PortLocatorService();
IList<Port> availablePorts = portLocatorService.FindAvailablePorts(command.Pop, command.PortType);
PortType portType = command.PortType;
IPortFactory portFactory = portType.GetPortFactory();
var portsToSelectFrom = ports.Select(port => (IInternetPort) portFactory.CreatePort(port.Id, port.PortDetails)).ToList();
IPort port = _algorithm.RunOn(portsToSelectFrom);
Port chosenPort = availablePorts.First(p => p.Id == port.Id);
chosenPort.Take(command.Spir);
_portRepository.Add(chosenPort);
return chosenPort;
}
不要被突然之间还有Port 类型这一事实搞糊涂了。这是另一个有界上下文中的聚合(在 DDD 的意义上)。
该算法需要将IInternetPort 列表作为输入,因为它在内部使用OfCapability 方法来选择端口。然而,当算法选择了正确的端口时,我们只对Id感兴趣,因此返回类型只是IPort。
【问题讨论】:
-
当您有工作代码并寻求帮助以重构或获得建议时,Code Review Stack Exchange 最适合这项工作。
-
IPort、IInternetPort和IPortFactory的消费者/客户是谁?您如何/何时决定使用PtpPort、PonPort或CatvPort?它们是否都在同一个应用程序实例中使用?还是您决定根据某些应用程序配置只使用其中一个? -
@Pierre-LucPineault,抱歉在错误的论坛发帖。下次我会记得 Code Review Stack Exchange!
-
@YacoubMassad,感谢您的深入提问。
IPort、IInternetPort和IPortFactory的消费者/客户端,即我原帖中“使用端口工厂时出现另一种代码气味”正下方的三行的地方,是同一个命令处理程序应用。端口类型在命令中作为字符串发送(最终是客户端将其作为发布请求正文的一部分提交)。 -
将
IPort作为接口有什么具体原因吗?
标签: c# design-patterns factory-pattern solid-principles open-closed-principle