7.3. Person Example (Refactoring Existing Code)

In our first interactive example, we will work through the code in the person subdirectory of our starter code. We will leverage inheritance to eliminate redundant code in the closely related Employee class.

Here is a UML diagram for the starter code. Notice the redundant methods and variables between Person and Employee.

hide circle
set namespaceSeparator none
skinparam classAttributeIconSize 0

package "cs1302.identity" {
   Driver -up-> Person: uses
   Driver -right-> Employee: uses

   class Person {
        -name: String
        -dateOfBirth: LocalDate
        +<<new>> Person(name: String, dateOfBirth: LocalDate)
        +getName(): String
        +getDateOfBirth(): LocalDate
        +computeAge(): int
        +<<override>> toString(): String
   }

   class Driver {
        {static} +main(args: String[]): void
   }

   class Employee {
        -id: long
        -name: String
        -dateOfBirth: LocalDate
        -dateOfHire: LocalDate

        +<<new>> Employee(id: long, name: String,
        \t\t\t    dateOfBirth: LocalDate, \n\t\t\t    dateOfHire: LocalDate)
        +getId(): long
        +getName(): String
        +getDateOfHire(): LocalDate
        +computeAge(): int
        +<<override>> toString(): String

   }

   hide Driver fields
}

In the video below, Dr. Cotterell refactors the code corresponding to the UML class diagram above.

Note

The example code in this section was written without inheritance, so we need to refactor it to eliminate existing redundancies. However, in a real world setting, you would want to design carefully to avoid writing redundant code in the first place!

To get the most out of this section, you will want to follow along with Dr. Cotterell in the video below. But first, make sure to change into the person subdirectory of cs1302-inheritance.

https://www.youtube.com/watch?v=V5Y85rfMfPw

IMAGE ALT TEXT

After replicating the steps in the video, an inheritance relationship is established. The UML diagram below illustrates this new relationship (note the different arrow head types and compare the size of the Employee class in this diagram with the diagram above):

hide circle
set namespaceSeparator none
skinparam classAttributeIconSize 0

package "cs1302.identity" {
   Driver -up-> Person: uses
   Driver -right-> Employee: uses
   Person <|-- Employee: extends (is-a)

   class Person {
        -name: String
        -dateOfBirth: LocalDate
        +<<new>> Person(name: String, dateOfBirth: LocalDate)
        +getName(): String
        +getDateOfBirth(): LocalDate
        +computeAge(): int
        +<<override>> toString(): String
   }

   class Driver {
        {static} +main(args: String[]): void
   }

   class Employee {
        -id: long
        -dateOfHire: LocalDate

        +<<new>> Employee(id: long, name: String,
        \t\t\t    dateOfBirth: LocalDate, \n\t\t\t    dateOfHire: LocalDate)
        +getId(): long
        +computeAge(): int
        +<<override>> toString(): String

   }

   hide Driver fields
}

By utilizing the power of inheritance, we were able to remove two redundant instance variable declarations from Employee along with two redundant method declarations. These instance variables and methods are now written only once - in Person.

The benefit may seem limited in this small example. However, it is important to consider the impact of inheritance on a larger software system. For example, in a larger software system, we could extend this hierarchy to include other people. We could add classes for Student, Athlete, Professor, etc. and each of these classes would automatically inherit the instance variables and methods from Person. So, in general, declaring entities (methods, variables) in the parent eliminates redundant code across all child classes!