Problem
I’m having a hard time creating a design (due to my lack of experience). I need a little of help to create the Model.
It’s a mathematics system. I’ve created an IProblem
interface with a function IsCorrect
, this function helps me to know if the result is correct or not (by parameter). It also has a function CreateAnswer()
because some problems have 1, 2 or many answers.
public interface IProblem
{
bool IsCorrect(IAnswer answer);
IAnswer CreateAnswer();
}
IAnswer
is an interface which contains the answer, it can be a string, integer, decimal, several, multiple choice, etc..
public interface IAnswer
{
}
public class IntegerAnswer : IAnswer
{
public int Number { get; set; }
}
public class StringAnswer : IAnswer
{
public string Number { get; set; }
}
public class DecimalAnswer : IAnswer
{ ... }
public class MultipleChoiceAnswer : IAnswer
{ ... }
Here is a concrete Problem:
public class BinaryProblem : IProblem, IEquatable<BinaryProblem>
{
private int _number1;
private int _number2;
public int Number1
{
get { return _number1; }
set { _number1 = value; }
}
public int Number2
{
get { return _number2; }
set { _number2 = value; }
}
public BinaryProblem(int number1, int number2)
{
this.Number1 = number1;
this.Number2 = number2;
}
public bool IsCorrect(IAnswer answer)
{
IntegerAnswer integerAnswer = (IntegerAnswer)answer;
return integerAnswer.Number == _number1 + _number2;
}
public IAnswer CreateAnswer()
{
return new IntegerAnswer();
}
public override string ToString()
{
return _number1 + " + " + _number2;
}
public override bool Equals(object obj)
{
if (obj is BinaryProblem)
return this.Equals(obj);
else
return false;
}
public bool Equals(BinaryProblem other)
{
return this.Number1 == other.Number1 &&
this.Number2 == other.Number2;
}
}
And to run this:
// Binary problem test
BinaryProblem binary = new BinaryProblem(4, 5);
IntegerAnswer answer = (IntegerAnswer)binary.CreateAnswer();
answer.Number = 9;
binary.IsCorrect(answer);
I realized it is necessary to store the correct answer. For example, if it’s a problem where the stated question is: convert decimal to fraction.
My limitation here is that I need to know the result at the moment the problem is created. I’m convinced that the Answer doesn’t belong to the problem, but also, the class needs to know the correct answer when it is created. Because 0.111 it’s confusing, it can be 1/9 or 111/1000, so I prefer to store the correct answer in some place.
So, where should I modify the model?
I created a factory where one can create problems, which contains values of necessary properties and the correct answer. I’d like to know if the performance or OOP rules are correct, or if there are things to do to improve the factory.
I’ve modified the answers by a generic class.
This is the interface for my factories:
public interface IFactory<T> where T : IProblem
{
T CreateProblem();
}
My base class factory:
public abstract class Factory<T> : IFactory<T> where T : IProblem
{
protected const int DEFAULT_SIZE = 20;
protected const int MAX_SIZE = 100;
protected static Random rnd;
protected static LinkedListNode<T> actualNode;
protected static LinkedList<T> problems;
public virtual int TotalProblemsPossible { get; set; }
protected Factory(Levels level)
{
Initialize();
ConfigureLevels(level);
FirstTime();
}
public virtual void FirstTime()
{
if (rnd == null) rnd = new Random(MAX_SIZE);
if (problems == null)
{
problems = new LinkedList<T>();
Generate(problems);
}
actualNode = problems.First;
}
public virtual T CreateProblem()
{
if (actualNode.Next == null)
{
Generate(problems);
}
actualNode = actualNode.Next;
return actualNode.Value;
}
private void Generate(LinkedList<T> problems)
{
for (int i = 0; i < DEFAULT_SIZE; i++)
{
T newProblem;
int bucles = 0;
do
{
if (bucles == 50) throw new InvalidOperationException();
newProblem = Generate();
bucles++;
} while (problems.Contains(newProblem));
problems.AddLast(newProblem);
}
}
protected virtual void Initialize()
{
}
protected abstract T Generate();
protected abstract void ConfigureLevels(Levels level);
}
And a particular factory. At this factory case, I create a class called Bound which contains limits for a number, and gets some numbers randomly to create the problem.
public class BinaryProblemFactory : Factory<BinaryProblem>
{
private Bound<int> Bound { get; set; }
public BinaryProblemFactory(Levels level)
: base(level)
{
}
protected override void Initialize()
{
Bound = new Bound<int>();
base.Initialize();
}
protected override void ConfigureLevels(Levels level)
{
switch (level)
{
case Levels.Easy:
this.Bound.Min = 10;
this.Bound.Max = 100;
break;
case Levels.Normal:
this.Bound.Min = 50;
this.Bound.Max = 200;
break;
case Levels.Hard:
this.Bound.Min = 100;
this.Bound.Max = 10000;
break;
}
}
protected override BinaryProblem Generate()
{
BinaryProblem problem;
int number1 = rnd.Next(Bound.Min, Bound.Max);
int number2 = rnd.Next(Bound.Min, Bound.Max);
Answer<int> correctValue = new Answer<int>(number1 + number2);
problem = new BinaryProblem(number1, number2, correctValue);
return problem;
}
}
The idea is to do something similar with each problem type I want to add to the program. What do you think?
For testing:
BinaryProblemFactory factory = new BinaryProblemFactory(Levels.Normal);
List<BinaryProblem> problemList = new List<BinaryProblem>();
for (int i = 0; i < 50; i++)
{
BinaryProblem binary2 = (BinaryProblem)factory.CreateProblem();
problemList.Add(binary2);
}
Solution
Firstly, do you really need it to be that generic? You support arbitrary types and numbers of answers. But unnecessary genericness just make the design overly complicated. If you are only going to every have a single number answer, then just write code to do that.
Secondly, you store the answers on the Problem object. But the answers given are not a property of the problem. It makes more sense to have the problem take in the answers as a parameter to the IsCorrect() function.
Thirdly, your problem interfaces don’t appear very useful. It seems that I get access and presumably modify the given answers, and then check if the answers are correct. But what about finding out what the problem is? There’s not much point in an interface if you are just going to have to cast down to the actual class to do any work.
Fourthly, your provide getters/setters on the numbers and such for the problem. Do you really need them? Are you really changing the problems after they are created?
Fifthly, an obvious way to use OOP here would be to one class for each operator (multiple, divide, add, subtract, etc).
The way I’d approach this:
interface IProblem
{
String GetQuestion();
bool CheckAnswer(int answer);
}
class BinaryOperatorProblem : IProblem
{
int number1, number2;
BinaryOperatorProblem(int number1, int number2);
}
class AdditionProblem
{
String GetQuestion()
{
return "%d + %d" % (number1, number2);
}
bool CheckAnswer(int answer)
{
return answer == number1 + number2;
}
}
Notes: I don’t do C# so above syntax is mostly guesses. I also don’t know much about your problem, so you mileage will vary.
Declaring the interfaces as below might proof useful:
public interface IProblem<T>
{
bool IsCorrect(IAnswer<T> answer);
IAnswer<T> CreateAnswer();
}
public interface IAnswer<T>
{
T Value { get; set; }
}
public class Answer<T> : IAnswer<T>
{
public T Value { get; set; }
}
This would allow for the following implementation:
public class BinaryProblem : IProblem<int>, IEquatable<BinaryProblem>
{
private int _number1;
private int _number2;
// .....
public bool IsCorrect(IAnswer<int> answer)
{
return answer.Value == _number1 + _number2;
}
public IAnswer<int> CreateAnswer()
{
return new Answer<int>();
}
//....
}
And can save you a ton of descendants, reduce the amount of knowledge you need within an IProblem
implementation, while still offering the possibility of creating very specialized IAnswer
implementations.