0

The title says it all. I'm trying to build a calculator application (for self-learning purposes). The application is going to have a very common UI, with plus(+), minus(-), multiply(*) and a divide(/) button. Also, the app can do, real-number as well as complex-number calculations.

So, the situation here is, depending upon the mode(normal or complex), the same button should perform different calculation ? Which pattern should I be using for this situation ? I feel, strategy pattern should be a good fit, but then, I don't exactly know how to implement that - I mean, I'm just not sure about how to design my classes, what to have as Interfaces, and what as delegates.

CURRENTLY, MY DESIGN CONTAINS


IOperation
{
  Do();
}
Add:IOperation{}
Subtract:IOperation{}
Multiply:IOperation{}
Divide:IOperation{}
Root:IOperation{} //not supported by ComplexNumber
ISupportedOperation 
{ 
   IList<IOperation> SupportedOps {get;}
}
INumber : ISupportedOperation {}
RealNumber:INumber{}
ComplexNumber : INumber {}
  1. Interface names starts with I
  2. All others are concrete classes

BUT IT'S A MESS. And, I'm totally lost within my own classes, and interfaces.

PS: Of course, I can do this using if/else, but that's not what I want to do. Not because I forcefully want to use a pattern, but because, those if-else will be scattered everywhere in the program for eg. ReadInput, PlusButtonClick, MinusButtonClick, etc. And, as I understand, design patterns are supposed to avoid these kind of situations of code-repetitions, by clever/tricky re-organization of the existing code.

PPS: Sorry for being too verbose.

  • 1
    Your interface could be named ICalculate and just have a Calculate() method. IOperation and Do() seem awkward. – Pete Sep 26 '14 at 4:17
  • I think operation is fine, but the do method should be called "eval" or similar. Also, IValue eval(IValue a, IValue b) perhaps? – StarWeaver Sep 26 '14 at 5:02
  • Agreed about the name. But another problem is, how do I link the Add/Subtract class to RealNumber and ComplexNumber class ? It's all intermangled like a spider web. :) – MrClan Sep 26 '14 at 5:29
  • 1
    I think the command pattern (that's what it looks like you're going for, but you just took a wrong left turn somewhere) is right for your use case. It might bulk up your code base, but you get cohesion and reusability also – kolossus Sep 26 '14 at 15:48
  • @kolossus Exactly, what I've been feeling. Can you help me find out that wrong turn ??? Any wild guesses you wanna make :) – MrClan Sep 26 '14 at 15:54
6

You're doing this backwards. Forget about the design patterns for awhile.

Just build your calculator program. Build it from scratch, and just design it however it is intuitive to you. Think about it for a few minutes, come up with a few ideas, then roll with it, start to finish.

While you are trying to implement your calculator, you might not design it right the first time and will have to restructure it now and then as you learn more about what's involved in your design. This is the process of learning, and this will teach you about how you've structured your code to attack the problem at hand.

Then, after you've done that, then take a look at the design patterns described in the GoF book, and see what patterns most closely describe how you ended up designing your calculator. Now you know what those patterns are used for, and now you'll know how to choose what pattern makes sense to apply when.

Design patterns are not building blocks, they are names for common structural paradigms, to improve communication with other developers.

  • well said, every time I see a question like this I shiver as they're all utterly useless. Not as bad as "what app should I write with the XXX pattern" but close. – jwenting Sep 30 '14 at 6:55
  • @whatsisname Everything you said is totally valid, and I agree with it 100%. But just so that you know, I'm beyond that phase - I'm not a beginner. Also, I've an already functioning calculator winforms app using C#, written by myself. But currently, all that I know is, my code is what anyone would call spaghetti code at first glance. So, my prime (and for now, only) objective is, *to make my code more beautiful (where, beautiful = OOP friendly, and more standard i.e. using recommended practices) . And that is where I'm confused, and stuck. – MrClan Sep 30 '14 at 16:32
  • @jwenting you sir, are just rude. I'm not sure, if you even read the question fully. Or simply started ranting after just looking at the title. – MrClan Sep 30 '14 at 16:38
  • 1
    @MrClan: in that case, don't make your code more beautiful, make something more complex. As you develop the skills to manage that complexity, the understanding of the design patterns will emerge in your brain along with it. OOP is a paradigm designed to help manage complexity. It won't make sense unless you are using it for a sufficiently complex task. – whatsisname Sep 30 '14 at 16:42
  • @whatsisname that kinda' makes sense...maybe I'm using it for something too simplistic. Actually, my plan was to emulate Casio Fx-911 model in C#, which was just too ambitious initially, but I think I'm going to have a go at it :) . – MrClan Sep 30 '14 at 16:59
4

First of all, a simplification: all real numbers are a subset of complex numbers, so just do everything with complex numbers.

Secondly, why isn't Root supported by ComplexNumber? If you're supporting complex numbers, then you can take the root of any number, including negative or complex ones. It all just becomes vector multiplication. So if it's all complex numbers, then you don't need to have ISupportedOperation, since every operation is supported (short of divide by zero, of course, but we usually handle that by throwing an exception).

Really, this should do:

interface ICalculator
{
    IComplexNumber Add(IComplexNumber a, IComplexNumber b);
    IComplexNumber Subtract(IComplexNumber a, IComplexNumber b);
    IComplexNumber Multiply(IComplexNumber a, IComplexNumber b);
    IComplexNumber Divide(IComplexNumber a, IComplexNumber b);
    IComplexNumber Root(IComplexNumber a, IComplexNumber b);
}

Remember, too, that a square root is just a^(1/2), so a more generic interface would be:

interface ICalculator
{
    IComplexNumber Add(IComplexNumber a, IComplexNumber b);
    IComplexNumber Subtract(IComplexNumber a, IComplexNumber b);
    IComplexNumber Multiply(IComplexNumber a, IComplexNumber b);
    IComplexNumber Divide(IComplexNumber a, IComplexNumber b);
    IComplexNumber Power(IComplexNumber a, IComplexNumber b);
}
  • Many thanks for your answer, it clarifies many things. But, how do I read input from the UI ? since, the format for a real or complex number will be different, and separate methods will have to be called for parsing ? Also, how do I link these methods to a button_click event of a typical winforms application ? Just to be clear, I'm not asking for codes, just the object graphs (interface and class definitions, like what you showed above). Don't you think, this app has scope for delegates, since the Add/Subtract/Multiply has very similar looking method-definitions ? – MrClan Sep 26 '14 at 15:21
  • 1
    @MrClan - the typical UI architecture is one of MVC or MVP. What I've described in ICalculator is the Model. The View would be the Form you build using Winforms. You typically have a separate class called the Controller or Presenter. In the event handlers, you call methods on the Controller. It makes calls to the Model to get answers, and updates the View. How much goes in the Model vs. Controller would be up for some debate. – Scott Whitlock Sep 26 '14 at 16:45
1

I'll take a different approach actually. I'll suggest you use a strategy pattern (pseudo code - been a while since I used C#).

class CalculatorContext {
   private List<Number> numberHistory;
   private Number currentDisplay;
   private List<Operation> commandHistory;
   private Operation currentCommand;

   public void evaluateAnswer(Operation op) {
       currentCommand = op;
       commandHistory.put(op);
       Number returnVal = currentCommand.execute(this);
       numberHistory.put(currentDisplay);
       currentDisplay = returnVal;
   }

   //Getters/setters.
}

interface Operation {
   public Number execute(CalculatorContext ctx);
}

class AddOperation : Operation {
   public Number execute(CalculatorContext ctx) {
      //Current number to add
      Number a = ctx.getCurrentDisplay();

      //Previous number to add
      assert(ctx.getNumberHistory() != null && ctx.getNumberHistory().size() > 1);
      Number b = ctx.getNumberHistory().last();

      return a + b;
    }
}

The reason I like that approach is you could modify it to handle parenthesis pretty easy but also it reflects how a real calculator works (a current state then modified via operations). In the method where you don't use a queue-like approach you then need to manage that in a different and much more painful way from the UI. Things like square root become a problem, whereas here you only get "A", not A + B. You could use a 'power' like above but the problem with that is - presumably - you don't want the CalculatorContext or the UI to be aware of the specifics of that operation. So I'd probably handle it in the operation and if it became burdensome collecting the parameters I'd build helper methods for it.

  • interesting approach, I'll give it a go, and definitely let you know :) !!! – MrClan Sep 30 '14 at 16:50
1

How about this...

interface IOperator
{
    double Calculate(double num1, double num2);

    // ComplexNumber Calculate(ComplexNumber num1, ComplexNumber num2); // up to you
}

class AddOperator : IOperator
{
    public AddOperator() { }

    public double Calculate(double num1, double num2)
    {
        return num1 + num2;
    }

    // ComplexNumber Calculate(ComplexNumber num1, ComplexNumber num2) { ... }
}

class MinusOperator : IOperator
{
    public MinusOperator() { }

    public double Calculate(double num1, double num2)
    {
        return num1 - num2;
    }

    // ComplexNumber Calculate(ComplexNumber num1, ComplexNumber num2) { ... }
}

// ...

Factory use to create instance:

static class Factory
{
    public static T CreateInstance<T>() where T : new()
    {
        return new T();
    }
}

Client:

static void Main(string[] args)
{
    IOperator op = Factory.CreateInstance<AddOperator>();
    Console.WriteLine(op.Calculate(1, 2));

    // Console.WriteLine(op.Calculate(complexNum1, complexNum2));

    Console.Read();
}

Now, if I want to add an operator, just add a suboperator class and implement the IOperator. Overload method will determine calculate simple or complex.

This is a update version base on Facotry Method use Generic Type.

  • 1
    don't do peoples' homework for them... – jwenting Sep 30 '14 at 7:07
  • 3
    @jwenting sometimes writing the code is the best and the most expressive way to express what you're trying to say. So, just because you see a code somewhere, doesn't mean that you should blindly point fingers at the poster. – MrClan Sep 30 '14 at 16:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.