如何分离带接口的抽象级别?

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种方法可以解决你的问题:

  1. 随处使用 IProduct 并放弃编译时类型安全以支持运行时检查。如果你走那条路,那么你至少应该明确表示 IPackingLine 可能会拒绝包装产品。

    例如

    public interface IPackingLine {
        IPackedProduct pack(IProduct product);
        bool canPack(IProduct);
    }
    
  2. 使用某种双重调度(带有重载方法的 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);
           }
       }
    
       //...
    
  3. 如果可能,引入新的抽象概念,允许包装线以相同的方式处理任何类型的产品。例如,假设您必须构建一个绘制正方形和三角形的应用程序。你可以为每个人配备一个专门的画家,但你也可以有一个专门处理抽象形状的画家。例如。 painter.paint(triangle.getShape()).