Lecture 2a: Inheritance, UML, Good Design


0. Packages

Packages are the exact equivalent of namespaces in C++. They hold a collection of anything. Good Java practice requires that you put your entire "project" or working module in at least one package. This way it makes it less ambiguous what's going on when you install your application.

Notice how in Java you have to declare public, private or protected for every member of a class. If you don't do that, it's still fine but will be called a "package public" member. What that means is that it will be seen as "public" to all members of the same package but "private" when viewed from outside the package.

I. Inheritance

Intro

No, we are not talking about you getting your parent's stuff when they die... we're talking about class inheritance in object oriented design. What this does is that it allows the child class to gain all abilities of the parent class(es) and be able to add its own abilities... kind of like people's expectation of athelete's kids, expecting them to have all the atheletic ability of mom and dad.

In Java all classes that you define implements/extends the Object class. This is the big daddy superdupper parent class of all classes in Java. It gives you the clone(), finalize(), hashCode(), equal(), toString(), copy(Object src), getClass(), notifyAll() and wait() functions.

Design and Abuse

Inheritance is powerful, but it must be done in away that makes sense. Like for example, your car can move, so can animals... you don't want to inherit animal from car. More will be covered in the good design section.

C++ specific:
Just like in genetics... incest is wrong in OO. If class B and C both inherit from class A, a new class, class D, can't inherit from class B and C. More details when we get to virtual/abstract functions. This, however, can't be done in Java since you can only inherit from two interfaces, not abstract classes.

Encapsulation

We had public and private members of a class before... what happens in inheritance? All private members are not accessible by the child class and you probably need it!... but you can prevent that from happening by declaring it protected. A protected member is directly accessible to the child. If you have private members in the parent class, you need to define getters and setters to allow access within the family.

Polymorphism and function overriding

So if you are a parent class and you have a special function made just for you... and you want your child to have a special one just for it, what do you do? For example, you have a shape in a graphics library... and it knows everything that inherits from it must draw... how do you tell the children to do that?

When you redefine functions with the same name, arguments and return types in a child class (overriding that function)... will allow you to customize that function for the child class. This can be seen in my code example. If you want to access the function from the parent class you can do it using the super() function. It works like the this pointer in C++. See my example for usage.

Abstract and Interface classes

C++ specific:
In C++ you have virtual functions and in Java in you have abstract functions. In C++ you can make a function pure virtual by just having the declaration = 0 and not define its implementation in the parent class.

In Java you have two choices:

  1. Abstract class: You have to have an implementation for some functions. All functions marked abstract will have to be defined in the children classes and can't be defined at the parent level.
  2. Interface class: You just declare a load of memberrs and you don't define any of them.

In Java you can implement multiple interface classes but you HAVE to extend from only ONE abstract class.

The other question is... in construction what happens? Same thing with function calls that are overriden via inheritance. In constructors the base class (parent class) constructor is called first and then the child class's constructor is then called.

C++ specific:
You, many times, would also want to make the destructor virtual (specific to C++)... but not always, reasons will be more clear after the lesson on linked list. You don't want to make constructor abstract/virtual, many compilers won't even let you do that. In Java, you don't have to worry about the constructor or destructor since it's taken care of for you, on the most part.

If you are implementing an abstract class then in a child class, you HAVE to implement all abstract functions. The compiler will not let you go unless you implement that function in the child class. If there are functionalities in the parent class's definition for that function that you wish to reuse, you may do so by calling the super.function(...) function (which has to be the first line in a constructor). This can also be done in the constructor as well.

C++ specific:
As promised earlier... let's get back to incest. In C++ you can define functionality to an "abstract" function, which is called a virtual function. If you have two implementations of it at the children level... which implementation does the grandchildren level use if both parents are from the same grandparent?

Doing it in code

In Java you need to use different keywords to say that you want to inherit from something depending on whether the parent is abstract or interface. Here is an example of abstract class:

// A.java
public abstract class A {
   
public A() {
        System
.out.println("Constructing A");
   
}
   
public void print() {
        System
.out.println("Executing print in A");
   
}
   
abstract void print2();
}

// B.java
public
class B extends A{
    public B(){
        System.out.println("Constructing B");
    }
    public void print() {
        System.out.println("Executing print in B ");
       
super.print();

    }

    public void print2(){
        System.out.println("Executing print2 in B");
    }
}

// Main.java
public class Main
{
    public static void main ( String[] args )
    {
        System.out.println("Calling new B()");
        B someB = new B();

        System.out.println();
        System.out.println("Calling B.print()");
        someB.print();

        System.out.println();      
        System.out.println("Calling B.print2()");
        someB.print2();
    }
}

OUTPUT:

Calling new B()
Constructing A
Constructing B

Calling B.print()
Executing print in B
Executing print in A

Calling B.print2()
Executing print2 in B

II. UML

Intro

UML is Unified Modeling Language, a modeling language used to draw diagrams that describes software in a language independent fashion. It is extensive but in real practice people only use three or four different symbols in that language.

This is a representation of a class. The top block is reserved for the name, second for attributes and third for methods/functions. The symbol in the front of each row specifies the protection of each member: - for private, + for public and # for protected. Each member and all the members in the member is separated by a : in such a way that it's like <name>:<type>.

Connections

The first type of connection is a composite/aggregation connection, which is a "has a" relationship. It says that something has something else:

In this case SomeClass has AnotherClass. It also says that 1 instance of SomeClass has many of AnotherClass. This is designated by the 1 for 1 count of something per (*) for many of another.

The next is inheritance, the "is a" relationsihp. It says that a class is a kind of another class:

Unlike the aggregation symbol, this one has no multiplicity symbols. In this case, AnotherClass (child) inherits SomeClass (parent).

Other than those symbols, there really aren't much else to be talked about. The other symbols are most likely drawing tool specific symbols. The tools can many times generate for you code stubs that compiles verbatium... of course, without the functionality. But UML's job is not to give the specifics of how things will work, it only shows how the software's structure will look.

Programs for drawing UML

III. Good Design

As mentioned earlier, we will now talk about good design. One of the best things to do is to sit down and try to construct sentences out of how you can approach constructing the structure of the problem. For example, we're making a car (in a simple definition) we can do the following:

Car:

Vehicle:

Door:

Tire:

Sedan:

Coupe:

So with the above description we can start converting this into UML. Notice how we only used two types of descriptions. We said what something is a kind of and what something has. We deliberately left certain things undesigned, like the vehicle. The reason for that is to not worry about it. This idea is called abstraction. We are only worried about what we are dealing with right now, and nothing more.


This is the design for the explicit definition of door count.


This is the design for the implicit definition of the doors.