Translate

Search This Blog

Total Pageviews

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...)

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.