Home > Blockchain >  How do I structure factory classes to allow for a fluent interface?
How do I structure factory classes to allow for a fluent interface?

Time:02-20

I want to create a series of Actions that do related things

public interface Action{
    public void execute();
}

public class DatabaseAction implements Action{
    public void execute(){}
}

public class WebAction implements Action{
    public void execute(){}
}

public class EmailAction implements Action{
    public void execute(){}
}

Generally speaking, users don't care about the details. They want all the actions to run and not worry about it.

But there's going to be some special cases where they only want to run some of the actions, and configure some of the actions.

And I suppose there could be cases where configuration is non-optional.

I figure a fluent interface is the most readable here.

// Executes all Actions - intended to be used in almost all cases
// I write to a database, call a web API, and send an email.
Actions.withAllDefaults().execute(); 

// I don't need to send an email and I need to configure the database
Actions.withAction(DATABASE_ACTION)
  .withConfiguration(DatabaseAction.PORT, 9000)
  .withAction(WEB_ACTION)
  .execute();

It feels like I should be implementing some sort of factory but it's hard for me to actually translate that into code.

CodePudding user response:

Consider using the Fluent Builder Pattern instead of trying to make your factory fluent.

CodePudding user response:

C# uses fluent programming extensively in LINQ to build queries using "standard query operators".

This is C# implementation. It looks like this sample code can be converted to Java as special features of C# is not used.

So let's see an example. We start from an interface IFluent which allows to build your actions with settings:

public interface IFluent
{
    IFluent WithAction(Action action);


    IFluent WithConfiguration(KeyValuePair<string, object> configuration);
}

and this is Fluent class which implements IFluent interface:

public class Fluent : IFluent
{
    private List<Action> actions;

    public IFluent WithAction(Action action)
    {
        if (actions == null)
            actions = new List<Action>();

        actions.Add(action);
        return this;
    }

    public IFluent WithConfiguration(KeyValuePair<string, object> configuration)
    {
        if (actions == null || actions.Count == 0)
            throw new InvalidOperationException("There are no actions");

        int currentActionIndex = actions.Count - 1;
        actions[currentActionIndex].Set(configuration);
        return this;
    }
}

Then we create an abstract class for Action that should define behavior for derived classes:

public abstract class Action
{
    public abstract Dictionary<string, object> Properties { get; set; }


    public abstract void Execute();


    public abstract void Set(KeyValuePair<string, object> configuration);


    public abstract void Add(string name, object value);
}

And our derived classes would look like this:

public class DatabaseAction : Abstract.Action
{
    public override Dictionary<string, object> Properties { get; set; } 
        = new Dictionary<string, object>() 
    {
        { "port", 0},
        { "connectionString", "foobarConnectionString"},
        { "timeout", 60}
    };


    public override void Execute()
    {
        Console.WriteLine("It is a database action");
    }


    public override void Set(KeyValuePair<string, object> configuration)
    {
        if (Properties.ContainsKey(configuration.Key))
            Properties[configuration.Key] = configuration.Value;
    }


    public override void Add(string name, object value)
    {
        Properties.Add(name, value);
    }
}

and EmailAction:

public class EmailAction : Abstract.Action
{
    public override Dictionary<string, object> Properties { get; set; } 
        = new Dictionary<string, object>()
    {
        { "from", "Head First - Object Oriented Design"},
        { "to", "who wants to learn object oriented design"},
        { "index", 123456}
    };

    public override void Execute()
    {
        Console.WriteLine("It is a email action");
    }

    public override void Set(KeyValuePair<string, object> configuration)
    {
        if (Properties.ContainsKey(configuration.Key))
            Properties[configuration.Key] = configuration.Value;
    }

    public override void Add(string name, object value)
    {
        Properties.Add(name, value);
    }
}

and WebAction:

public class WebAction : Abstract.Action
{
    public override Dictionary<string, object> Properties { get; set; } 
        = new Dictionary<string, object>()
    {
        { "foo", "1"},
        { "bar", "2"},
        { "hey", "hi"}
    };

    public override void Execute()
    {
        Console.WriteLine("It is a email action");
    }

    public override void Set(KeyValuePair<string, object> configuration)
    {
        if (Properties.ContainsKey(configuration.Key))
            Properties[configuration.Key] = configuration.Value;
    }

    public override void Add(string name, object value)
    {
        Properties.Add(name, value);
    }
}

The it is possible to call code like this:

Fluent actions = new Fluent();
actions.WithAction(new DatabaseAction())
    .WithConfiguration(new KeyValuePair<string, object>("port", 1))
    .WithAction(new EmailAction())
    .WithConfiguration(new KeyValuePair<string, object>("to", "me"));
  • Related