views:

58

answers:

2

I have a very complicated setup of objects and each selection along the way limits or expands options available. I would hate to throw exceptions or to create invalid instance of the object. So, I want to limit options (methods available to invoke) when building an instance based on the previous method input paramters. For example if I have some rules that say if user is of type "A" then allow it to be added to roles otherwise if it is of type "B" ask for location and if location is in specific zip code ... You get the idea

Is this possible with anonymous methos, types, whatever

user type A

UserBuilder builder = new UserBuilder
builer.Build().ForType("A").WithRoles(rolesList);

user type B

UserBuilder builder = new UserBuilder
builer.Build().ForType("B").WithLocations(locationList);

Update: So basically my question is, Is there a way to limit all other public method options from the api, except for the valid ones based on the state of the object.

+2  A: 

Yes, this would be theoretically possible. (Though anonymous methods/types would not, most likely, be involved.)

However, usage will be a bit of a nightmare. Unfortunately, if you put these types of restrictions in using a fluent interface, you pretty much need to know the rules to understand how to "build" the type.

I would much prefer making specialized user subclasses for your different types of users. Have the constructors of each subclass require the parameters necessary for them to be initialized correctly, and expose methods and properties as required.

This way, you just make the appropriate user:

UserA userA = new UserA(rolesList); // This requires roles to construct
UserB userB = new UserB(locationList); // This requires locations to construct
Reed Copsey
unfortunatelly, the rules are soooo convoluted that it is impossible to create that many user types. I am actually trying not to make it nightmare. Each step along the way would limit all other steps and would guide you until you come to Finish(). You would only have one possible method to call after each step.
epitka
Unfortunaly, Reed is right. The natural way to do it is through derived classes. The alternative is to build a (very) complicated locking system.
Henk Holterman
@epitka: That's the exact reason why constructors are better. You can specify the possible allowable combinations as unique constructors of concrete types. "Builder" types always seem to make life worse. Granted, this is complicated, and will require a lot of options, but the solutions I've seen are worse than the problem they're trying to solve.
Reed Copsey
A: 

You can do something like the code below, which is obviously just a concept. The problem is that you have to code the method calls for each type in the chain to maintain the fluent interface.

class Program
{
    static void Main(string[] args)
    {
        var builder = new UserBuilder();
        builder.BuildTypeA().WithRoles("a,b");
        builder.BuildTypeB().WithLocations("c,d");
    }
}

public abstract class User {}
public class UserA : User {}
public class UserB : User {}

public class UserBuilder
{
    public UserABuilder BuildTypeA() { return new UserABuilder(); }
    public UserBBuilder BuildTypeB() { return new UserBBuilder(); }
}

public class UserABuilder
{
    public UserABuilder WithRoles(string roles)
    {
        // add roles
        return this;
    }
}

public class UserBBuilder
{
    public UserBBuilder WithLocations(string locations)
    {
        // add locations
        return this;
    }
}
Jamie Ide
I started down this road, but it seems sooo much work. I wonder if dynamic types in 4.0 would help here? Based on your answers I am left with 2 options: allow creation of the instance in invalid state, or throw an exception. I don't like either of them.
epitka