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

时间:2017-07-27 11:43:34

标签: oop interface domain-driven-design abstraction

我自动化仓库,我正在尝试为下一个任务创建域模型:

仓库里有很多产品。产品可以是液体,杂货,也可以是一块一块。仓库中有两条包装线,用于包装液体产品或所有其他产品。逐件产品不需要包装。

以下是我的模特:

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,以便能够以更常见的方式使用它们。

如何避免?

1 个答案:

答案 0 :(得分:1)

我认为有三种主要方法可以解决您的问题:

  1. 在任何地方使用IProduct并删除编译时类型安全性,以支持运行时检查。如果你沿着这条路前进,那么你至少应该明确表示IPackingLine可能拒绝包装产品。

    E.g。

    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())