【问题标题】:How to separate levels of abstractions w/ Interfaces?如何使用接口分离抽象级别?
【发布时间】:2023-04-02 15:36:01
【问题描述】:

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

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

这是我的模型:

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

如何避免这种情况?

【问题讨论】:

  • 您似乎需要使用泛型,这样您就可以限定给定打包程序可以打包的东西种类。猜测语言,参见例如docs.microsoft.com/en-us/dotnet/csharp/programming-guide/…
  • 您的PackingManager 违反了打开关闭原则。为什么需要通用/通用PackingManager
  • 我需要有人来确定使用哪条包装线。在其他情况下,逻辑将分布在整个应用程序中。关于GetPackingLineByProduct - 这里只是为了简单起见。在真正的应用程序中,我将注入一个工厂,它将构建我IPackingLine
  • 假设PackingManager 这是应用程序的某种服务层。

标签: oop interface domain-driven-design abstraction


【解决方案1】:

我认为有 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())

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2013-02-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多