Translate

Search This Blog

Total Pageviews

Showing posts with label OO. Show all posts
Showing posts with label OO. Show all posts

Thursday, December 6, 2018

Object Orientation

If we want to model in an object-oriented style, we must first clarify what object orientation means.
The introduction of object orientation dates back to the 1960s when the simulation language SIMULA was presented, building on a paradigm that was as natural to humans as possible to describe the world.
The object-oriented approach corresponds to the way we look at the real world; we see it as a society of autonomous individuals, referred to as objects, which take a fixed place in this society and must thereby fulfill predefined obligations.
There is not only one single definition for object orientation. However, there is a general consensus about the properties that characterize object orientation. Naturally, objects play a central role in object-oriented approaches.
Viewed simply, objects are elements in a system whose data and operations are described. Objects interact and communicate with one another.

                              Classes

In many object-oriented approaches, it is possible to define classes that describe the attributes and the behavior of a set of objects (the instances of a class) abstractly and thus group common features of objects.

For example, people have
  • a name, 
  • an address, and a 
  • social security number(SSN).
Courses have
  • a unique identifier, 
  • a title, and 
  • a description.
Lecture halls have
  • a name as well as 
  • a location, etc.
A class also defines a set of permitted operations that can be applied to the instances of the class.

For example, you can reserve a lecture hall for a certain date, a student can register for an exam, etc.
In this way, classes describe the behavior of objects.

                            Objects

The instances of a class are referred to as its objects.
For example, lh1,the Lecture Hall 1 of the Vienna University of Technology, is a concrete instance of the class LectureHall.
In particular, an object is distinguished by the fact that it has its own identity, that is, different instances of a class can be uniquely identified. 
For example, the beamer in Lecture Hall 1 is a different object to the beamer in Lecture Hall 2, even if the devices are of the same type.
Here we refer to identical devices but not the same device. 
The situation for concrete values of data types is different: the number 1, which is a concrete value of the data type Integer, does not have a distinguishable identity.
An object always has a certain state. A state is expressed by the values of its attributes. 
For example, a lecture hall can have the state
  • occupied or 
  • free.
An object also displays behavior.
The behavior of an object is described by the set of its operations. Operations are triggered by sending a message.

                          Encapsulation

Encapsulation is the protection against unauthorized access to the internal state of an object via a uniquely defined interface.
Different levels of visibility of the interfaces help to define different access authorizations. 
Java, for example, has the explicit visibility markers 
  • public,
  • private, and 
  • protected,
which respectively permit access for
  • all,
  • only within the object, and 
  • only for members of the same class, its subclasses, and of the same package.

                             Messages

Objects communicate with one another through messages.
  1. A message to an object represents a request to execute an operation. 
  2. The object itself decides whether and how to execute this operation. 
  3. The operation is only executed if the sender is authorized to call the operation—this can be regulated in the form of visibilities (see Encapsulation)—and a suitable implementation is available.
In many object-oriented programming and modeling languages the concept of overloading is supported. This enables an operation to be defined differently for different types of parameters. 
For example, the operator + realizes different behavior depending on whether it is used to add up integers (e.g., 1 + 1= 2) or to concatenate character strings (e.g., “a” + “b” = “ab”).


                           Inheritance

The concept of inheritance is a mechanism for deriving new classes
from existing classes. A subclass derived from an existing class (= superclass) inherits all visible attributes and operations (specification and implementation) of the superclass.

A subclass can:
  • Define new attributes and/or operations
  • Overwrite the implementation of inherited operations
  • Add its own code to inherited operations
Inheritance enables extensible classes and as a consequence, the creation of class hierarchies as the basis for object-oriented system development.
A class hierarchy consists of classes with similar properties,
for example, Person ← Employee ← Professor ← ... where
A ← B means that B is a subclass of A.

When used correctly, inheritance offers many advantages:
  1. reuse of program or model parts (thus avoiding redundancy and errors), 
  2. consistent definition of interfaces, 
  3. use as a modeling aid through a natural categorization of the occurring elements, and 
  4. support for incremental development, i.e., a step-by-step refinement of general concepts to specific concepts.

                           Polymorphism

In general terms, polymorphism is the ability to adopt different forms.
During the execution of a program, a polymorphic attribute can have references to objects from different classes. 
When this attribute is declared, a type (e.g., class Person) is assigned statically at compile time.
At runtime, this attribute can also be bound dynamically to a sub-
type (e.g., subclass Employee or subclass Student).

A polymorphic operation can be executed on objects from different
classes and have different semantics in each case. This scenario can be implemented in many ways:
  1.  via parametric polymorphism, better known as genericity—here, type parameters are used. In Java for example, the concrete classes are transferred to the operations as arguments;
  2. via inclusion polymorphism—operations can be applied to classes and to their direct and indirect subclasses; 
  3. via overloading of operations; and 
  4. via coercion, that is, the conversion of types.
The first two methods above are known as universal polymorphism; the other two methods are referred to as ad hoc polymorphism.


Resources

.

Friday, November 16, 2018

Basics of Object-Oriented Programming

The fundamental program structure in an object-oriented program is the object. The concept of a class, is similar to ADTs in that it can be used to create objects of types that are not directly supported by language.
The concept of an interface, helps us specify program requirements

                                               The Basics

To understand the notion of objects and classes, we can use an analogy. When a car manufacturer decides to build a new car, considerable effort is expended in a variety of activities before the first car is rolled out of the assembly lines. These include:
  • Identification of the car's user community and assessment of the user’s needs. For this, the manufacturer may form a team.
  • After assessing the requirements, the team may be expanded to include automobile engineers and other specialists who come up with a preliminary design.
  • A variety of methods may be used to prepare and refine the initial design (the team may have experience in building a similar vehicle): prototypes may be built, simulations and mathematical analysis may be performed.
Perhaps after months of such activity, the design process is completed.
  • Another step that needs to be performed is the building of the plant where the car will be produced.
  • The assembly line has to be set up and people hired.
After such steps, the company is ready to produce cars.
  • The design is now reused many times in manufacture. 
  • Of course, the design may have to be fine-tuned during the process based on the company’s observations and user feedback.
The development of software systems often follows a similar pattern.
  1. User needs have to be assessed, 
  2. a design has to be made, and 
  3. then the product has to be built.
From the standpoint of object-oriented systems, a different aspect of the car manufacturing process is important.
The design of a certain type of car will call for specific types of engine, transmission, brake system, and so on, and each of these parts in itself has its own design (blue print), production plants, etc. In other words, the company follows the same philosophy in the manufacture of the individual parts as it does in the production of the car. 
Of course, some parts may be bought from manufacturers, but they in turn follow the same approach.
Since the design activity is costly, a manufacturer reuses the design to manufacture the parts or the cars.
The above approach can be compared with the design of object-oriented systems which are composed of many objects that interact with each other. Often, these objects represent real-life players and their interactions represent real-life interactions. 
Just as design of a car is a collection of the individual designs of its parts and a design of the interaction of these parts,
the design of an object-oriented system consists of designs of its constituent parts and their interactions.
For instance, a banking system could have a set of objects that represent customers, another set of objects that stand for accounts, and a third set of objects that correspond to loans.
  • When a customer actually makes a deposit into her account in real life, the system acts on the corresponding account object to mimic the deposit in software.
  • When a customer takes out a loan, a new loan object is created and connected to the customer object; when a payment is made on the loan, the system acts on the corresponding loan object.
  • Obviously, these objects have to be somehow created. When a new customer enters the system, we should be able to create a new customer object in software.
  • This software entity, the customer object, should have all of the relevant features of the real-life customer. For example, it should be possible to associate the name and address of the customer with this object; however, customer’s attributes that are not relevant to the bank will not be represented in software. As an example, it is difficult to imagine a bank being interested in whether a customer is right-handed or in his  eyes color; therefore, the software system will not have this attribute.

DefinitionAn attribute is a property that we associate with an object; it serves to describe the object and holds some value that is required for processing.

The class mechanism in object-oriented languages provides a way to create such
objects.
A class is a design(blueprint) that can be reused any number of times to create objects.
For example, consider an object-oriented system for a university. There are
  • student objects, 
  • instructor objects, 
  • staff member objects, and so on.
Before such objects are created, we create classes that serve as blue-prints for students, instructors, staff members, and courses as follows:

public class Student {
    // code to implement a single student
}
public class Instructor {
    // code to implement a single instructor
}
public class StaffMember {
    // code to implement a single staff member
}
public class Course {
    // code to implement a single course
}
The above definitions show how to create four classes, without giving any details. (We should put in the details where we have given comments.)
  • The token class is a keyword that says that we are creating a class and that the following token is the name of the class. 
  • The left-curly bracket ({) signifies the beginning  of the definition of the class and the corresponding right-curly bracket (}) ends the definition.
  • The token public is another keyword that makes the corresponding class available to be used from other classes(throughout the file system).
Before we see how to put in the details of the class, let us see how to create
objects using these classes.
The process of creating an object is also called instantiation. 
Each class introduces a new type name. Thus Student, Instructor,
StaffMember and Course are types that we have introduced.
The following code instantiates a new object of type Student.
new Student();
The new operator causes the system to allocate an object of type Student with
enough storage for storing information about one student. The new operator returns the address of the location that contains this object. This address is termed a reference.

The above statement may be executed when we have a new student admitted to the university. Once we instantiate a new object, we must store its reference somewhere, so that we can use it later in some appropriate way. For this, we create a variable of type Student.
Student harry;
Notice that the above definition simply says that harry is a variable that can store references to objects of type Student(doesn't create an object).
Thus, we can write
harry = new Student();
We cannot write
harry = new Instructor();
because harry is of type Student, which has no relationship (as far as the class
declarations are concerned) to Instructor, which is the type of the object created
on the right-hand side of the assignment.

Whenever we instantiate a new object, we must remember the reference to that
object somewhere.
However, it is not necessary that for every object that we instantiate, we declare a different variable to store its reference. If that were the case, programming would be tedious.
Let us illustrate by giving an analogy. When a student drives to school to take a
class, (s)he deals with only a relatively small number of objects:
  • the controls of the car,
  • the road, 
  • the nearby vehicles (and sometimes their occupants, although not always politely), and 
  • traffic signals and signs. (Some may also deal with a cell phone, which is not a good idea!)
There are many other objects that the driver (student) knows about, but is not dealing with them at this time.

Similarly, we keep references to a relatively small number of objects in our pro-
grams. When a need arises to access other objects, we use the references we already have to discover them. For instance, suppose we have a reference to a Student object. That object may have an attribute that remembers the student’s adviser, an Instructor object. If it is necessary to find out the adviser of a given student, we can query the corresponding Student object to get the Instructor object. A single Instructor object may have attributes that remember all the advisees of the corresponding instructor.

                             Implementing Classes

Let's now give some of the basics of creating classes. Let us focus on the Student class that we initially coded as
public class Student {
    // code to implement a single student
}
We would like the ability to give a student a name: given a student object, we should be able to specify that the student’s name is "Tom" or  in general, some string. This is sometimes referred to as
a behaviour of the object
We can think of student objects having the behaviour that they respond to assigning a name.
For this purpose, we modify the code as below
public class Student {
    // code for doing other things
    public void setName(String studentName) {
        // code to remember the name
    }

}
The code that we added is called a method. The method’s name is setName.
A method is like a procedure or function in imperative programming in that it is a unit of code that is not activated until it is invoked. 
  • As in the case of procedures and functions, methods accept parameters (separated by commas in Java). 
  • Each parameter states the type of the parameter expected. 
  • A method may return nothing (as is the case here) or return an object or a value of a primitive type. Here we have put void in front of the method name meaning that the method returns nothing. 
  • The left and right curly brackets begin and end the code that defines the method.
  • Unlike functions and procedures, methods are usually invoked through objects.
The setName method is defined within the class Student and is invoked on
objects of type Student.
Student aStudent = new Student();
aStudent.setName("Ron");
The method setName() is invoked on that object referred to by aStudent. Intuitively, the code within that method must store the name somewhere.
Remember that every object is allocated its own storage. This piece of storage must include space for remembering the name of the student.
We embellish the code as below.
public class Student {
    private String name;
    public void setName(String studentName) {
        name = studentName;
    }
    public String getName() {
        return name;
    }
}
Inside the class we have defined the variable name of type String. It is called a field.
Definition : A field is a variable defined directly within a class and corresponds to an attribute. Every instance of the object will have storage for the field.
Let us examine the code within the method setName. It takes in one parameter,
studentName, and assigns the value in that String object to the field name.

It is important to understand how Java uses the name field.
  • Every object of type Student has a field called name. 
  • We invoked the method setName() on the object referred to by aStudent. Since aStudent has the field name and we invoked the method on aStudent, the reference to name within the method will act on the name field of aStudent.
  • The getName() method retrieves the contents of the name field and returns it.
Members (fields and methods for now) of a class can be accessed by writing
<object-reference>.<member-name>

The field name in the code
name = studentName;
refers to different objects in different instantiations and thus different instances of fields.

Let us write a complete program using the above code.
public class Student {
      // code
     private String name;

     public void setName(String studentName) {
         name = studentName;
     }

    public String getName() {
         return name;
    }

    public static void main(String[] s) {
        Student student1 = new Student();
        Student student2 = new Student();
        student1.setName("John");
        student2.setName("Mary");
        System.out.println(student1.getName());
        System.out.println(student2.getName());
   }
}
The keyword public in front of the method setName() makes the method available wherever the object is available. But what about the keyword private in front of the field name? It signifies that this variable can be accessed only from code within the class Student. Since the line
name = studentName;
is within the class, the compiler allows it. However, if we write
Student someStudent = new Student();
someStudent.name = "Mary";
outside the class, the compiler will generate a syntax error.
As a general rule, fields are often defined with the private access specifier and methods are usually made public. The general idea is that fields denote the state of the object and that the state can be changed only by interacting through pre-defined methods which denote the behaviour of the object. Usually, this helps preserve data integrity.
In the current example though, it is hard to argue that data integrity consideration plays a role in making name private because all that the method setName does is change the name field.
However, if we wanted to do some checks before actually changing a student’s name (which should not happen that often), this gives us a way to do it. 
If we had kept name public and others coded to directly access the field, making the field private later would break their code.
For a more justified use of private, consider the grade point average (GPA) of a
student.
Clearly, we need to keep track of the GPA and need a field for it. GPA is not something that is changed arbitrarily: it changes when a student gets a grade for
a course. So making it public could lead to integrity problems because the field can be inadvertently changed by bad code written outside.
Thus, we code as follows.
public class Student {

    // fields to store the classes the student has registered for.
    private String name;
    private double gpa;


    public void setName(String studentName) {
        name = studentName;
    }


    public void addCourse(Course newCourse) {
        // code to store a ref to newCourse in the Student object.
    }


    private void computeGPA() {
        // code to access the stored courses, compute and set the gpa
    }


    public double getGPA() {
        return gpa;
    }


    public void assignGrade(Course aCourse, char newGrade) {
        // code to assign newGrade to aCourse
        computeGPA();

   }
}
We now write code to utilise the above idea.
Student aStudent = new Student();
Course aCourse = new Course();
aStudent.addCourse(aCourse);
aStudent.assignGrade(aCourse, ’B’);
System.out.println(aStudent.getGPA());
The above code
  1. creates a Student object and 
  2. a Course object. 
  3. It calls theaddCourse method on the student, to add the course to the collection of coursestaken by the student, and 
  4. then calls assignGrade. Note the two parameters: aCourse and ’B’. The implied meaning is that the student has completed the course (aCourse) with a grade of ’B’. 
  5. The code in the method assignGrade should then compute the new GPA for the student using the information presumably in the course (such as number of credits) and the number of points for a grade of ‘B’.

                                                       Constructors

The Student class has a method for setting the name of a student. Here so far we set the name of the student after creating the object. This is somewhat unnatural. Since every student has a name, when we create a student object, we probably know the student’s name as well. It would be convenient to store the student’s name in the object as we create the student object.

Consider the following declarations of variables of primitive data types.
int counter = 0;
double final PI = 3.14;
Both declarations store values into the variables as the variables are created. On the other hand, the Student object, when created, has a zero in every bit of every field.
Java and other object-oriented languages allow the initialization of fields by using what are called constructors.

Definition : A constructor is like a method in that it can have
  • an access specifier (like public or private), 
  • a name, 
  • parameters, and 
  • executable code.
However, constructors have the following differences or special features.
  1. Constructors cannot have a return type: not even void.
  2. Constructors have the same name as the class in which they are defined.
  3. Constructors are called when the object is created(only trough new operator) and connot be called as other methods such as object.constructor. << >>
For the class Student we can write the following constructor.
public Student(String studentName) {
    name = studentName;
}
The syntax is similar to that of methods, but there is no return type. However, it has a parameter, an access specifier of public, and a body with executable code.

If needed, one could put local variables as well inside constructors.
Let us rewrite the Student class with this constructor and a few other modifications.
public class Student {

    private String name;
    private String address;
    private double gpa;

    public Student(String studentName) {
        name = studentName;
    }


    public void setName(String studentName) {
       name = studentName;
    }


    public void setAddress(String studentAddress) {
        address = studentAddress;
    }


    public String getName() {
        return name;
    }


    public String getAddress() {
        return address;
    }


    public double getGpa() {
        return gpa;
    }


    public void computeGPA(Course newCourse, char grade) {
        // use the grade and course to update gpa
    }
}
We now maintain the address of the student and provide methods to set and get the name and the address.
With the above constructor, an object is created as below.
Student aStudent = new Student("John");
When the above statement is executed, the constructor is called with the given parameter, “John.” This gets stored in the name field of the object.
In previous versions of the Student class, we did not have a constructor. In such cases where we do not have an explicit constructor, the system inserts a constructor with no arguments. Once we insert our own constructor, the system removes this default, no-argument constructor.
As a result, it is important to note that the following is no longer legal because
there is no constructor with no arguments.
Student aStudent = new Student();
A class can have any number of constructors. They should all have different signatures: that is, they should differ in the way they expect parameters (in number, type and order).
The following adds two more constructors to the Student class.
public Student(String studentName, String studentAddress) {
    name = studentName;
    address = studentAddress;
}


public Student() {
}
Notice that
  • all constructors have the same name, which is the name of the class. 
  • One of the new constructors accepts the name and address of the student and stores it in the appropriate fields of the object. 
  • The other constructor accepts no arguments and does nothing: as a result, the name and address fields of the object are null.

                                                Printing an Object

Suppose we want to print an object. We might try

System.out.println(student);
where student is a reference of type Student.
The statement, however, will not produce anything very useful for someone expecting to see the name and address of the student.
For objects, unless the programmer has provided specific code, Java always prints the name of the class of which the object is an instance, followed by the @ symbol and a value, which is the unsigned hexadecimal representation of the hash code of the object. It does not make any assumptions on the fields to be printed; it prints none of them!
This problem is solved by putting a method called toString() in the class.
This method contains code that tells Java how to convert the object to a String.
public String toString() {
    // return a string
}
Whenever an object is to be converted to a String, Java calls the toString method
on the object just as any other method.

The method call System.out.println() attempts to convert its arguments to the string form. So it calls the toString() method.
We can complete the toString method for the Student class as below.
public String toString() {
    return "Name " + name + " Address " + address + " GPA " + gpa;
}
It is good practice to put the toString method in every class and return an appropriate string. Sometimes, the method may get slightly more involved than the simple method we have above; for instance, we may wish to print the elements of an array that the object maintains, in which case a loop to concatenate the elements is in order.

                                                  Static Members

So far, all members of a class were accessed using the syntax

<object_reference>.<member_name>
This is quite logical because we wanted to act on specific objects. Every Student
object, for example, has its own name, gpa, and address fields. If we did not
specify the object and merely specified the field/method, the specification would be incomplete.
Sometimes, we need fields that are common to all instances of an object. In other words, such fields have exactly one instance and this instance is shared by all instances of the class. Such fields are called static fields. In contrast, fields maintained separately for each object are called instance fields.
Let us turn to an example. Most universities usually have the rule that students
not maintaining a certain minimum GPA will be put on academic probation.
Let us assume that this minimum standard is the same for all students. Once in a while, a university may decide that this minimum standard be raised or lowered. (Grade inflation can be a problem!)

We would like to introduce a field for keeping track of this minimum GPA. Since
the value has to be the same for all students, it is unnecessary to maintain a separate field for each student object. In fact, it is risky to keep a separate field for each object:
  1. since every instance of the field has to be given the same value, special effort will have to be made to update all copies of the field whenever we decide to change its value. This can give rise to integrity problems
  2. It is also quite inefficient.
Suppose we decide to call this new field, minimumGPA, and make its type
double. We define the variable as below.
private static double minimumGPA;
The specifier static means that there will be just one instance of the field
minimumGPA;
The field will be created as soon as the class is loaded by the system.
Note that there does not have to be any objects for this field to exist. This instance will be shared by all instances of the class.
Suppose we need to modify this field occasionally and that we also want a method that tells us what its value is. We typically write what are called static methods for doing the job.
public static void setMinimumGPA(double newMinimum) {
    minimumGPA = newMinimum;
}

public static double getMinimumGPA() {
    return minimumGPA;
}

The keyword static specifies that the method can be executed without using an object. The method is called as below.
<class_Name>.<method_name>
For example,
Student.setMinimumGPA(2.0);
System.out.println("Minimum GPA requirement is "
+ Student.getMinimumGPA());
Methods and fields with the keyword static in front of them are usually called
static methods and static fields respectively.

It is instructive to see, in the above case, why we want the two methods to be
static. Suppose they were instance methods. Then they have to be called using an object as in the following example.
Student student1 = new Student("John");
student1.setMinimumGPA(2.0);
While this is technically correct, it has the following disadvantages:
  1. It requires that we create an object and use that object to modify a static field. This goes against the spirit of static members; they should be accessible even if there are no objects.
  2. Someone reading the above fragment may be lead to believe that setMinimumGPA() is used to modify an instance field.
On the other hand, a static method cannot access any instance fields or methods. It is easy to see why. A static method may be accessed without using any objects at all. Therefore, what object should the method use to access the member? In fact, there may not be any objects created yet when the static method is in use.



(to be continued...)

Saturday, November 10, 2018

History of OO

History of the object-oriented programming approach could be traced to the idea of  ADTs and the concept of objects in Simula 67 programming language, which was developed in the 1960s for performing simulations.

The first true object-oriented programming language that appeared before the larger software development community was Smalltalk in 1980, developed at Xerox PARC.
Smalltalk used objects and messages as the basis for computation. Classes could be created and modified dynamically. Most of the vocabulary in object-oriented paradigm has originated from this language.
Toward the end of the 1970s, Bjarne Stroustrup, who was doing doctoral work in England, needed a language for doing simulation of distributed systems. He developed a language based on the class concept in Simula, but this language was not particularly efficient. However, he pursued his attempt and developed an object-oriented language at Bell Laboratories as a derivative of C, which would blossom into one of the most successful programming languages, C++.
The language was standardised in 1997 by the American National Standards Institute (ANSI).

The 1980s saw the development of several other languages such as
The rising popularity of the object-oriented model also propelled changes to the language Ada, originally sponsored by the U.S. Department of Defense(DOD) in 1983. This resulted in Ada 9x, an extension to Ada 83, with object-oriented concepts including
  • inheritance, 
  • polymorphism, and 
  • dynamic binding.
The 1990s saw two major events.
  1. One was the development of the Java programming language in 1996. Java appeared to be a derivative of C++, but many of the controversial and troublesome concepts in C++ were deleted in it. Although it was a relatively simple language when it was originally proposed, Java has undergone substantial additions in later versions making it a moderately difficult language. Java also comes with an impressive collection of libraries (organized in so called packages) to support application development.
  2. A second watershed event was the publication of the book Design Patterns by Gamma et al. in 1994. The book considered specific design questions (23 of them) and provided general approaches to solving them using object-oriented constructs. The book (as also the approach it advocated) was a huge success as both practitioners and academicians soon recognized its significance.
The last few years saw the acceptance of some dynamic object-oriented languages that were developed in the 1990s.
Dynamic languages allow users more flexibility, for example the ability to dynamically add a method to an object at execution time.
One such language is Python, which can be used for solving a variety of applications including
  • web programming, 
  • databases, 
  • scientific and numeric computations and 
  • networking.
Another dynamic language, Ruby, is even more object-oriented in that
everything in the language, including numbers and primitive types, is an object.

Concepts of Object-Oriented Design

                                       The Central Role of Objects

Object-orientation, makes objects the centerpiece of software design.
The design of earlier systems was centered around processes, which were susceptible to change, and when this change came about, very little of the old system was ‘re-usable’.
The notion of an object is centered around
  1. a piece of data and 
  2. the operations (or methods) that could be used to modify it.
This makes possible the creation of an abstraction that is very stable since it is not dependent on the changing requirements of the application.
The execution of each process relies heavily on the objects to store the data and provide the necessary operations; with some additional work, the entire system is ‘assembled’ from the objects.

                                                 The Notion of a Class


Classes allow a software designer
  • to look at objects as different types of entities.
  • Viewing objects this way allows us to use the mechanisms of classification to categorise these types, define hierarchies and 
  • engage with the ideas of specialization and generalization of objects.

                                 Abstract Specification of Functionality

In the course of the design process, the software engineer specifies the properties of objects (and by implication the classes) that are needed by a system.
  • This specification is abstract in that it does not place any restrictions on how the functionality is achieved.
  • This specification, called an interface or an abstract class, is like a contract for the implementer which also facilitates formal verification of the entire system.


                               A Language to Define the System

The Unified Modelling Language (UML) has been chosen by consensus as the standard tool for describing the end products of the design activities. The documents generated in this language can be universally understood and are thus analogous to the ‘blueprints’ used in other engineering disciplines.



                                                    Standard Solutions

The existence of an object structure facilitates the documenting of standard solutions, called design patterns. Standard solutions and corresponding patterns are found at all stages of software development, but
design patterns are perhaps the most common form of reuse of solutions.

                      An Analysis Process to Model a System

Object-orientation provides us with a systematic way to translate a functional specification to a conceptual design. This design describes the system in terms of conceptual classes from which the subsequent steps of the development process generate the implementation classes that constitute the finished software.
  1. functional specification -->
  2. conceptual design -->
  3. conceptual classes -->
  4. implementation classes

            The Notions of Extendability and Adaptability

Software has a flexibility that is not typically found in hardware, and this allows us to modify existing entities in small ways to create new ones.
  1. Inheritance, which creates a new descendant class that modifies the features of an existing (ancestor) class, and 
  2. composition, which uses objects belonging to existing classes as elements to constitute a new class,
are mechanisms that enable such modifications with classes and objects.

As the object-oriented methodology developed, the science of software design progressed too, and several desirable software properties were identified. Not central enough to be called object-oriented concepts, these ideas are nonetheless closely linked to them and are perhaps better understood because of these developments.

                                   Modular Design and Encapsulation

Modularity refers to the idea of putting together a large system by developing a
number of distinct components independently and then integrating these to provide the required functionality.
  1. This approach, when used properly, usually makes the individual modules relatively simple and thus the system easier to understand than one that is designed as a monolithic structure. In other words, such a design must be modular. 
  2. The system’s functionality must be provided by a number of well-designed, cooperating modules. 
  3. Each module must obviously provide certain functionality that is clearly specified by an (module's) interface. The interface also defines how other components may interact or communicate with the module. 
  4. We would like that a module clearly specify what it does, but not expose its implementation(internal workings). This separation of concerns gives rise to the notion of encapsulation, which means that the module hides details of its implementation from external agents. 
The abstract data type (ADT), the generalization of primitive data types such as integers and characters, is an example of applying encapsulation.
The programmer specifies the collection of operations on the data type and the data structures that are needed for data storage.
Users of the ADT perform the operations without concerning themselves with the implementation details.

                                             Cohesion and Coupling

Each module provides certain functionality;
cohesion of a module tells us how well the entities within a module work together to provide this functionality.
Cohesion is a measure of how focused the responsibilities of a module are. If the responsibilities of a module are unrelated or varied and use different sets of data, cohesion is reduced.
Highly cohesive modules tend to be more reliable, reusable, and understandable than less cohesive ones.
To increase cohesion, we would like that all the constituents
contribute to some well-defined responsibility of the module. This may be quite a challenging task.
In contrast, the worst approach would be to arbitrarily assign responsibilities to modules, resulting in a module whose constituents have no obvious relationship.


Coupling refers to how dependent modules are on each other. 
The very fact that we split a program into multiple modules introduces some coupling into the system.

Coupling could result because of several factors:
  1. a module may refer to variables defined in another module or 
  2. a module may call methods of another module and use the return values.
The amount of coupling between modules can vary.
In general, if modules do not depend on each others implementation, i.e., modules depend only on the published interfaces of other modules and not on their internals, we say that the coupling is low.
In such cases, changes in one module will not necessitate changes in other modules as long as the interfaces themselves do not change. 
Low coupling allows us to modify a module without worrying about the ramifications of the changes on the rest of the system. 
By contrast, high coupling means that changes in one module would necessitate changes in other modules, which may have a domino effect and also make it harder to understand the code.

                                      Modifiability and Testability

A software component, unlike its hardware counterpart, can be easily modified in small ways. This modification can be done to change both functionality and design.
  1. The ability to change the functionality of a component allows for systems to be more adaptable; the advances in object-orientation have set higher standards for adaptability. 
  2. Improving the design through incremental change is accomplished by refactoring, again a concept that owes its origin to the development of the object-oriented approach. 
There is some risk associated with activities of both kinds; and in both cases, the organization of the system in terms of objects and classes has helped develop systematic procedures that mitigate the risk.


Testability of a concept, in general, refers to both: 

falsifiability, i.e., the ease with which we can find counterexamples, 
and
the practical feasibility of reproducing such counterexamples. 
In the context of software systems, it can simply be stated as
the ease with which we can find bugs in a software and the extent to which the structure of the system facilitates the detection of bugs. 
Several concepts in software testing (e.g., the idea of unit testing) owe their prominence to concepts that came out of the development of the object-oriented paradigm.



                  Benefits and Drawbacks of the Paradigm

From a practical standpoint, it is useful to examine how object-oriented methodology has modified the landscape of software development. As with any development, we do have pros and cons.
The advantages listed below are largely consequences of the ideas already presented here.
  1. Objects often reflect entities in application systems. This makes it easier for a designer to come up with classes in the design. In a process-oriented design, it is much harder to find such a connection that can simplify the initial design.
  2. Object-orientation helps increase productivity through reuse of existing software. Inheritance makes it relatively easy to extend and modify functionality provided by a class. Language designers often supply extensive libraries that users can extend.
  3. It is easier to accommodate changes. One of the difficulties with application
    development is changing requirements. With some care taken during design, it is possible to isolate the varying parts of a system into classes.
  4. The ability to isolate changes, encapsulate data, and employ modularity reduces the risks involved in system development. 
 The above advantages do not come without a price tag. 
  1. Perhaps the number one casualty of the paradigm is efficiency. The object-oriented development process introduces many layers of software, and this certainly increases overheads. 
  2. In addition, object creation and destruction is expensive. Modern applications tend to feature a large number of objects that interact with each other in complex ways and at the same time support a visual user interface. This is true whether it is a banking application with numerous account objects or a video game that has often a large number of objects. 
  3. Objects tend to have complex associations, which can result in non-locality, leading to poor memory access times. 
  4. Programmers and designers schooled in other paradigms, usually in the imperative paradigm, find it difficult to learn and use object-oriented principles. In coming up with classes, inexperienced designers may rely too heavily on the entities in the application system, ending up with systems that are ill-suited for reuse. Programmers also need acclimatisation; some people estimate that it takes as much as a year for a programmer to start feeling comfortable with these concepts. 
  5. Some researchers are of the opinion that the programming environments also have not kept up with research in language capabilities. They feel that many of the editors and testing and debugging facilities are still fundamentally geared to the imperative paradigm and do not directly support many of the advances such as design patterns.

Friday, November 9, 2018

What Is Object-Oriented Development?

The traditional view of a computer program is that of :
a process that has been encoded in a form that can be executed on a computer. 
This view originated from the fact that the first computers were developed mainly to automate a well-defined process (i.e. an algorithm) for numerical computation, and dates back to the first stored-program computers.

Accordingly, the software creation process was seen as a translation from a description in some ‘natural’ language to a sequence of operations that could be
executed on a computer.
As many would argue, this paradigm is still the best way to introduce the notion of programming to a beginner, but as systems became more complex, its effectiveness in developing solutions became suspect. This change of perspective on part of the software developers happened over a period of time and was
fueled by several factors including the high cost of development and the constant efforts to find uses for software in new domains.

One could safely argue that the software applications developed in later years had two differentiating characteristics:
  • Behaviour that was hard to characterize as a process
  • Requirements of reliability, performance, and cost that the original developers did not face
The ‘process-centered’ approach to software development used what is called top-down functional decomposition.
  1. The first step in such a design was to recognize what the process had to deliver (in terms of input and output of the program), which 
  2. was followed by decomposition of the process into functional modules. 
  3. Structures to store data were defined and the computation was carried out by invoking the modules, which performed some computation on the stored data elements.
The life of a process-centred design was short because changes to the process specification (something relatively uncommon with numerical algorithms when compared with business applications) required a change in the entire program. This in turn resulted in an inability to reuse existing code without considerable overhead.
As a result, software designers began to scrutinize their own approaches and also study design processes and principles that were being employed by engineers in other disciplines. Cross-pollination of ideas from other engineering disciplines started soon after, and the disciplines of ‘software design’ and ‘software engineering’ came into existence.

In this connection, it is interesting to note the process used for designing simple
electromechanical systems.
For several decades now, it has been fairly easy for people with limited knowledge of engineering principles to design and put together simple systems in their backyards and garages. So much so, it has become a hobby that even a 10 years old could pursue. The reasons for this success are easy to see:
  • easily understandable designs, 
  • similar (standard) solutions for a host of problems, 
  • an easily accessible and well-defined ‘library’ of ‘building-blocks’,
  • interchangeability of components across systems, 
  • ... and so on.
Some of the pioneers in the field of software design began to ask whether they could not also design software using such ‘off-the-shelf’ components.
The object-oriented paradigm, one could argue, has really evolved in response to this outlook. 
There are, of course, several differences with the hardware design process (inevitable, because the nature of software is fundamentally different from hardware), but parallels can be drawn between many of the defining characteristics of hardware design and what today’s advocates of good software
design recommend.
This methodology, provides us with:
  1. a step-by-step process for software design, 
  2. a language to specify the output from each step of the process so that we can transition smoothly from one stage to the next, 
  3. the ability to reuse earlier designs, standard solutions that adhere to well-reasoned design principles and, 
  4. even the ability to incrementally fix a poordesign without breaking the system.
The overall philosophy here is to define a software system as a collection of objects of various types that interact with each other through well-defined interfaces.
Unlike a hardware component, a software object can be designed to handle multiple functions and can therefore participate in several processes. A software component is also capable of storing data, which adds another dimension of complexity to the process.
The manner in which all of this has departed from the traditional process-oriented view is that
  1. instead of implementing an entire process end-to-end and defining the needed data structures along the way, we first analyze the entire set of processes and from this identify the necessary software components. 
  2. Each component represents a data abstraction and is designed to store information along with procedures to manipulate the same. 
  3. The execution of the original processes is then broken down into several steps, each of which can be logically assigned to one of the software components. 
  4. The components can also communicate with each other as needed to complete the process