which pattern is most intuitive for a calculator app?

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.

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.

6

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);
}

2

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.

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.

2

I would start with factory and command pattern like this:

class Program
{
    static void Main(string[] args)
    {
        CalculatorFactory calculatorFactory = new CalculatorFactory();
        var arithmeticCalculator = calculatorFactory.GetCalculator<ArithmeticCalculator>();
        IOperation addOperation = new AddOperation(arithmeticCalculator, new List<double> { 2, 3 });          
        IOperation subtractOperation = new SubtractOperation(arithmeticCalculator, new List<double> { addOperation.Execute(), 3 });
        Console.WriteLine(subtractOperation.Execute());
        var trigonometricCalculator = calculatorFactory.GetCalculator<TrigonometricCalculator>();
        IOperation sinOperation = new SinOperation(trigonometricCalculator, 90);
        Console.WriteLine(sinOperation.Execute());
        Console.ReadLine();
    }
}

class AddOperation: IOperation
{
    private ArithmeticCalculator arithmeticCalculator;
    public List<double> Operands { get; set; } = new List<double>(2);

    public AddOperation(ArithmeticCalculator arithmeticCalculator, List<double> operands)
    {
        this.arithmeticCalculator = arithmeticCalculator;
        Operands = operands;
    }

    public double Execute()
    {
        return arithmeticCalculator.Add(Operands[0],Operands[1]);
    }
}
internal interface IOperation 
{
    List<double> Operands { get; set; }
    double Execute();
}

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật