Flexible Designs With Composition
Inside here, we’re going to create a new instance attribute that will map an ID to a role class. The ID
'manager' will map to a
'secretary' will map to a
'sales' will map to a
SalesRole, and finally, the
'factory' will map to a
If you noticed, I’ve named this instance attribute starting with an underscore (
_). This tells other developers that they shouldn’t access the
._roles attribute from outside of this class. Instead, we’re going to create a method inside of this class that can be used to obtain the desired role without having to have access to the dictionary above.
This method will be called
.get_role(), and it will take in a
role_id and return an object representing that role. In order to get the class associated with the
role_id, we can call the dictionary’s
.get() method, passing in the
role_id is any of the four roles, the class associated with that role will be stored in this
role_type variable. The only problem is if something that is not a role is passed into this method, the
.get() method will return
None, which will lead to an exception when we try to instantiate it. To check for this I’ll type
if not role_type: then raise a
ValueError and we’ll make it say
role_type is valid, we’re going to return a new instance of that class. Remember,
role_type is referencing one of our role classes right now, and not an object. In order to instantiate that class into an object, we add these parentheses after the class name.
One last change I’m going to make to this class is within the
.track() method. Instead of storing the result of working and printing it, I just want to call the
.work() method on the current
employee, passing in
Next, we are going to work on the
PayrollSystem will operate in a similar way. It will keep an internal database of payroll policies for each employee depending on their ID. In other words, this dictionary will map employee IDs to their payroll policy.
This looks almost identical to the last method we wrote, except the
return line doesn’t include the parentheses. All that’s left to do in this module is create the payroll policies to be instantiated.
The first class is
PayrollPolicy, which will be the base class to every other policy. It will keep track of the number of hours worked and add the new hours as appropriate using a
The last payroll class is for the commission policy. A commission employee is one who receives a commission on top of their base salary. Because they earn a salary too, that class inherits from
SalaryEmployee so that we also have access to the salary attribute.
In the past, we made this class simply accept a commission and add it onto their salary. This time we’re going to accept a
commission_per_sale value, and then calculate their total commission based on this number and the number of sales. To start, I’m going to rename
commission_per_sale, and I’ll change the instance attribute too.
Let’s create a method for calculating the total commissions value. The number of sales they make is dependent on time. Of course, this isn’t exactly representative of the real world, but let’s just say they make a sale every
Then, we can return the number of sales they make times the
commission_per_sale, and that will give us the total commission amount. The last thing we have to do here is modify the
.calculate_payroll() method to obtain the commission value from the method we just wrote.
Just like before, I want to add an internal employee address database to this module, along with a method for obtaining the address of some employee. I’ll do this by creating a new class called
AddressBook above the existing
I created an instance attribute out of a dictionary that maps employee IDs to their addresses. Then, I create a new method called
.get_employee_address(), which returns the
Address object associated with a given
Now that we have independent mechanisms in place for creating the components of an employee, we’re going to move on to the
employees module and get rid of everything except the basic definition of an
Employee. At the top, I’ll make sure we have access to the
We’re going to create a new class called
EmployeeDatabase, which will act as a database of employees all together. I will give this class an
.__init__() method, and inside here, I will create a new list of dictionaries where each dictionary represents an employee with their ID, name, and role.
That looks like this. Basically, when this function is called, it’s going to return a list and each item in that list will be an
Employee object constructed from one of the dictionaries in the list above. This line of code is relying on some
._create_employee() method, so let’s create that now. This method will be in charge of taking in an
name, and a
role and returning a fully-functional
I started this name with an underscore (
_) to indicate that this method should only be called from within this class, such as in the
.employees() method above. The address can be obtained with the
id provided, like this.
This will convert the string representation of the role into an actual role object that the
Employee objects can use. Finally, we’ll get the payroll object too. Now, all that’s left to do here is return a new
Employee object constructed and initialized with all the data that we just gathered.
EmployeeDatabase is a prime example of composition. It tracks the name, the ID, and the role of each employee. It has an instance of the
PayrollSystem, and the
AddressBook, which are all used to create
This method will accept the number of hours to make the employee work for. The string representing its duties will be stored in this variable called
duties, which we can obtain with
self.role.work(), passing in the number of hours to work for.
Then, we can print some information about the employee, along with a string containing information about the work they did. Lastly, I’ll call the
.track_work() method in the employee’s payroll policy, which will add this many hours to their total working hours.
The last module to work on is the main one,
program.py. I’ll move in to there and I’m going to delete everything and start fresh. The first thing I need to do is make sure the
EmployeeDatabase classes are all imported so we can use them.
Then, I’m going to instantiate each of these systems. Rather than using them to obtain new components like the individual employee classes do, we’re just going to use them to call their
.calculate_payroll() methods, passing in a list of employees.
This list of employees can be obtained like this:
employees = employee_database.employees(). Then, we can call the
.track() method on the
productivity_system, passing in a list of employees and the number of hours to work.
There’s line 7. And if I scroll down, we can see the
HourlyPolicy class. Ah, there’s a problem. We’re using the
PayrollPolicy class to track the hours worked, so we can remove that from the
HourlyPolicy constructor. Instead, we need to call the parent class’s
.__init__() method, which will give us access to the
We also need to do this same thing with the
SalaryPolicy. Simply inheriting from
PayrollPolicy wasn’t enough. This goes to show you what happens when you forget to initialize the parent class’s constructor, which has attributes that the child class needs.
Great! You can see that every class is being tracked in the
ProductivitySystem and they’re all being paid to the correct street address. This design is what’s called a policy-based design, where modules are composed of different policies, which are in charge of doing the actual work.
Other classes like the
PayrollSystem take in some information and employ the correct policy. As you’ll see in the next video, this type of design gives you flexibility that you’ll need in case requirements change in the future.
Become a Member to join the conversation.