How to improve my abstract factory pattern?

2019-08-22 16:24发布

问题:

I have really no much experience using design patterns. I guess I need to use Abstract Factory Pattern in my situation.

I'm creating a system to generate math problems. The developer must implement two interfaces:

  • Problem: This contains the properties which needs the problem generated.
  • Configuration: This is the range parameters or conditions to generate a Problem.
  • Factory: He is incharge of create the new Problem.

What does it mean? It means like a black box. For input is the Configuration and the output is Problem, the interface in the middle is the factory.

Here I have my interface and marker interfaces:

public abstract class Problem { }
public abstract class Configuration { }
public interface IProblemFactory
{
    Problem CreateProblem();
}

This is a base for the Factories, because I need the Random class. All my classes which implement this one, must have the same seed, so I have a static instace.

public abstract class ProblemBaseFactory<TProblem, TConfiguration> : IProblemFactory
    where TProblem : Problem
    where TConfiguration : Configuration
{
    private const int DEFAULT_SEED = 100;
    protected TConfiguration _config;
    private static Random _random;

    public ProblemBaseFactory() { }
    public ProblemBaseFactory(TConfiguration config)
    {
        _config = config;

        if (_random == null) _random = new Random(DEFAULT_SEED);
    }

    protected TConfiguration Configuration { get { return _config; } }
    protected Random Random { get { return _random; } }

    public void SetSeed()
    {
        _random = new Random(DEFAULT_SEED);
    }

    public Problem CreateProblem()
    {
        return CreateProblem(_config);
    }

    public abstract TProblem CreateProblem(TConfiguration config);
}

Then I have an implementation of all of this. For example, this is an Module for BinaryProblems like 2+3.

public class BinaryConfiguration : Configuration
{
    public Range<int> Range1 { get; set; }
    public Range<int> Range2 { get; set; }
    public List<Operators> Operators { get; set; }

    public BinaryConfiguration(Range<int> range1, Range<int> range2, List<Operators> operators)
    {
        this.Range1 = range1;
        this.Range2 = range2;
        this.Operators = operators;
    }

public class BinaryProblem : Problem
{
    public BinaryProblem(decimal x, decimal y, Operators op, decimal response)
    {
        this.X = x;
        this.Y = y;
        this.Response = response;
    }

    public decimal X { get; private set; }
    public decimal Y { get; private set; }
    public decimal Response { get; private set; }
}

public enum Operators
{
    Addition, Substract, Multiplication, Division
}

And the most important part, here is a concrete factory. Look at this part I set the generic values. Why? Because I supposed is the best way to implement the concrete values, I mean I don't have to cast any value right now.

public class BinaryFactory : ProblemBaseFactory<BinaryProblem, BinaryConfiguration>
{
    public BinaryFactory(BinaryConfiguration config) : base(config) { }

    public override BinaryProblem CreateProblem(BinaryConfiguration config)
    {
        var x = GenerateValueInRange(config.Range1);
        var y = GenerateValueInRange(config.Range2);

        var index = Random.Next(config.Operators.Count);
        var op = config.Operators[index];

        return new BinaryProblem(x, y, op, x + y);
    }

    private decimal GenerateValueInRange(Range<int> range)
    {
        return Random.Next(range.Min, range.Max);
    }
}

And to implement it is:

        BinaryConfiguration configuration = new BinaryConfiguration() {.. }
        IProblemFactory factory = new BinaryFactory(configuration);
        var a = factory.CreateProblem();

But at this point, I guess this is not the best design.. because if I want to use a new Configuration, I should create another instance of it, and I supposed is not the best thing.

How can I improve it?

回答1:

As a starting point I would suggest that you use a recursive type definition. Try this:

public abstract class Problem<P, C>
    where P : Problem<P, C>
    where C : Configuration<P, C>
    { }

public abstract class Configuration<P, C>
    where P : Problem<P, C>
    where C : Configuration<P, C>
    { }

public interface IProblemFactory<P, C>
    where P : Problem<P, C>
    where C : Configuration<P, C>
{
    P CreateProblem(C configuration);
}

This effectively implements the Gang of Four "Bridge" Design Pattern for your types.

Let me know if this helps.