Morfik CSharp Object Oriented Programming

We will start our trip through the world of Object Oriented Programming (OOP) with Morfik C#, right at the beginning, with a quick review of what exactly is OOP. This chapter will provide you will a general overview of Morfik C#


What is Object Technology?

In a very simple definition, Object Technology (OT) is a set of methodologies for analysis, design and programming whose goal is to model the characteristics and behaviors of real world objects. In this chapter we will focus on the programming aspects of Object Technology, more commonly known as Object Oriented Programming.

Object Oriented Programming

Morfik C# is built on the most common dialects of C# found in the world today, with some added features. Over the recent years C# has had great advances as a language and Morfik C# reflects that. Through all of its development, one of the main goals in creating Morfik C# was to provide a powerful, yet easy to use language for the development of Web applications. Towards that end, Morfik C# was developed into a powerful object oriented language which can hold its own against languages which much more widely know for their OOP features.

Today, no large scale application development project is undertaken without the use of OOP. All major tools and languages support the concepts and they have been widely accepted into mainstream for several years.

In order to be considered Object Oriented, a language must have built-in support for three concepts:

Encapsulation – Modularization and the ability to hide implementation details.

Inheritance – Defining characteristics and implementation objects based on pre-existing classes so that code can be easily and successfully reused

Polymorphism – the same message, when sent to different objects provokes different responses, depending on the nature of the object that receives the message.

What Are Objects?

Objects are models we create to represent real world entities, in software design. In everyday life you are surrounded by object all the time. Look around and you will see: cars, refrigerators, chairs, cats and dogs. All of these are objects.

Software applications also have objects such as buttons in the interface, grids and their cells, menus, string lists, etc. These objects, just as their real world counterparts, have states and behaviors. You can represent these characteristics (and their change over time – ie. states) and behaviors with software structures which are generally called objects.

In its daily comes and goings a car can be modeled as a car. An object has characteristics, which change over time defining its states (speed, aim, fuel consumption, etc) and behaviors (engine off, engine on, accelerate, turn left, break, etc.).

You drive to the office where you work with clients. The way you interact with clients can, also, be modeled through an object. The client also has characteristics (Current Phone, Current Address, etc) and behaviors (Move, Close Deal, etc).

Basic Concepts

In programming, an object’s characteristics and state are defined by its instance variables (sometimes called member variables, or fields). Instance variables should always be private so as to not be accessible from outside the of the object. Public instance variables are accessible by code which does not belong to the object’s class. This should be avoided as we will see when looking into encapsulation.

The behaviors of an object are defined by its methods. Methods handle all interaction with and operate on the instance variables, altering the object’s state and even creating new objects.

In figure 1 you can see a simple graphical representation of an object. This picture represents the conceptual structure of an object, which is very similar to that of a cell. As in a cell, an external structure (the membrane) is the only part to have contact with the outside world and protects the nucleus.


conceptual-object.jpg
Figure 1: Graphical representation of an Object.


The object’s instance variables are encapsulated within it, surrounded by the object’s methods. Except on very specific cases the object’s methods should be the only the only venue available for other objects to access or alter instance variables. In Morfik C# it is possible to have instance variables declared as public or published, except where absolutely necessary (like for IDE interaction with a Form’s controls) this should be avoided as these variables will be globally available to other objects. Access levels and member visibility will be seen in greater detail in this chapter.

Encapsulation

The basic idea behind the concept of Encapsulation is that the less that a user of objects you create know about how they are implemented, the less he/she will be affected by changes to that implementation.

A person that knows nothing about cars does not conclude anything about certain noises that the engine makes. A person with a bit more experience could be tempted to guess at the cause of a problem based on such noise and be frustrated by a new type of engine.

Please, don’t image that this will help in hiding problems with your code. The goal is to avoid problems by avoiding that developers create code that depends on how your objects are implemented. This allows you to change how the objects are implemented without adversely affecting code created by third parties.

Inheritance

To put it simply, inheritance is the capability of creating new types of objects (called Classes) from previously existing types. With inheritance we re-use a previously existing class’ (ancestor) functionality, add new functionalities and, if necessary, alter some of the class’ behavior, thus creating a new Class (descendant) with a huge part of its functionality already tried and tested.

Before the advent of object oriented programming and the widespread use of inheritance, code reuse was mostly done by the creation of function libraries or by the old cut-and-paste method. The cut-and-paste approach to code reuse normally requires that the copied code suffer small modifications and is very prone to introduce bugs in an application.

Polymorphism

Polymorphism is the ability to treat objects of different types as if they were all of the same type, provided that they have a common ancestor. An example of this capability is that I can write a function which takes a Form object as a parameter and then pass any of the forms in an XApp to that function. This is possible because all forms in an XApp are descendant classes of the Form class.

Classes

Morfik C# has full support for object oriented programming. When adapting C# to work in the Morfik universe, a large number of features where added to the language because Morfik is a hybrid Object-procedural language instead of being pure object oriented. Where the feature did not exist in C# Morfik introduced it trying to be as close to the corresponding feature in C++, which is the immediate ancestor of C# in the C family of languages, as possible.

Classes are essentially the templates from which we create new objects in our applications. A class represents the definition of what instance variables and methods a certain type of object will have. A class is not an object in itself, but the recipe to create new objects. Real objects are created by creating them from a class definition in a process called instantiation. This is very similar to how you can create several cakes from the same recipe.

In the Morfik Framework, except for a few of the higher level classes such as the Form and visual controls, all class names are prefixed with the letter "T". This is a convention from the Pascal world which was adopted by Morfik; its purpose is to make it simpler to differentiate when you are dealing with a class and when you are dealing with an object (an instance of a class).

Class Declarations

Class declarations in Morfik C# are very similar to their counterparts in C#, in keeping with the spirit of making the learning of the syntax as easy as possible for developers coming from a C# background. You can see a class declaration in Listing 1.

Listing 1 – A class declaration in Morfik C#

    Public Class BaseClass : TObject{
        Private String fSomething;
 
        Public Void BaseClass(String aSomething){
            Base.new();
            fSomething = aSomething;
            ShowMessage("creating BaseClass...");
        }
 
        Public Void ~BaseClass(){
            ShowMessage("destroying BaseClass...");
            Base.dispose();
        }
 
        Public String GetSomething(){
            Return fSomething;
        }
 
    }


The class presented in this sample is a very simple one.

The first line of code in Listing 1 reads as:

    Public Class BaseClass : TObject


This informs the compiler that the class called BaseClass is a descendant of the TObject class.

Member elements of a class can be variables and functions as well as properties. Variables declared inside a class are called instance variables (or fields, or member variables), while functions and subs are called methods. We will see more about properties further ahead in this chapter.

All members of a class can be declared with on of several scope modifiers. These include: Private, Protected, Public and Published. The following table describes de scope defined by each of these modifiers.


Visibility Modifier Scope
Private Restricted to the current class, but can be accessed by other elements declared within the same module.
Protected Restricted to the current class, but can be accessed by other elements declared within the same module and from within descendants of this class.
Public Accessible from all modules in the project, which have imported the current module.
Published The same as Public but used for the declaration of Controls and Event Handlers which are accessed by the visual editors within the Morfik IDE.


Method declarations using these modifiers take the following form:


Private String Test() {}
 
Protected String Test() {}
 
Public String Text() {}
 
published message Void MyEventHandler(TDOMEvent Event) {}


Note that methods which are created to handle events triggered by controls are not only declared as Published but they also get an additional modifier: "message".

Instantiating Objects from a Class

The process of creating objects is called instantiation. We instantiate an object based on a class definition. To better illustrate this lets take a simple example. In listing 2 you can see the definition of a simple class, called TPoint. This class defines objects capable of describing a point in a two dimensional space.

Listing 2 – TPoint Class definition.

Namespace geometry
{
    Public Class TPoint : TObject
      Private Integer X;
      Private Integer Y;
      Public Void Move(Integer XValue, Integer YValue){
         X = XValue;
         Y = YValue;
      }
    }
 
    Public TPoint NewPoint(){
      TPoint Pt; 
      Pt = New TPoint();
      Pt.Move(10, 10);
      Return P;
    }
}


Notice that in Listing 2 there is a function called NewPoint. This function instantiates a new TPoint object through the use of the New operator and assigns it to a local variable called Pt. Once the object has been created its methods can be called as the call to the Move method demonstrates. This call, based on the methods definition, should set the X and Y instance variables of the object to the value of 10.

Message Passing and Methods

If object A wants object B to perform some action on its behalf, in OT jargon, it should send a message to object B. The message is sent by calling one of object B’s methods. It should be noted that calling a method in Morfik C# is very similar to calling a sub routine or a function. In some programming languages methods are called member functions of a class. This should give you a general idea of how close the concepts are.

conceptual-message-between-objects.jpg
Figure 2: Message sending/Method invocation between objects

Constructors and Destructors

Class instance variables normally require some initialization code and, also frequently, some clean up code. This initialization and clean up are done through two special kinds of methods: Constructors and Destructors. Constructors are the methods which are called when a class instance variable is created, while destructors are called whenever a class instance is destroyed.

Listing 3 – A Morfik C# class implementing a Constructor and a Destructor.

Public Class TestClass {
 
  Public Void TestClass(){
     Base.New();    // call to the ancestor's Constructor
     //...          instance initialization
  }
 
  Public Void ~TestClass(){
    //...              instance clean up code
    Base.Dispose();        // call to the ancestor's Destructor
  }
 
}


From within a Morfik C# class method you can call a method that is inherited from the ancestor (also called Base) class, through the use of the Super identifier as shown in Listing 3.

Method Overload

Morfik C# supports having more than one method, within the same class, with the same name, as long as they have different parameter lists. The different methods are implicitly identified by the compiler through the complete method signature which is composed of the method name plus its parameter list.

Let us bring together the concepts of a constructor and of method overloading to demonstrate how they can be used. In Listing 4 we extend a bit the definition of out TPoint class to include two constructors, thus using method overload. We also introduce a second function, called NewPoint2, for the creation of TPoint objects. This second function uses the constructor definition that takes two integers as parameters, instead of calling the one that takes no parameters and then calling the Move method.

Listing 4 – TPoint Class definition, revised.

Namespace geometry
{
    Public Class TPoint : TObject {
      Private Integer X;
      Private Integer Y;
      Public Void Move(Integer XValue, Integer YValue){
         X = XValue;
         Y = YValue;
      }
      Public Void TPoint(){
        Base.new();
        X = 1;
        Y = 1;
      }
      Public Void TPoint(Integer XValue, Integer YValue){
        Base.new();
        X = XValue;
        Y = YValue;
      }
    }
 
    Public TPoint NewPoint(){
      TPoint Pt;
      Pt = New TPoint();
      Pt.Move(10, 10);
      Return P;
    }
    Public TPoint NewPoint2(){
      TPoint Pt;
      Pt = New TPoint(10, 10);
      Return P;
    }
}

Forward Class Declaration

There are situations when one class in your application needs to reference another class which, in turn references the first one. When working with Morfik you always need to have any data types declared prior to their use, which would make this scenario impossible. In order to work around this problem you can use what is called a forward class declaration as can be seen in Listing 5.

Listing 5 – Usage of Forward Class Declarations

Namespace ClassForwardDeclarations
{
 
Public forward Class Class1;
 
Public Class Class2{
    Public Class1 Field1 
}
 
Public Class Class1{
    Public Class2 Field1
}
}


The insertion of the line with the forward declaration, as shown below, satisfies the compiler’s need to know all referenced type before their usage.

  Public forward Class Class1;


At this point the compiler knows that Class1 is a class and that it will be detailed later. If the actual declaration is not found when the application is compiled an error will result.

Properties

Properties look very much like instance variables and are used as those, however they are actually implemented through two sub routines, one for reading and one for setting the value that is associated with them. Normally a property will be directly related with an instance variable which holds its value.

Why use properties and not just plain instance variables? Properties have their values set and read through what we call access methods giving us encapsulation of the underlying instance variable and allowing us to take whatever appropriate action whenever their value is read or changes. In listing 6 you can see how to add properties to a class, with Morfik C#.

Listing 6 – TPoint class with added properties.

Namespace Geometry
{
    Public Class TPoint : TObject {
      Private Integer FX;
      Private Integer FY;
 
      Public Integer X {
        Get { Return FX; }
        Set { FX = X; }
      }
 
      Public Integer Y {
        Get { Return FY; }
        Set { FY = Y; }
      }
 
      Public Void Move(Integer XValue, Integer YValue){
         FX = XValue;
         FY = YValue;
      }
 
      Public Void TPoint(){
        Base.new();
        FX = 1;
        FY = 1;
      }
 
      Public Void TPoint (Integer XValue, Integer YValue){
        Base.new();
        FX = XValue;
        FY = YValue;
      }
 
    }
 
    Public TPoint NewPoint(){
      TPoint P;
      P = New TPoint();
      P.Move(10, 10);
      Return P;
    }
 
    Public TPoint NewPoint2(){
      TPoint P;
      P = New TPoint(10, 10);
      Return P;
    }
}


Note: In Morfik C# you can have properties which are read only or write only. This is done by simply not creating the code which would otherwise be responsible for handling the operation which will not be performed.
Read Only properties

You can create a read only property in a class by not implementing the Set code block as shown in the following code snippet.

    Public String Name{
        Get { Return fName; } 
    }
Write Only Properties

You can create a write only property in a class by not implementing the Get code block as shown in the following code snippet.

    Public String Phone {
        Set { fPhone = Value; }
    }

The This Parameter

Every method in a class receives an invisible parameter called This. This parameter is a reference to the exact object through which the method is being called and can be used to disambiguate variable names. Suppose that in a class with an instance variable called FX, you had a method with a local variable called FX; how do you differentiate between them? You can see a sample of how to use the This parameter in listing 7.

Listing 7 – TPoint class with added properties.

Namespace Geometry
{
    Public Class TPoint : TObject
      Private Integer FX;
      Private Integer FY;
 
      Public Integer X{
        Get {
          Integer FX;
          FX = 2*15; //assigns value to the local variable
          // unrelated code
          Return this.FX; //returns the instance variable
        }
        Set { FX = X; }
 
      }
        .
        .
        .
    }
}

Class References (Metaclasses)

Class references allow us to perform operations on Classes instead of on objects (class instances). This is very useful when you need dynamically choose what kind of class you are going to use to instantiate an object.

In Listing 8 you can see a function which based on a string parameter, the kind of food an animal eats, chooses which class of animal to instantiate.

Listing 8 – Class Reference declaration.

Namespace Animals
{
Public Class Animal{
  Public Static String FoodType(){
    Return "";
  }
}
 
Public AnimalClass Class of Animal;
 
Public Class mouse : Animal {
  Public Static String FoodType(){
     Return "cheese";
  }
}
 
Public Class cat : Animal{
  Public Static String FoodType(){
     Return "fish";
  }
}
 
Public Class dog : Animal{
  Public Static String FoodType(){
     Return "slippers";
  }
}
 
Public AnimalClass GetAnimalWithFoodType(String food){
  If (food == mouse.FoodType)  
    { Return mouse; }
  Else {
    If (food == cat.FoodType)  
      { Return cat; }
    Else {
      If (food == dog.FoodType) 
        { Return dog; }
    }
  }
}
 
Public Void TestAnimal(){
  AnimalClass MyPetClass;
  Animal MyPet;
 
  MyPetClass = GetAnimalWithFoodType("cheese");
  MyPet = New MyPetClass();
  MyPet.Free;
}
}

Inheritance

Inheritance as we have seen in a method through which we can define new and more sophisticated classes of objects, based on pre-existing classes. Let us consider another simple animal example. Most species of felines are generally referred to as cats or big cats. One could conceive that if we had an object representation of a cat it would be a good starting place to create an object representation for, say, a lion. In Morfik C#, as we have seen when we previously discussed class declarations you can specify class inheritance when you declare a new class.

In listing 9 you can see a small section of code with the declaration of the Lion class as a descendant from the Cat class.

Listing 9 – Lion and Cat classes

    Public Class Cat{
    // Cat member variables and methods
    }
 
    Public Class Lion : Cat {
    // Lion additions to the members of the Cat class
    }


In keeping to a simplistic view we can suppose that the Cat class would have all the instance variables and methods to correctly describe the characteristics and behaviors of a generic feline. This being the case all our Lion class would have to redefine is how it describes its visual representation to the outside world.


Note: Note that by typing a class name after a colon (’: AncestorClassName’) in the class declaration you define that you class in a descendant of the class referenced (AncestorClassName).

To more clearly exemplify this extension by inheritance method of attacking core reuse, we will examine a TPoint3D class which will be a descendant of the TPoint class we have seen previously in this chapter. The TPoint3D class will simply add an extra coordinate Z which is necessary to plot a point in three dimensional space.

Listing 10 – The TPoint3D class definition.

  Public Class TPoint3D : TPoint {
    Private Integer FZ;
 
    Public Integer Z{
      Get { Return FZ; }
      Set { FZ = Z; }
    }
 
    Public Void TPoint3D(Integer XValue, Integer YValue, 
                         Integer ZValue) {
      Base.new(XValue, YValue);
      FZ = ZValue;
    }
 
    Public Void Move(XValue, YValue, ZValue as integer){
      Base.Move(XValue, YValue);
      FZ = ZValue;
    }
  }


The TPoint3D class adds an instance variable to the original TPoint class and overloads some the methods in the TPoint class adding new versions which take three parameters.

Since the instance variables of our original TPoint class were all declared as private, we can’t access them even within a descendant class. To work around this limitation we can use the preexisting ones, as you can see in listing 10’s implementation of the Move method for the TPoint3D class which calls the Move method of its ancestor class and thus sets the X and Y coordinates before setting its own Z coordinate.


Note: If there is a possibility that complex descendants of a class you are writing will be created, you should make the instance variables protected, instead of private.

Virtual Methods

A method can be declared as Virtual by adding the Virtual modifier to its declaration in the class. When a Virtual method is called the real type of the object contained in the variable and not the type of the variable itself is used to identify which method is called.

When you assign a Cat object to a variable of type Animal (ancestor of Cat) and you call a method through that variable if the method was not marked as Virtual the version of the method which will get called will be the one defined in the Animal class. If the method was originally marked as Virtual and is redefined in the Cat class, however, it will be the version introduced in the Cat class that will be called.

Overrides Methods

When creating a descendant class from a class which has methods marked as Virtual you can redefine those methods and be sure that the correct version of the methods will always get called by adding the overrides modifier to the new method declarations.

In Listing 11 you can see a new version of the Animals module with a Virtual Eat method added to the Animals class and reintroduced with the override modifier in the Cat and Dog classes.

Listing 11 – Animals module with some methods of the Animals, Cat and Dog classes being overriden.

Public Class Animal{
  Protected Integer QtyEaten;
 
  Public virtual Void Eat (){
     QtyEaten = QtyEaten + 1;
  }
 
  Public Static virtual abstract string FoodType();
}
 
Public typedef Class of Animal AnimalClass;
 
Public Class mouse : Animal{
  Public Static Override String FoodType(){
     Return "cheese";
  }
}
 
Public Class cat : Animal{
 
  Public Override Void Eat () {
    QtyEaten = QtyEaten + 2;
  }
 
  Public Static Override String FoodType() {
     Return "fish";
  }
 
}
 
Public Class dog : Animal{
 
  Public Override Void Eat (){
    QtyEaten = QtyEaten + 5;
  }
 
  Public Static Override String FoodType() {
     Return "slippers";
  }
}
 
Public AnimalClass GetAnimalWithFoodType(String food){
  If (food == mouse.FoodType) 
    { Return mouse; }
  Else {
    If (food == cat.FoodType) 
     { Return cat; }
    Else { 
      If (food == dog.FoodType) 
        { Return dog; }
    }
  }
}
 
Public Void TestAnimal() {
  AnimalClass MyPetClass;
  Animal MyPet;
  MyPetClass = GetAnimalWithFoodType("cheese");
  MyPet = New MyPetClass();
  MyPet.Free;
}

Abstract Methods

In Morfik C# you can create methods without any implementation; these are generally called abstract methods. These methods are placeholders, usually designed to ensure that all descending classes have such a method. A call to such a method will result in a runtime error. All classes which wish to make use of this method must override the inherited method.

An abstract method should have the modifier Abstract applied to it. The Animals module which is shown in Listings 8 and 11 has an example where such a method could be used in the Animal class: the FoodType shared function. Notice that this method returns an empty string and essentially functions as a placeholder to be overridden in descendant classes. If the Abstract modifier where used the declaration would look like this:

  Public Static abstract String FoodType() {}


Note: Abstract methods are especially interesting when you a creating a class for the sole purpose of being the common ancestor of two or more classes.
It is important to note, however, that in Morfik 2 abstract methods are not supported on browser side code.

Static Methods

Static methods are methods which are declared with the Static modifier and which can be invoked through a class reference, without the need to actually have an instance variable. You can see an example of this in the Animals sample module used to explain class references. See listings 8 or 11 for a full example. Next you can see the header of a shared function extracted from that listing.

  Public Static String FoodType(){}


One use of static methods can be to group together a set of functions which are related to a certain subject but which would not necessarily constitute an object in itself. In Listing 12 you can see an example of what such a class would look like and a small test sub routine showing how to use it.

Listing 12 – A class with all methods marked as static.


Public Class TString {
 
   Public Static Integer length(String Str){
     Return length(Str);
   }
 
   Public Static String UpperCase(String Str) {
     Return UpperCase(Str);
   }
 
   Public Static String LowerCase(String Str) {
     Return LowerCase(Str);
   }
 
}
 
Public Void TestTSringClass {
  String Str = "my test string";
  Integer StringLength;
 
  StringLength = TString.Length(Str);
  Str = TString.UpperCase(Str);
}


Note: Initialized variables as seen in Sub TestTStringClass of Listing 12 will not work if you have chosen to use Delphi as your platform backend compiler.

Wrapping it up

Morfik C# offers a wide range of Object Oriented technology features which allow developers to be very productive. Most features found in today’s most used programming languages are a part of Morfik C# and you should feel free to explore new possibilities.


Related Topics

See Also

Back to top