如何分离带接口的抽象级别?
How to separate levels of abstractions w/ Interfaces?
我正在自动化仓库,我正在尝试为下一个任务创建领域模型:
一个仓库有很多产品。产品可以是液体的,也可以是杂货,也可以是一件一件的。仓库中有两条包装线,用于包装液体产品或所有其他产品。一件一件的产品不需要包装。
这是我的模型:
enum ProductType
{
Liquid,
Grossery
}
interface IProduct
{
ProductType ProductType { get; }
}
interface IPackedProduct : IProduct
{
bool IsPacked { get; }
}
interface ILiquidProduct : IProduct
{
}
interface IGrosseryProduct : IProduct
{
}
interface IPackingLine
{
IPackedProduct Pack(IProduct product);
}
interface IGrosseryPackingLine : IPackingLine
{
IPackedProduct Pack(IGrosseryProduct p);
}
class ProductPackingLine : IPackingLine
{
public IPackedProduct Pack(IProduct product)
{
Console.WriteLine("Packing {0} default packing line", product);
return new PackedProduct(product);
}
}
class LiquidsPackingLine : IPackingLine
{
public IPackedProduct Pack(ILiquidProduct product) // I want this <=======================
{
Console.WriteLine("Packing {0} by liquid packing line", product);
return new PackedProduct(product);
}
}
class GrosseryPackingLine : IPackingLine
{
public IPackedProduct Pack(IProduct product)
{
Console.WriteLine("Packing {0} by grossery packing line", product);
return new PackedProduct(product);
}
}
我是这样使用的:
IProduct milk = new LiquidProduct(ProductType.Liquid);
IProduct pasta = new GrosseryProduct(ProductType.Grossery);
var packer = new PackingManager();
IPackedProduct packedMilk = packer.Pack(milk);
IPackedProduct packedPasta = packer.Pack(pasta);
这是 PackingManager
class PackingManager
{
public IPackedProduct Pack(IProduct product)
{
IPackingLine pl = GetPackingLineByProduct(product);
return pl.Pack(product);
}
private IPackingLine GetPackingLineByProduct(IProduct product)
{
switch (product.ProductType)
{
case ProductType.Liquid:
return new LiquidsPackingLine();
case ProductType.Grossery:
return new GrosseryPackingLine();
default:
throw new InvalidOperationException();
}
}
}
问题是,如果我使用 IPackingLine.Pack(IProduct p)
,我可能会错误地将 ILiquidProduct
的对象传递到错误的包装线。但是我需要我所有的包装线来实现 IPackingLine
以便能够以更常见的方式使用它们。
如何避免这种情况?
我认为主要有3种方法可以解决你的问题:
随处使用 IProduct
并放弃编译时类型安全以支持运行时检查。如果你走那条路,那么你至少应该明确表示 IPackingLine
可能会拒绝包装产品。
例如
public interface IPackingLine {
IPackedProduct pack(IProduct product);
bool canPack(IProduct);
}
使用某种双重调度(带有重载方法的 dynamic
关键字使这在 C# 中更容易):
public interface IPacker {
IPackedProduct pack(IProduct product);
IPackedProduct packLiquid(ILiquidProduct product);
IPackedProduct packGrossery(IGrosseryProduct product);
}
public interface IProduct {
IPackedProduct packWith(IPacker packer)
}
class LiquidProduct implements IProduct {
IPackedProduct packWith(IPacker packer) {
return packer.packLiquid(this);
}
}
//...
如果可能,引入新的抽象概念,允许包装线以相同的方式处理任何类型的产品。例如,假设您必须构建一个绘制正方形和三角形的应用程序。你可以为每个人配备一个专门的画家,但你也可以有一个专门处理抽象形状的画家。例如。 painter.paint(triangle.getShape())
.
我正在自动化仓库,我正在尝试为下一个任务创建领域模型:
一个仓库有很多产品。产品可以是液体的,也可以是杂货,也可以是一件一件的。仓库中有两条包装线,用于包装液体产品或所有其他产品。一件一件的产品不需要包装。
这是我的模型:
enum ProductType
{
Liquid,
Grossery
}
interface IProduct
{
ProductType ProductType { get; }
}
interface IPackedProduct : IProduct
{
bool IsPacked { get; }
}
interface ILiquidProduct : IProduct
{
}
interface IGrosseryProduct : IProduct
{
}
interface IPackingLine
{
IPackedProduct Pack(IProduct product);
}
interface IGrosseryPackingLine : IPackingLine
{
IPackedProduct Pack(IGrosseryProduct p);
}
class ProductPackingLine : IPackingLine
{
public IPackedProduct Pack(IProduct product)
{
Console.WriteLine("Packing {0} default packing line", product);
return new PackedProduct(product);
}
}
class LiquidsPackingLine : IPackingLine
{
public IPackedProduct Pack(ILiquidProduct product) // I want this <=======================
{
Console.WriteLine("Packing {0} by liquid packing line", product);
return new PackedProduct(product);
}
}
class GrosseryPackingLine : IPackingLine
{
public IPackedProduct Pack(IProduct product)
{
Console.WriteLine("Packing {0} by grossery packing line", product);
return new PackedProduct(product);
}
}
我是这样使用的:
IProduct milk = new LiquidProduct(ProductType.Liquid);
IProduct pasta = new GrosseryProduct(ProductType.Grossery);
var packer = new PackingManager();
IPackedProduct packedMilk = packer.Pack(milk);
IPackedProduct packedPasta = packer.Pack(pasta);
这是 PackingManager
class PackingManager
{
public IPackedProduct Pack(IProduct product)
{
IPackingLine pl = GetPackingLineByProduct(product);
return pl.Pack(product);
}
private IPackingLine GetPackingLineByProduct(IProduct product)
{
switch (product.ProductType)
{
case ProductType.Liquid:
return new LiquidsPackingLine();
case ProductType.Grossery:
return new GrosseryPackingLine();
default:
throw new InvalidOperationException();
}
}
}
问题是,如果我使用 IPackingLine.Pack(IProduct p)
,我可能会错误地将 ILiquidProduct
的对象传递到错误的包装线。但是我需要我所有的包装线来实现 IPackingLine
以便能够以更常见的方式使用它们。
如何避免这种情况?
我认为主要有3种方法可以解决你的问题:
随处使用
IProduct
并放弃编译时类型安全以支持运行时检查。如果你走那条路,那么你至少应该明确表示IPackingLine
可能会拒绝包装产品。例如
public interface IPackingLine { IPackedProduct pack(IProduct product); bool canPack(IProduct); }
使用某种双重调度(带有重载方法的
dynamic
关键字使这在 C# 中更容易):public interface IPacker { IPackedProduct pack(IProduct product); IPackedProduct packLiquid(ILiquidProduct product); IPackedProduct packGrossery(IGrosseryProduct product); } public interface IProduct { IPackedProduct packWith(IPacker packer) } class LiquidProduct implements IProduct { IPackedProduct packWith(IPacker packer) { return packer.packLiquid(this); } } //...
如果可能,引入新的抽象概念,允许包装线以相同的方式处理任何类型的产品。例如,假设您必须构建一个绘制正方形和三角形的应用程序。你可以为每个人配备一个专门的画家,但你也可以有一个专门处理抽象形状的画家。例如。
painter.paint(triangle.getShape())
.