Skip to content

3.31 Lecture 5

Introduction to Systems Programming - Lectures - Class 5.pdf

Introduction to Systems Programming

Code Review


Introduction to Systems Programming

Reviewing the structure of a class


Reviewing the structure of a class

package laboratory;

import java.util.ArrayList;
import laboratory.data.Student;
import laboratory.data.Teacher;
//could use laboratory.data.* to import all classes
/**
 * <Class documentation>
 */
public class Laboratory {

    private ArrayList<Student> students;
    private Teacher teacher;
    private String name;

    /**
     * <Constructor documentation>
     */
    public Laboratory(String name, Teacher teacher) {
    }
    //<implementation>

    //More methods

    /**
     * <Method documentation>
     */
    public int numberOfStudents() {
        return students.size();
    }
}

image

The src/main/java​ folder is the usual convention for a Java project, it's one of many standard folders.


Reviewing the structure of a class

package laboratory;

import java.util.ArrayList;
import laboratory.data.Student;
import laboratory.data.Teacher;

import​ in Java does not perform code insertion as in, for example, C


Reviewing the structure of a class

The interface of the class

package laboratory;

import java.util.ArrayList;
import laboratory.data.Student;
import laboratory.data.Teacher;

/**
 * <Class documentation>
 */
public class Laboratory {

    private ArrayList<Student> students;
    private Teacher teacher;
    private String name;

    /**
     * <Constructor documentation>
     */
    public Laboratory(String name, Teacher teacher) {
    }
    //<implementation>

    //More methods

    /**
     * <Method documentation>
     */
    public int numberOfStudents() {
        return students.size();
    }
}

to

package laboratory;
import java.util.ArrayList;
import laboratory.data.Student;
import laboratory.data.Teacher;
/**
 * <Class documentation>
 */
public class Laboratory {
/**
 * <Constructor documentation>
 */
public Laboratory(String name, Teacher teacher)
//More methods (documentation and signatures)
/**
 * <Method documentation>
 */
public int numberOfStudents()
}

Visibility, class fields/methods, constants.

Accessibility of members declared by class A from different classes.

Visibility/Class From class A (package pA) From class A2 (package pA) From class B2 (package pB, subclass of A) From class B1 (package pB)
private ACCESSIBLE INACCESSIBLE INACCESSIBLE INACCESSIBLE
package ACCESSIBLE ACCESSIBLE INACCESSIBLE INACCESSIBLE
protected ACCESSIBLE ACCESSIBLE ACCESSIBLE INACCESSIBLE
public ACCESSIBLE ACCESSIBLE ACCESSIBLE ACCESSIBLE

Visibility, class fields/methods, constants.

To declare a class member we use the keyword static

  • Meaning: static​ means something belongs to the class itself, not to any specific object (instance) of that class.
  • Role:

  • Shared: Static variables are shared among all instances of the class (one copy exists).

  • Accessible without object: You can call static methods or access static variables using the class name directly (e.g., Math.PI​ or MyClass.myStaticMethod()​), without needing to create an object first.

    Example

    public class MathHelper {
    
        // This method IS static. It belongs to the MathHelper class itself.
        public static int add(int num1, int num2) {
            return num1 + num2; // Doesn't need any object-specific data
        }
    
        // This method is NOT static. It belongs to an individual MathHelper object.
        // (We don't really need it here, but it shows the contrast)
        public void instanceMethodExample() {
            System.out.println("This needs an object to be called.");
        }
    
        public static void main(String[] args) {
    
            // --- Using the STATIC method ---
            // You can call 'add' directly using the class name: MathHelper
            // No object creation (like 'new MathHelper()') is needed!
            int result = MathHelper.add(10, 5);
            System.out.println("Static method result: " + result); // Output: Static method result: 15
    
            // --- Trying to use the NON-STATIC method (instance method) ---
            // This would cause a compile ERROR because instanceMethodExample is not static:
            // MathHelper.instanceMethodExample(); // ERROR!
    
            // To call the non-static method, you MUST create an object first:
            MathHelper helperObject = new MathHelper();
            helperObject.instanceMethodExample(); // Now this works.
    
        }
    }
    

Think "class-level" instead of "object-level".


Visibility, class fields/methods, constants.

Sometimes, we need fields, and/or variables, to behave as constants. A constant MUST be initialized and one cannot change its value.

We use the final keyword for this.


Generic/Parameterized Classes

Sometimes, a class may have fields that may not have a specific type. We have seen this with ArrayList​, where we have “ArrayList of T"

e.g.: ArrayList<String>​ as an "ArrayList of String"; ArrayList<Student>​ as an
"ArrayList of Student".

So far we have used parameterized classes, let's see how to make them.


Generic/Parameterized Classes - Motivational example

We want to have a Book Library where we can search for a Book by a title,
but that book may not exist.

/**
 * This class represents a personal library.
 */
public class Library {

    private ArrayList<Book> books;

    //Constructors and Methods

    public? searchBook(String expression);
}

What should we return?

We could make a class that represents an optional value, i.e.: “Maybe a
value". But making a class like this only for Book would not be reusable.


Software need to change

Software is not a constant product, it evolves, it is modified. The phrase
"change or die" is a very pertinent invariant in software development.

  • A software that is continuously maintained will prevail in time.
  • A software that is static will become obsolete and die.

From this we can infer that a software that is hard to maintain will be
thrown away.

There are ways to increase the maintainability of our software.


Code quality

  • Self documented code, i.e.: the code is easy to understand and follow.
  • Proper documentation.
  • Testability, i.e.: is easy to test the code.
  • Low coupling.
  • High cohesion.
  • Others ...

Code quality

  • Self documented code, i.e.: the code is easy to understand and follow.
  • Proper documentation.
  • Testability, i.e.: is easy to test the code.
  • Low coupling.
  • High cohesion.
  • Others ...

Coupling and Cohesion

  • Coupling refers to links between separate units of a program.
  • If two classes depend closely on many details of each other, we say they are tightly coupled.
  • We aim for loose coupling.

Coupling and Cohesion

Loose coupling makes it possible to:

  • Understand one class without reading others.
  • Change one class without affecting others.
  • Thus: improves maintainability.

Coupling and Cohesion

  • Cohesion refers to the number and diversity of tasks that a single unit is responsible for.
  • If each unit is responsible for one single logical task, we say it has high cohesion.
  • Cohesion applies to classes and methods.
  • We aim for high cohesion.

Coupling and Cohesion

High cohesion makes it easier to:

  • Understand what a class or method does.
  • Use descriptive names
  • Reuse classes or methods.

Coupling and Cohesion

  • A method should be responsible for one and only one well defined task.
  • Classes should represent one single, well defined entity.

Code duplication

Code duplication:

  • Is an indicator of bad design.
  • Makes maintenance harder.
  • Can lead to introduction of errors during maintenance.

Responsibility-driven design (RDD)

  • It focuses on the contracts between client () and provider (*).
  • Each class should be responsible for manipulating its own data.
  • RDD leads to low coupling.

() The one using the features (provided by a class).
(
*) The one offering the features (the class).


Localizing change

  • One aim of reducing coupling and responsibility-driven design is to localize change.
  • When a change is needed, as few classes as possible should be affected *.

(*) This is a general rule, not only applied to RDD.


Thinking ahead

  • When designing a class, we try to think what changes are likely to be made in the future.
  • We aim to make those changes easy.

But remember: do not overengineer your solutions.


Refactoring

  • When classes are maintained, often code is added.
  • Classes and methods tend to become longer.
  • Every now and then, classes and methods should be refactored to maintain cohesion and low coupling.

Refactoring and testing

  • When refactoring code, separate the refactoring from making other changes.
  • First do the refactoring only, without changing the functionality.
  • Test before and after refactoring to ensure that nothing was broken (Regression testing is the usual way to validate this).

Design guidelines

  • A method is too long if it does more than one logical task.
  • A class is too complex if it represents more than one logical entity.

Note: these are guidelines - they still leave much open to the designer.