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.

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:

  1. Identify issues with code structure caused by subclassing and inheritance
  2. Extract class behaviors into interfaces

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 the LabTech class
  • If we set the default permission to false, then we need to put an @Override method in the MiddleManager 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?

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?

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.

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 of Permission objects and appends it to the current permissions List. (Hint: )
  • In the constructor methods for each of your Employee subclasses, you will need to add a method call which passes along new Permission objects to set the correct permissions for each employee type! (Hint: )

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 of Permission 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!

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.