构建器模式 - 具有前置条件的方法

时间:2015-05-05 08:15:10

标签: c# design-patterns methods builder prerequisites

对于测试,我有Factory使用Product生成Builder。每个Product都可以具有状态(Available / InUse / Disposed /等)。我需要生产各种状态的产品。

我的问题在于,为了让我生成一个Product,让我们说Disposed状态,首先需要InUse(必须创建它)使用new ProductBuilder().CheckIn().Dispose().Build();而不仅仅是new ProductBuilder().Dispose().Build();

我如何(或必须我?)强制执行构建器方法的前提条件并保持圈复杂度为1 (因此它不需要进一步测试)。

不希望使用类似if (product.Status == Product.INUSE) {...}的内容并为每种可能的场景投掷exceptions(不同的状态需要不同的前置条件)。

由于构建器是私有的,我是否需要强制执行此操作?我只是依靠程序员来了解需要调用方法的顺序,并在每个构建器方法之前添加一些注释?我选择不同的模式(哪个?)。

public static class ProductFactory
{
    private class ProductBuilder
    {
        private Product product;

        public ProductBuilder()
        {
            product = new Product {Status = product.AVAILABLE};
        }

        public ProductBuilder Dispose()
        {
            product.Status = product.DISPOSED; return this;
        }

        public ProductBuilder CheckIn()
        {
            product.Status = product.INUSE; return this;
        }

        public Product Build()
        {
            return product;
        }
    }

    public static Product CreateAvailable()
    {
        return new ProductBuilder().Build();
    }

    public static Product CreateInUse()
    {
        return new ProductBuilder().CheckIn().Build();
    }

    public static Product CreateDisposed()
    {
        return new ProductBuilder().CheckIn().Dispose().Build();
    }
}

2 个答案:

答案 0 :(得分:3)

首先,您必须跨多个界面隔离这些方法(CheckInDisposed):

public interface IBaseBuilder
{
    Product Build();
}

public interface IProductBuilder : IBaseBuilder
{
    ICheckedInProductBuilder CheckIn();
}

public interface ICheckedInProductBuilder : IBaseBuilder
{
    IDisposedProductBuilder Dispose();
}

public interface IDisposedProductBuilder : IBaseBuilder
{

}

这样,给定一个初始IProductBuilder

  • 您只能拨打CheckIn - >按特定顺序Dispose
  • 调用CheckIn后,无法再次调用
  • 调用Dispose后,无法再次调用
  • 您也可以在任何时候致电Build来创建产品。

为了使事情更容易实现,您可以让一个类实现所有三个接口,并使用主IProductBuilder接口将其注入其客户端。或者,您可以使用不同的类来实现接口。我会把它留作练习。

作为一个真实世界的例子,这种技术在Moq和FluentAssertions中被广泛使用,以实现流畅的API。

相关:Making my class 'fluent'

答案 1 :(得分:2)

您可以通过返回接口来抽象实现,而不是返回ProductBuilder。这样,您可以通过首先接收该接口来使某些方法可用。

让我在代码中解释一下。假设Disposed功能仅在InUse之后才可用,那么您可以这样做:

public interface IInUseProductBuilder
{
    IDisposedProductBuilder Dispose();
}

public interface IDisposedProductBuilder
{
    IBuiltProductBuilder Build();
}

当你实施它时:

public class ProductBuilder : IInUseProductBuilder, IDisposedProductBuilder
{
    public IDisposedProductBuiler Dispose()
    {
        product.Status = product.DISPOSED; 
        return this;
    }
}

这样,用户只能使用在特定构建器上声明的方法,您将根据工作流程公开这些方法。