2

For testing purposes, I have a Factory that produces Products using a Builder. Each Product can have a status (Available/ InUse/ Disposed/ etc). I need to produce products in various states.

My problem is that, in order for me to produce a Product with, let's say, Disposed status, it first needs to be InUse ( must create it using new ProductBuilder().CheckIn().Dispose().Build(); not just new ProductBuilder().Dispose().Build();)

How can I (or must I ?) enforce this precondition for a builder method and keep the cyclomatic complexity of 1 (so it doesn't need further testing).

I don't wish to use something like if (product.Status == Product.INUSE) {...} and throwing exceptions for each possible scenario (different states need different preconditions).

Since the builder is private, do I need to enforce this? do I just rely on the programmer to know the order in which the methods need to be called and just add some comments before each builder method? do I choose a different pattern (which?).

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();
    }
}
  • There doesn't seem to be much benefit in using the builder pattern here. Why not just return products directly? – Enigmativity May 05 '15 at 08:33
  • I agree with @Enigmativity. There's not much to encapsulate. The builder pattern is useful if you want to inject a builder into many different clients - here, there's only *one* client, the factory. – dcastro May 05 '15 at 08:59
  • the methods inside the builder are actually a bit more complicated and I felt the need to extract them somehow and I wanted to be able to use the fluency the builder provides when constructing objects. Also, in my real case, there are more statuses I need to obtain by combining the same methods (the order matters, providing different results). I will consider your advice though... – user3215557 May 05 '15 at 11:05

2 Answers2

3

First off, you'd have to segregate these methods (CheckIn and Disposed ) across multiple interfaces:

public interface IBaseBuilder
{
    Product Build();
}

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

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

public interface IDisposedProductBuilder : IBaseBuilder
{

}

This way, given an initial IProductBuilder:

  • you can only call CheckIn -> Dispose in that specific order
  • once CheckIn has been called, it cannot be called again
  • once Dispose has been called, it cannot be called again
  • you can also, at any point in time, call Build to create the product.

To make things easier to implement, you can have one class implement all three interfaces, and inject it into its clients using the primary IProductBuilder interface. Or, you could have different classes implement the interfaces. I'll leave that as an exercise.

As a real world example, this technique is widely used in Moq and FluentAssertions to achieve a fluent API.

Related: Making my class 'fluent'

Community
  • 1
  • 1
dcastro
  • 66,540
  • 21
  • 145
  • 155
  • from what I read, apparently this is the way to go... though I think the class diagram would explode (I have more than one "`Product`").. So, for each entity having various states, I would need M x N interfaces... that kind of sucks.. but I guess this is the price of fluency... – user3215557 May 05 '15 at 11:23
  • @user3215557 Indeed, fluency is no easy task :/ As I said, you could have multiple small, fine-grained interfaces, but then have a class that implements most of them - that might help reduce the complexity. – dcastro May 05 '15 at 13:06
2

Instead of returning a ProductBuilder, you can abstract away the implementation by returning an interface. This way, you could make certain methods available only via receiving that interface first.

Let me explain in code. Let's say a Disposed feature should only be available after being InUse, then you could do this:

public interface IInUseProductBuilder
{
    IDisposedProductBuilder Dispose();
}

public interface IDisposedProductBuilder
{
    IBuiltProductBuilder Build();
}

And when you implement it:

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

This way, the user will only be able to use methods declared on that specific builder, which you'll expose depending on your workflow.

Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
  • 1
    thanks for your effort :). I got the point. I wish I could mark 2 answers. but I will choose dcastro's answer this time. – user3215557 May 05 '15 at 11:17