View this PageEdit this PageUploads to this PageVersions of this PageHomeRecent ChangesSearchHelp Guide

MP5

Part 1

Part 1. Double dispatch

For this exercise, download parcel, CS598rej-Grading HW 5, included below. Your task is to model buying and selling electronic parts, e.g. DVD drives. Our primary interest is calculating the actual price of a part, which depends on both the buyer and the seller. Assume that there’s only one buyer and one seller per transaction.

Companies and Retailers are two types of Merchants that engage in trading electronic parts. They can be both buyers and sellers. Depending on the type of buyer and seller, different types of discounts (summarized below) apply.

 

Seller

Buyer

Company

Retailer

Company

.8

.85

Retailer

.95

.9

According to the table, if a Company is selling an electronic part to a Retailer, it gives the Retailer a discount of .85 (i.e. 15%; 1 – 15% = .85). But if the Retailer is selling a part to a Company, it offers it a discount of .95.

In addition, each Company and each Retailer has a premiumRate that is applied to the price of a part as yet another discount.

The code to calculate the final price of a part is:

part basePrice * (buyer discountFrom: seller).

where part is an instance of ElectronicPart class, and buyer and seller is either a Company or a Retailer.

Your task is to implement the discountFrom: method. Since the discount depends on the concrete type of the buyer and the seller, this is a good place to use double dispatch. One might be tempted to write code like this:

Company>> discountFrom: aSeller

"This method is an example of how NOT to write double dispatch code."

(aSeller class = Company) ifTrue: [^ 0.8 * aSeller premiumRate.].

(aSeller class = Retailer) ifTrue: [^ 0.95 * aSeller premiumRate.].

This code will work, but it is wrong for three reasons: (1) if new types of Merchant are added, you will need to modify this code in each existing class, (2) checking the type of a class is not a good practice in OOP, and (3) this method does not use double dispatch.

In typical programming, we use single dispatch. The method which is executed depends on the name of the request (here, discountFrom: ) and the type of the receiver (Company). Even if there are many methods with this name in the system, only one of them can be invoked on objects of class Company.

Double dispatch is an OO technique that determines what method to execute based on the name of the request (here, discountFrom: ), the type of the receiver (Company), and the type of the input argument. To invoke the correct method, two method calls (or dispatches) are needed. Rather than inspecting the input argument directly (as with the if statements above), double dispatch results in invoking another method in the body of discountFrom:. Which method is called depends on the type of the receiver and the type of the argument. For example, here is a correct implementation of the discountFrom: method in class Company:

Company>>discountFrom: aSeller

^ aSeller discountForCompany: self

This code invokes the discountForCompany: method on the seller object passing it the reference to the buyer object (self). If we assume that aSeller is actually an instance of the Retailer class, here is the implementation of that method:

Retailer>>discountForCompany: aCompany

^ 0.95 * aCompany premiumRate

Inside the body of this method, it is guaranteed that the input argument (aCompany, or the buyer) is of type Company and the receiver (i.e. seller) is of type Retailer. A quick glance at the table above suffices to see that the appropriate discount is indeed .95. Since the premium rate depends on the instance of the argument, premiumRate method is called on each invocation.

1.1 Review all the code provided. It includes the code described above and is sufficient to pass testOne in the DoubleDispatchTest class.

1.2 Implement the code for the other 3 combinations of buyers and sellers with discounts specified in the table above. The base class Merchant provides the abstract definitions of all required methods. Make sure that testTwo and testThree pass.

1.3 Add a new subclass of Merchant, Individual. The table above now changes to:

 

Seller

Buyer

Company

Retailer

Individual

Company

.8

.85

.95

Retailer

.95

.9

.85

Individual

X

X

1

According to the updated table, Individuals cannot sell electronic parts to Companies or Retailers. Any attempts to do so should generate an exception.

Note that adding a new subclass requires adding a new method to all existing classes to handle the new type of Merchant.

To learn more about double dispatch, you can read the original paper on the topic by Dan Ingalls (see link below). You can also read "Arithmetic and double dispatching in Smalltalk-80" by Kurt J. Hebel and Ralph E. Johnson (also available below).

 

Part 2. Changing Payroll to manage vacations

For this portion, you will need to reuse the code from MP2. Make all your changes in the Payroll HW2 parcel.

The payroll system doesn't handle vacation. Class Employee has a variable defined for it, and each time-card collects the amount of vacation time that was taken that week, but the code for processing time-cards doesn't do anything about vacation.

Change the payroll system to record the amount of vacation time that has been taken and the vacation time that has accrued. Assume that for every 50 hours of non-overtime that an employee works, they get 2 hours of vacation time.

You should not need to add any more instance variables to any classes (but you may if you need to so that it is more clear, easier to understand, etc). You could do this by adding to accrued vacation  (hours worked / 25) - hours of vacation taken. Note that this will lead to fractional hours of vacation accumulated. This might be a problem if this were being written in COBOL, but it is OK for Smalltalk.

To find where the work gets done, try single-stepping through the posting of a time-card. Place a halt to start the debugger, and then single-step from there. You'll have to figure out how to get the hours of vacation taken and the hours worked. They are stored in the time-card.

Write tests to test your changes. Put them in a single class PayrollVacationsTest.



Deliverables


Since there are multiple classes involved with each solution, it is best if you could just list the methods that you added/change for the class. This makes it easier for me to quickly read what you have done to make the test cases pass. There are not many methods that you have to change so typing them out will not be a problem.

Use the standard notation to describe the changed methods.

For instance methods:

ClassName >> myMethodWith: param1 and: param2
"Do something here"
^ param1 + param2

For class methods:

ClassName class >>myMethodWith: param1 and: param2
"Do something here"
^ param1 + param2

Any reorganization (creating new categories, renaming existing ones) should also be stated.

For part 2 where you have to write your own tests, please write some comment on what you are testing for.

Files


CS598rej-Grading HW5.st
ingalls.pdf
double-dispatch.pdf


Link to this Page

  • Machine Problems last edited on 14 May 2008 at 4:25:53 pm by c-98-212-224-168.hsd1.il.comcast.net