I'm creating a factory design for math problems. The purpose of the factory is:
- The problems must be generated with a certain difficulty (or
level
). To do this, I've got an abstract method called ConfigureLevels. - I set an abstract method called
Generate
, this one must be implemented in a concrete class.
This is the factory design which I'm talking to.
Here is the abstract ProblemFactory
.
- The abstract factory provide a
Random
variable. - Also contains a
CanConfigureXLevel()
which by default returns false, but if you want that will be available, just override it to true.ConfigureXLevel()
is an abstract method which knows how to configure the level (returnsIConfigure
). - Contains a dictionary which contains the available levels (
key:=Levels
,value:=IConfiguration
which works like a container of objects usefull 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();
public virtual bool CanConfigureEasyLevel() { return false; }
public virtual bool CanConfigureMediumLevel() {return false; }
public virtual bool CanConfigureHardLevel() {return false; }
protected abstract IConfiguration ConfigureEasyLevel();
protected abstract IConfiguration ConfigureMediumLevel();
protected abstract IConfiguration ConfigureHardLevel();
private void LoadLevels()
{
if (CanConfigureEasyLevel()) {
Configurations.Add(Levels.Easy, ConfigureEasyLevel());
}
if (CanConfigureMediumLevel()) {
Configurations.Add(Levels.Medium, ConfigureMediumLevel());
}
if (CanConfigureHardLevel()) {
Configurations.Add(Levels.Hard, ConfigureHardLevel());
}
}
private void Configure(Levels level)
{
if (!Configurations.ContainsKey(level)) throw new InvalidOperationException("Level not available");
// ..
}
}
And here is a concrete class, check how I'm overriding some ConfigureXLevel
from the abstract ProblemFactory
. The factory create additions problems, and it should know how to calculates.
public class AdditionProblemFactory : ProblemFactory
{
public AdditionProblemFactory(Levels level)
: base(level) { //.. }
public override Problem Generate(IConfiguration configuration)
{
int x = //.. x must receive a random number according to the configuration
int y = //..
Operators op = //..
return ProblemA.CreateProblemA(x, y, op);
}
public override bool CanConfigureMediumLevel()
{
return true;
}
protected override IConfiguration ConfigureEasyLevel()
{
throw new NotImplementedException();
}
protected override IConfiguration ConfigureMediumLevel()
{
BinaryProblemConfiguration configuration = new BinaryProblemConfiguration();
configuration.Bound1 = new Bound<int>(2, 10);
configuration.Bound2 = new Bound<int>(2, 10);
configuration.Operators = new List<Operators>() { Operators.Addition, Operators.Subtraction, Operators.Multiplication, Operators.Division };
return configuration;
}
protected override IConfiguration ConfigureHardLevel()
{
throw new NotImplementedException();
}
}
And this is a BinaryProblemConfiguration
public class BinaryProblemConfiguration : IConfiguration
{
public Bound<int> Bound1 { get; set; }
public Bound<int> Bound2 { get; set; }
public List<Operators> Operators { get; set; }
public BinaryProblemConfiguration() { }
public BinaryProblemConfiguration(Bound<int> bound1, Bound<int> bound2, List<Operators> operators)
{
this.Bound1 = bound1;
this.Bound2 = bound2;
this.Operators = operators;
}
}
The matter is in the Generate method
from AdditionProblemFactory
and TimesTablesProblemFactory
, x, y and operator variables
should receive random numbers according to the Level IConfiguration.
Here is the part I don't know what I should modify of the design. Maybe is better move the random variable to IConfiguration and generate numbers there.
Here is the current project, don't worry it's so compact (Factories => Infrastructure.FactoryCore | Configurations => Infrastructure.ConfigurationCore | BinaryProblem => ExerciseA):