Open-Closed Principle に基づいていくつかのコードをリファクタリングしようとしましたが、デザイン パターンの適用に関しては、次のクラスを正しく取得できないようです。(以下に概説する多くのクラスをお詫びします - 可能な限りそれらを減らしましたが、残りは私のデザインを示すために必要です)。
セットアップは、次のクラスで構成されます。
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
です。
すでにこのコードには、コード臭の兆候があると思います。inCreatePort
では、キャストするのではなく、PtpPortFactory
受け入れるPtpPortDetails
(から継承する) ことができます。PortDetails
ただし、そうすると、PonPortFactory
も実装する を作成できません。IPortFactory
これらのポートにはPonPortDetails
. またはCatvPortFactory
そのことについて。
ポート ファクトリを使用すると、別のコードの匂いがします。
PortType portType = command.PortType;
IPortFactory portFactory = portType.GetPortFactory();
var portsToSelectFrom = ports.Select(port => (IInternetPort) portFactory.CreatePort(port.Id, port.PortDetails)).ToList();
IPort
from からto へのダウンキャストを実行する必要はIInternetPort
なく、単にCreatePort
returnが必要IInternetPort
です。
上記を理解するために必要な最後の情報は、おそらく次のクラスです ( Jimmy Bogards クラスに基づくEnumeration
)。
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
です。