Oop 如何将抽象级别与接口分离?

Oop 如何将抽象级别与接口分离?,oop,interface,domain-driven-design,abstraction,Oop,Interface,Domain Driven Design,Abstraction,我正在自动化仓库,并尝试为下一个任务创建域模型: 仓库里有很多产品。产品可以是液态的,也可以是杂货店的,或者是一件一件的。仓库中有两条包装线,用于包装液体产品或所有其他产品。逐件产品不需要包装 以下是我的模型: enum ProductType { Liquid, Grossery } interface IProduct { ProductType ProductType { get; } } interface IPackedProduct : IProduct

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

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

以下是我的模型:

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);
    }
}
这是包装经理

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();
        }
    }
}
问题是,如果我使用
ipackline.Pack(ipproduct p)
我可能会将
ILiquidProduct
的对象错误地传递到错误的包装行。但我需要所有打包行来实现
iPackline
,以便能够以更常见的方式使用它们


如何避免这种情况

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

  • 在任何地方使用
    IProduct
    ,放弃编译时类型安全性,支持运行时检查。如果你走这条路,那么你至少应该明确指出,
    iPackline
    可能拒绝包装产品

    例如

  • 使用某种双重分派(带有重载方法的
    dynamic
    关键字使C#中的分派更容易):

  • 如果可能的话,引入新的抽象概念,允许包装线以同样的方式处理任何类型的产品。例如,假设您必须构建一个绘制正方形和三角形的应用程序。您可以为每一个都指定一个专门的画家,但也可以指定一个处理抽象形状的画家。例如,
    painter.paint(triangle.getShape())


  • 似乎您需要使用泛型,这样您就可以限定某个特定打包机可以打包的东西的种类。猜测语言,请参见您的
    PackingManager
    打破了开-关原则。为什么你需要一个通用/通用的
    包装经理
    ?我需要一个能决定使用哪种包装线的人。在其他情况下,逻辑将分布在整个应用程序中。关于
    GetPackingLineByProduct
    ——这里只是为了简单起见。在真正的应用程序中,我将注入一个工厂,它将构建我的
    iPackline
    比方说
    PackingManager
    这是应用程序的某种服务层。我将使用#1。谢谢。我已经这样创建了它:
    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();
            }
        }
    }
    
    public interface IPackingLine {
        IPackedProduct pack(IProduct product);
        bool canPack(IProduct);
    }
    
       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);
           }
       }
    
       //...