RAD Studio 10.4.1 is now available! Learn more. Looking for discounts? Visit our Special Offers page!
News

Learn to Program in Delphi – Putting the Calculator Together

Author: Vanners

Welcome to the last episode in the “Learn to Program with Starter” series. In the first four episodes we have covered “Introduction and Installation” of the free Delphi Starter edition, then we moved to IDE basics in “Building in Debugging”. In “Architecture and Layers of Code” the key concepts of proper app structure were discussed and in the last episode we have started “Designing User Interfaces”.

There is also “Learn to Program in C++” series written by C++Builder Product Manager David Millington, who already has finished the series with the final blog post covering C++ operators and final application.

Coding in C++ and Object Pascal is different. Initially I was tempted to implement the calculator here in my “own way”, but I have realised that for some developers it could be interesting to be able to compare how the two languages can be used to implement the very same UI and code design. Consequently I have slightly modified the calculator UI that I have created in the previous episode so it matches David’s.

The final source code of the “Delphi Super Calculator” project can be downloaded from my Amazon S3.

Application Structure

Here is the screen shot from Project Manager and you can see that the calculator project contains one main form and five units with code.

In the previous episode we have created the calculator project with the main form with 20 buttons and a label that acts as a “display”. It is very important to separate user interface code from the application logic. The best way to achieve this separation is by using interfaces. Trying to match David’s design I have added to my project a new “uInterfaces” unit and defined two interfaces there. One “ICalculator” that my “TCalculator” class implements and one “ICalculatorDisplay” that is implemented by the main form. These two interfaces are the only way that the UI communicates with the application logic.

 
unit uInterfaces;

interface

type
  ICalculatorDisplay = interface
    procedure UpdateUI(strText: string);
  end;

  TOperatorEnum = (opNull, opAdd, opSubtract, opMultiply, opDivide);

  ICalculator = interface
    procedure AddDigit(digit: integer);
    procedure AddDecimalSeparator;
    procedure SetOperator(op: TOperatorEnum);
    procedure Equals;
  end;

implementation

end.

You can remove the sample code from the “TCalculator” class that was added earlier for demo purposes and we can start implementing the calculator for real.

unit uCalculator;

interface

uses
  uInterfaces;

type
  TCalculator = class(TInterfacedObject, ICalculator)
  private
    FDisplay: ICalculatorDisplay;
    // ...
  public
    constructor Create(ADisplay: ICalculatorDisplay);
    destructor Destroy; override;
    // ICalculator
    procedure AddDigit(digit: integer);
    procedure AddDecimalSeparator;
    procedure SetOperator(op: TOperatorEnum);
    procedure Equals;
  end;

implementation

constructor TCalculator.Create(ADisplay: ICalculatorDisplay);
begin
  FDisplay := ADisplay;
// ... end; // ...

Our “TCalculator” class implements an interface and that is why it cannot be inherited directly from “TObject”, but it needs to be derived from “TInterfacedObject” which is the base class for all Delphi classes that implements interfaces. If you do not specify the base class for your object, you will be inheriting from “TObject”. Similarly all Delphi interfaces derives from “IInterface” and the “TInterfacedObject” class just implements these inherited methods. The constructor of our “TCalculator” class takes a reference to “ICalculatorDisplay” and stores it in the private field, so any time calculator needs to display something, it can just call “UpdateUI” method and pass the string to be displayed. This is good design. The calculator class knows about the UI as little as possible. It just needs to be able to pass a string for display. This could be a FireMonkey, VCL or maybe even a console app.

uses
  uInterfaces, // ...

type
  TFormCalc = class(TForm, ICalculatorDisplay)
    //..
  private
    FCalculator: ICalculator;
  public
    // ICalculatorDisplay
    procedure UpdateUI(strText: string);
  end;

var
  FormCalc: TFormCalc;

implementation

{$R *.fmx}

uses uCalculator;

procedure TFormCalc.FormCreate(Sender: TObject);
begin
  FCalculator := TCalculator.Create(self);
end;

procedure TFormCalc.UpdateUI(strText: string);
begin
  LabelDisplay.Text := strText;
end;

// ..

The main form also contains the reference to the “ICalculator” class as a private field. The actual “uCalculator” unit reference is in the “implementation” uses clause of the form, so this is as clean as possible. The code in the main form only needs to know about methods of the “ICalculator” interface to communicate with the application logic.

So far this roughly covers the C++ code that David discusses in “Learn to program with C++Builder: #3, Design, Architecture, and UIs” blog post.

The next two episodes go into the inner workings of C++ and Object Pascal just do things differently. My goal was to mimic in Delphi the code from C++. There is no concept of “smart pointers” in Object Pascal. There are constructors and destructors that you need to call to instantiate the object and to free it.

There is also no “boost” library in Delphi and no optional types that provide nullable semantics for both simple and object types. Object Pascal treats built-in types differently than C++. The Object Pascal “nullable” types are planned for the future – according to RAD Studio official roadmap – but still not there. In order to model to some extent “boost optional” type there is “uOptionals” unit in the project in Delphi project that is using a generic “TOptional<T>” type to model optional integers and optional doubles that C++ “TCalculator” class uses.

unit uOptionals;

interface

type
  TOptional<T> = record
    Value: T;
    Exists: boolean;
    procedure Reset;
  end;

  TOptionalInt = TOptional<integer>;
  TOptionalDouble = TOptional<double>;

implementation

procedure TOptional.Reset;
begin
  Exists := False;
end;

end.

It it very primitive, but to some extent mimics C++ boost “optional” semantics.

Another interesting type in the implementation is the polymorphic “operator” class that performs an operation on two double values and return result as double. It also has the “Name” function that is used to display the symbol of a given operation.

 
unit uOperator;

interface

uses
  uInterfaces;

type
  TOperator = class abstract
    function Calc(A, B: double): double; virtual; abstract;
    function Name: string; virtual; abstract;
  end;

  TOperatorAdd = class(TOperator)
    function Calc(A, B: double): double; override;
    function Name: string; override;
  end;

  TOperatorSubtract = class(TOperator)
    function Calc(A, B: double): double; override;
    function Name: string; override;
  end;

  TOperatorMult = class(TOperator)
    function Calc(A, B: double): double; override;
    function Name: string; override;
  end;

  TOperatorDiv = class(TOperator)
    function Calc(A, B: double): double; override;
    function Name: string; override;
  end;

function CreateOperator(op: TOperatorEnum): TOperator;

implementation

function CreateOperator(op: TOperatorEnum): TOperator;
begin
  case op of
    opAdd: Result := TOperatorAdd.Create;
    opSubtract: Result := TOperatorSubtract.Create;
    opMultiply: Result := TOperatorMult.Create;
    opDivide: Result := TOperatorDiv.Create;
  else
    Result := nil;
  end;
end;

function TOperatorAdd.Calc(A, B: double): double;
begin
  Result := A + B;
end;

function TOperatorAdd.Name: string;
begin
  Result := '+';
end;

function TOperatorSubtract.Calc(A, B: double): double;
begin
  Result := A - B;
end;

function TOperatorSubtract.Name: string;
begin
  Result := '-';
end;

function TOperatorMult.Calc(A, B: double): double;
begin
  Result := A * B;
end;

function TOperatorMult.Name: string;
begin
  Result := '*';
end;

function TOperatorDiv.Calc(A, B: double): double;
begin
  Result := A / B;
end;

function TOperatorDiv.Name: string;
begin
  Result := '÷';
end;

end.

In Object Pascal we can just define a class with virtual abstract methods that acts as a blueprint for specialized classes that provide implementations for inherited abstract methods. There is also a global factory function that returns correct “TOperator” compatible instance type.

The rest of the project code is almost identical as the C++ simple calculator version available from GitHub. The final source code of the “Delphi Super Calculator” project can be downloaded from my Amazon S3

That’s it! I hope that you have you enjoyed the “Learn to Program with Delphi” series as much as I did.


It is time for new challenges. In coming days I will be doing two global webinars that you are all invited for. You can register below:

  • Dec 14th: Appx Development for Windows 10 Store
  • Dec 20th: Migrating to RAD Server

 


Reduce development time and get to market faster with RAD Studio, Delphi, or C++Builder.
Design. Code. Compile. Deploy.
Start Free Trial   Learn More About Upgrading

Related posts
C++

What's New in the GetIt Package Manager - June 2020

C++

RAD Studio 10.4 Now Available, Learn More

C++NewsRAD Studio

Open for Business with 10.4! - May 2020 GM Update

C++DelphiNews

Embarcadero Open Source Project Sponsorship

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

IN THE ARTICLES