Search code examples
c#design-patternsfactoryfactory-pattern

How to add a random feature for a factory design?


I'm working on an internal project for my company, and part of the project is to be able to generate various "Math Problems" in a factory design.

  • To generate problems, must specify the level of difficulty in the factory.
  • Each ProblemFactory contains abstracts methods as ConfigureXLevels and Generate.
  • Provides a Random variable.
  • Contains a dictionary which contains the available levels (key:=Levels, value:=IConfiguration which works like a container of objects useful to generate the problem (for example binary and times tables both needs two Bound objects)).

_

public abstract class ProblemFactory
{
    private IDictionary<Levels, IConfiguration> Configurations = new Dictionary<Levels, IConfiguration>();
    protected Random Random = new Random();

    public ProblemFactory() {
        LoadLevels();
    }

    protected abstract Problem Generate();
    protected abstract IConfiguration ConfigureEasyLevel();
    protected abstract IConfiguration ConfigureMediumLevel();
    protected abstract IConfiguration ConfigureHardLevel();

    private void LoadLevels() {
            Configurations.Add(Levels.Easy, ConfigureEasyLevel();
            Configurations.Add(Levels.Medium, ConfigureMediumLevel();
            Configurations.Add(Levels.Hard, ConfigureHardLevel();
    }
} 

And here is a concrete class about create addition problems, check how I'm overriding some ConfigureXLevel from the abstract ProblemFactory and returnsIConfiguration.

public class AdditionProblemFactory : ProblemFactory
{
    public override Problem Generate() {
        int x = //.. x must receive a random number according to the configuration selected for the level
        int y =  //.. 
        Operators op = Operator.Addition

        return BinaryProblem.CreateProblem(x, y, op);
    }

    protected override IConfiguration ConfigureEasyLevel() {
        // the same of ConfigureMediumLevel() but with others values
    }

    protected override IConfiguration ConfigureMediumLevel() {
        BinaryProblemConfiguration configuration = new BinaryProblemConfiguration();
        configuration.Bound1 = new Bound<int>(100, 1000);
        configuration.Bound2 = new Bound<int>(10, 100);

        return configuration;
    }

    protected override IConfiguration ConfigureHardLevel() {
        // the same of ConfigureMediumLevel() but with others values
    }
}

public class BinaryProblemConfiguration : IConfiguration
{
    public Bound<int> Bound1 { get; set; }  //Bounds for Number1 of a binary problem
    public Bound<int> Bound2 { get; set; } // Bounds…    Number2  …
}

The matter is in the Generate method from AdditionProblemFactory and TimesTablesProblemFactory, x, y should receive random numbers according to the Level IConfiguration.

Bound class contains Min and Max values. For example is I select Levels.Medium, I must receive a problem with the specific range or bound in Number1 and Number2 (Number 1 + Number 2 = X )

    AdditionProblemFactory factory = new AdditionProblemFactory();
    BinaryProblem problem = (BinaryProblem)factory.Generate(Levels.Medium);

Here is the part which I don't know what should I modify in the design. Random is on ProblemFactory, but maybe is better to move the variable to IConfiguration and generate numbers there.

If you prefer to download it. Don't worry, it's so small. http://www.mediafire.com/?z5j9hu1szpuu2u5


Solution

  • By doing some refactoring, I would suggest creating the following class hierarchy:

    public interface IConfiguration
    {
         Bound<int> Bound1 { get; }
         Bound<int> Bound2 { get; }
    }
    
    public class EasyLevelConfiguration : IConfiguration
    {
        public Bound<int> Bound1
        {
            get { return new Bound<int>(100, 1000); }
        }
    
        public Bound<int> Bound2
        {
            get { return new Bound<int>(10, 100); }
        }   
    }
    

    and change the implementation of your ProblemGeneratorFactory as follows:

    public override Problem Generate(IConfiguration configuration)
    {
        int x = this.Random.Next(configuration.Bound1.Max); //use value from configuration
        int y = this.Random.Next(configuration.Bound2.Min); //use value from configuration
        Operators op = Operator.Addition
        return BinaryProblem.CreateProblem(x, y, op);
    }
    

    Alternatively, you can provide the various IConfiguration objects in the constructor for the ProblemFactory and have different instances of it for various configurations.