最近几天,我试图了解如何更改代码以遵循SOLID
原则。
但是在这个示例中,我找不到合适的处理方式。
一个产品有一个Name
和一个BarCode
:
public partial class Product
{
public string Name { get; set; }
public string BarCode { get; set; }
}
此外,某些产品必须具有Batch Number
和/或Sale Deadline Date
:
public partial class Product
{
public bool IsBatchNumberManaged { get; set; }
public bool IsSaleDeadlineDateManaged { get; set; }
}
我有一些Stock
,其中涉及这些Products
,还有一些数据:Quantity
,Batch Number
(如果需要),Sale Deadline Date
(如果需要)。
public class Stock
{
public Product Product { get; set; }
public int Quantity { get; set; }
// should be null if Product is not managed by BN,
// else it has not to be null
public string BatchNumber { get; set; }
// should be null if Product is not managed by SDD,
// else it has not to be null
public DateTime? SaleDeadlineDate { get; set; }
}
我假设我的Stock
记录应这样构建:
public interface IStock
{
Product Product { get; set; }
int Quantity { get; set; }
}
public interface IBatchNumberManagedStock : IStock
{
string BatchNumber { get; set; }
}
public interface ISaleDeadlineDateManagedStock : IStock
{
DateTime SaleDeadlineDate { get; set; }
}
public class Stock : IStock
{
public Product Product { get; set; }
public int Quantity { get; set; }
}
这是我不满意的课程:
public class BatchNumberManagedStock : IStock, IBatchNumberManagedStock
{ ... }
public class SaleDeadlineDateManagedStock : IStock, ISaleDeadlineManagedStock
{ ... }
public class BatchNumberAndSaleDeadlineDateManagedStock : IStock, IBatchNumberManagedStock, ISaleDeadlineDateManagedStock
{ ... }
是否必须为每种可能的配置创建另一个类?
如果我现在有这个怎么办?:
public partial class Product
{
public bool IsSerialNumberManaged { get; set; }
}
我必须创建四个新类,只是为了实现这个非常新的属性?
public interface ISerialNumberManagedStock : IStock
{
string SerialNumber { get; set; }
}
public class SerialNumberManagedStock : IStock, ISerialNumberManagedStock { ... }
public class BatchNumberAndSerialNumberManagedStock : ... { ... }
public class SaleDeadlineDateAndSerialNumberManagedStock : ... { ... }
public class BatchNumberAndSaleDeadlineDateAndSerialNumberManagedStock : ... { ... }
这 是解决此问题的正确方法吗?我真的觉得我创建了一个海妖,对于Product
类的每个新配置,它将涉及大量的代码编写工作……
未来一点:
<< em>嘿,对于这些Product
,我们需要将BatchNumber设置为格式为“ yyyyMMdd”的SaleDeadlineDate 。>
我应该再次 建立新类吗?
哦,我忘了告诉你! 序列号管理产品的库存数量为1!
答案 0 :(得分:1)
这可能会帮助您解决当前的问题:
您是否注意到所有有关“额外”属性的示例都与管理有关?将其分解成自己的概念然后应用visitor pattern怎么样?
public interface IProductManagement
{
void Accept(IProductManagementVisitor visitor);
}
public interface IManagedByBatchNumber
: IProductManagement
{
public int BatchNo { get; set; }
}
public interface IManagedBySerialNumber
: IProductManagement
{
public int SerialNo { get; set; }
}
... etc ...
public interface IProductManagementVisitor
{
void Visit(IManagedByBatchNumber management);
void Visit(IManagedBySerialNumber management);
...etc...
}
并更新库存:
public class Stock
{
public Product Product { get; set; }
public int Quantity { get; set; }
public IProductManagement Management { get; set; }
}
添加访问者:
public class BatchNumberPrintingVisitor
: IProductManagementVisitor
{
public void Visit(IManagedByBatchNumber management)
{
Console.WriteLine($"Batch: {management.BatchNo}");
}
public void Visit(IManagedBySerialNumber management)
{ /* ignore */ }
}
添加管理员:
public class BatchNumberManagement
: IManagedByBatchNumber
{
public int BatchNo { get; set; }
public void Accept(IProductManagementVisitor visitor)
{
visitor.Visit(this);
}
}
public class SerialNumberManagement
: IManagedBySerialNumber
{
public int SerialNo { get; set; }
public void Accept(IProductManagementVisitor visitor)
{
visitor.Visit(this);
}
}
public class CompositeProductManagement
: IProductManagement
{
private readonly IEnumerable<IProductManagement> parts_;
public CompositeProductManagement(params IProductManagement[] parts)
{
parts_ = parts.ToArray();
}
public void Accept(IProductManagementVisitor visitor)
{
foreach (var part in parts_)
{
part.Accept(visitor);
}
}
}
并使用:
var stockManagedByBatch = new Stock
{
Product = "A",
Quantity = 1,
Management = new BatchNumberManagement
{
BatchNo = 123456
}
};
var stockManagedByBatchAndSerialNo = new Stock
{
Product = "B",
Quantity = 1,
Management = new CompositeProductManagement(
new BatchNumberManagement { BatchNo = 123456 },
new SerialNumberManagement { SerialNo = 9870 }
}
};
var stocks = new [] { stockManagedByBatch, stockManagedByBatchAndSerialNo };
// print batch# of all stocks managed by batch to console
var printingVisitor = new BatchNumberPrintingVisitor();
foreach (var stock in stocks)
{
stock.Management.Accept(printingVisitor);
}
请注意,IProductManagementVisitor
界面也可以视为违反SOLID原则,因为添加新的管理概念需要更新所有访客。如果这真的开始令人感到不适,则可以切换到动态访客模式:
public interface IProductManagement
{
void Accept(IProductManagementVisitor visitor);
}
public interface IManagedByBatchNumber
: IProductManagement
{
public int BatchNo { get; set; }
}
public interface IProductManagementVisitor
{
void Visit(IProductManagement management);
}
public class BatchNumberPrintingVisitor
: IProductManagementVisitor
{
void Visit(IProductManagement management)
{
var batchManagement = management as IManagedByBatchNumber;
if (Object.ReferenceEquals(null, batchManagement))
return;
Console.WriteLine($"Batch number: {batchManagement.BatchNo}");
}
}
答案 1 :(得分:0)
我认为您做得太过分了。除非有充分的理由为所有属性扩展创建单独的接口,否则为什么不
public interface IStock
{
Product Product { get; set; }
int Quantity { get; set; }
bool IsBatchNumberManaged { get; }
string BatchNumber { get; set; }
bool IsSaleDeadlineDateManaged { get; }
DateTime? SaleDeadlineDate { get; set; }
}
public class Stock : IStock
{
public Product Product { get; set; }
public int Quantity { get; set; }
public IsBatchNumberManaged { get { return BatchNumber != null;} }
string BatchNumber { get; set; }
IsSaleDeadlineDateManaged { get { return SaleDeadlineDate != null;} }
DateTime? SaleDeadlineDate { get; set; }
}