
Employee Database
Beware! This document needs cleaning up. Formatting may be messy or broken. Text may be inaccurate or in need of editing.
You can look at this preview, but be aware that the content will change before the instructor assigns this to the class.
This document is the result of an automated conversion. You can view the original version but again beware: that version too is not necessarily what will eventually be assigned.
Learning goals
In this activity, you will learn how to refactor a subclass structure to use interfaces and has-a relationships instead. After this activity, you should be able to:
- Identify issues with code structure caused by subclassing and inheritance
- Extract class behaviors into interfaces
Task 1: Understanding the Problem
You are the Head Scientist at ACME Corporation, a company manufacturing such illustrious products as jet-powered roller skates, explosive tennis balls, and invisible paint. As a hub of scientific innovation, all of your employees so far have been laboratory technicians. However, as a result of your business success, you now have so many lab techs that you need to add a few middle managers to your company. The invisible paint keeps spilling all over the anvils, leading to all of the techs tripping over invisible anvils, and you really need someone to keep track of lab space and work assignments!
Run the DatabaseMain
class and observe how it behaves. You will preserve that functionality as you work on this activity!
We would like to add a new type of permission (like labPermission()
and supplyClosetPermission()
in the Employee
class) for middle managers: a room assignment permission, which gives employees with the middle manager role the permission to assign people and projects to specific rooms. However, lab techs should not have permission to do this—they would all fight each other to get the room with the nicest view!
Because the employee database program has already been set up with class-based inheritance, the only way to do this currently is to declare a new permission method in the Employee
class. We must pick a default permission. What should it be? Talk it over with your partner.
- If we set the default permission to
true
, then we need to put an@Override
method in theLabTech
class - If we set the default permission to
false
, then we need to put an@Override
method in theMiddleManager
class
Either option is annoying! What is the point of all of this inheritance if we have to override the inherited method half the time, anyway?
Interlude: Let’s think ahead
You might say, “This doesn’t sound so bad, honestly. What’s one little method, anyway? It just returns a boolean.”
Fair enough. Suppose we don’t bother to find a better approach, and let the code keep growing as is. Fast forward to 10 years in the future:
The company has grown enormously, buoyed by the success of its jet-propelled unicycles. It now has dozens of types of employee, each with its own new permission needs. The Employee
class is littered with numerous highly specific permission methods that have nothing to do with most of the employees at the company.
Some of those methods don’t just return a boolean literal. Certain lab techs and custodial staff may or may not have permission to enter chemical hazard areas based on complex rules involving their safety certification and lab coat material. Not wanting to copy the complex implementation of chemicalHazardAreaPermission()
across multiple subclasses, we decided to put the code in a superclass. Inheritance to the rescue!
However, the employees to whom this rule applies straddle different parts of our already-established inheritance hierarchy, so the only available superclass in which we can put our default implementation of chemicalHazardAreaPermission()
is Employee
itself. Worse yet, that method’s complex logic requires safetyCertificationLevel
and labCoatMaterial
instance variables, so those variables must also live in Employee
.
The result: every single Employee
must have a labCoatMaterial
, despite the fact that most employees don’t even wear lab coats. Multiply this by all the other permissions, and the Employee
class has become so large that nobody can really understand it.
This code smell — a superclass overstuffed with methods and variables that only really apply to a few of its subclasses — is a sign of bad decisions about inheritance. This problem is not hypothetical, and is not limited to class activities your instructors cook up to demonstrate principles. It happened to Microsoft, for example, in the early 1990s, with their MFC framework for building Windows apps. In MFC, one gigantic superclass of all UI elements ended up with methods for scrolling (even though most subclasses don’t scroll at all) and fonts (even though most subclasses don’t have text), and…well, just take a moment to scroll through its mind-boggling list of methods. (Don’t try to understand; just behold the sheer number of methods in that class.) MFC overused inheritance, and paid a high price in complexity.
OK, suppose we do want to find a better approach after all! What then?
Task 2: Implement a Permission
Interface
We’ve caught an issue early. As the ACME Corporation continues to grow and add new employee types, we want a flexible way to add additional permissions, each with their own unique relationship to pre-existing employee roles.
Similar to your Design Patterns reading, what we need is an interface that handles different permission types. Then, our Employee
class can contain a List of Permission
objects.
Create a new interface called Permission
. Add these methods:
-
getName()
, which returns the name of permission that the object represents (this will be Laboratory Access, Supply Closet Access, or Room Assignment Permission) -
getStatus()
, which returns a boolean representing whether an employee has permission
Add a List of Permission
objects called permissions as an instance variable in Employee
class.
- The
Employee
constructor method should initialize this as an empty ArrayList
Remove the labPermission()
and supplyClosetPermission()
methods from your Employee
class. This should break things! Your MiddleManager
class now overrides methods that no longer exist, so remove those as well. Also, your description()
methods now call removed methods—we’ll fix those later.
Task 3: Adding Permissions Classes
We need three permissions classes, each of which should implement the Permission
interface:
LabAccess
SupplyClosetAccess
RoomAssignment
These classes should have a constructor method which takes in a boolean value, meant to represent the status of the employee’s permission to do that thing. They will need a getter method for that boolean value, as well.
They should also have a method which returns a string stating the type of the permission.
- “Laboratory Access”
- “Supply Closet Access”
- “Room Assignment Permission”
Now that you have those, it’s time to add them to the Employee
class. We already created a List to hold Permission
objects; let’s also add some functionality so that we can put Permission
objects in that List.
- Add a method to your
Employee
class which takes in a List ofPermission
objects and appends it to the current permissions List. (Hint: take a look at the Javadoc page for the List interface!) - In the constructor methods for each of your
Employee
subclasses, you will need to add a method call which passes along newPermission
objects to set the correct permissions for each employee type! (Hint: You’ll need to pass these Permission objects in as an already-constructed list. Remember List.of()?)
As a reminder:
- Laboratory technicians have access to the laboratory and supply closet, but cannot make room assignments
- Middle managers can make room assignments, but do not have access to the laboratory or supply closet
Finally, you will need to fix those broken description()
methods. Doing this takes some fiddling. I suggest:
- Adding an accessor to the
Employee
class which returns a List ofPermission
objects - Using that accessor in each subclass to generate a similar String to what was previously generated by the
description()
method
After all of this is done, you should be able to run the DatabaseMain
program again without errors!
Bonus
Now that you’ve made it easy to dynamically handle permissions for each type of employee at ACME Corporation, it’s time to challenge yourself! The lab techs keep exploding things, and this necessitates a new role at the company: Cleaner
. Add a Cleaner
class that extends the Employee
class, and give it permissions for lab access and room assignment (so that they can keep techs out of the room while they’re cleaning it), but which doesn’t have supply closet access.
You will also need to modify the base DatabaseMain
program so that you can add this new employee type to the company.
Other bonus tasks:
- Create a new type of permission, and decide which employees should have access to it. Make sure that it prints as part of your
description()
method! - Modify the conditions for lab access so that it takes into account a new variable: whether the employee has had lab safety training.