13.1. The Next Step

 <  Day Day Up  >  

13.1. The Next Step

"OK, what do you want next?" I asked Sam.

"A double cappuccino with latte," he replied eagerly.

"I meant for the system," I said.

"Oh, I'd like to get the charge system running, so I can bill customers monthly rather than for each rental. That will avoid leaving a lot of cash lying around the store. I also want to be able to offer discounts to frequent renters," Sam rambled on.

"OK," I said, "that sounds like two things. Which one do you want first?"

"Let's start with the charging part," he replied.

"We have at least two halves to that part: determining how much to bill the customer and then actually doing the billing. The determination part seems simpler: total up completed rentals and any late fees to compute a bill. The second part requires a bit more work. You need to sign up with an Internet-based credit card handler. That's another external interface, but a much more interesting one. You can't just test against that database willy-nilly. You don't want to charge customers for things they haven't bought," I stated.

"We need a new class. The one-line definition of this class is 'A charge to a customer for rentals during a period of time.' What do you want to call this class: Invoice or Bill?" I asked.

"I have a brother named Bill. So let's call it Invoice or else I'll get confused ," he answered .

Here is the class that Sam and I created:

 class Invoice         Customer the_customer         InvoiceDetail the_detail         Dollar total         submit_for_payment( )         create_invoice_detail( ) 

InvoiceDetail represents the details of the charges on the invoice. I am not sure whether that will turn out to be a CommonString or a more structured class. So we denote it as a separate class ("When You're Abstract, Be Abstract All the Way").

The Customer class needs to know the customer's credit card number, so we add it to that class:

 Customer         Name name         Address address         CreditCard credit_card         PhoneNumber home_phone_number         Boolean on_charge_plan 

On a monthly basis, Sam will create invoices for each customer and submit them for payment to the credit card company. Sam and I created a brief outline of the process for this activity:



Invoice_customers

  1. For each Customer on a charge plan in CustomerCollection , create an Invoice .

  2. Submit the Invoice to the credit card company.

  3. If the charge is approved, denote Invoice as paid.

Otherwise...

We stopped at the word Otherwise . Otherwise what? We need to handle this deviation. Does Sam keep charging unpaid invoices? Are they printed and sent to a collection agency? Sam can leave the decision up to a business rule. If we try to handle all the possibilities in this use case, it can get complicated. Sam and I fill in the ending:

Otherwise, denote it as an unpaid Invoice .

We deal with unpaid Invoice s in another use case ("Separating Concerns Makes Smaller Concerns"). Sam is unsure what to do with them at this time, so we just note a simple process for this use case:



Handle_unpaid_invoices

  1. Produce list of unpaid Invoice s.

Sam may later want to create an automated system to handle the unpaid invoices. At this point, we can use this list for testing purposes to ensure that the Invoice_customers use case works properly.

13.1.1. Dealing with Failure

Each invoice is submitted for payment to InvoiceProcessor . This interface represents the functionality for a credit card processor. The Invoice with the Customer and its CreditCard has all the information needed to make a credit card charge. The InvoiceProcessor interface looks like Example 13-1.

Example 13-1. InvoiceProcessor
 interface InvoiceProcessor     process_for_payment(Invoice an_invoice) 

If Sam requests the flexibility to use multiple vendors to process credit cards, we could separate out another interface ("Separate Concerns to Make Smaller Concerns "). The process_for_payment( ) method in InvoiceProcessor would use a CreditCardProcessor that represents the interface to a credit card processor. This interface could look like Example 13-2.

Example 13-2. CreditCardProcessor
 class CreditCardCharge     CommonString first_name     CommonString last_name     Address billing_address     PhoneNumber phone_number     Dollar amount     CommonString identifer interface CreditCardProcessor     submit_a_charge(CreditCardCharge a_charge) 

The implementation of this interface needs to know Sam's merchant ID and other information specific to the vendor processing the charge.

We need to examine the possible situations that submit_a_charge( ) might encounter. These include the following.



ChargeAccepted

The charge was processed successfully.



UnableToContact [*]

There was a network or server failure.



ChargeRejectedInsufficientFunds

This could be a temporary failure.



ChargeRejectedAccountNotValid

This is a permanent failure.

We need to determine how to respond to these failure situations. In the event of ChargeRejectedInsufficientFunds or ChargeRejectedAccountNotValid , the Invoice is unpaid and is dealt with by the Handle_unpaid_invoices use case.

The UnableToContact event poses an interesting question. Is this failure a deviation, a nonfatal error, or a fatal error (see Chapter 3)? Since the service involves a connection to an outside server over a public network, failure can be expected to occur. Although a failure is expected, it should not occur during normal processing. If no backup network or backup server were available, the event certainly is a fatal error. Further processing is futile. The user should be informed of the situation. He might be able to do something to correct the situation (e.g., check the Internet connection).

CONSIDER FAILURE AN EXPECTATION, NOT AN EXCEPTION

Plan how operations should respond to failures .


13.1.2. Planning Tests

Tim and I know that the CreditCardProcessor implementation has to be tested carefully . ZIPCodeLookupInterface was relatively easy to test. We submitted a variety of addresses and compared the results to a definitive source of Zip Codes. Processing a credit card is a little more difficult. We need to test the implementation of CreditCardProcessor to ensure that it works according to the vendor-supplied protocol. Vendors have test servers to check that charges are submitted in properly formatted requests.

We need to check whether CreditCardCharges are being created accurately. We create our own test version of the CreditCardProcessor interface in Example 12-2. ("Build Flexibility for Testing"). Instead of submitting the charge to the vendor, the test version just creates a list of the charges. If the vendor's test system cannot simulate all the failure situations, this test version can be programmed to do so.

The vendor's test server and the production server provide the same interface to the real implementation of CreditCardProcessor . The only difference is that a charge submitted to the production server produces a real charge to a real credit card. CreditCardProcessor should work the same, regardless of the actual server. We need to have a way to check that this is the case. We will submit a set of small charges (e.g., $.01) to both servers. We check that the real server produces the correct results by examining the statement Sam receives from the vendor.

 <  Day Day Up  >  


Prefactoring
Prefactoring: Extreme Abstraction, Extreme Separation, Extreme Readability
ISBN: 0596008740
EAN: 2147483647
Year: 2005
Pages: 175
Authors: Ken Pugh

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net