3.11. The Spreadsheet Conundrum

 <  Day Day Up  >  

3.11. The Spreadsheet Conundrum

The spreadsheet is an analogy for many design decisions you make during development. Consider the data on a spreadsheet, as shown in Figure 3-1. If you were to store the data in a linear manner in a file, you would need to decide whether to store the data by row or by column. Perhaps storing it by row seems most natural. What if programs that require the data in column order access it later? Row order makes that access inefficient.

Figure 3-1. Spreadsheet of CDDiscs and days

If you knew that future programs were going to use column order, you should have considered that in your initial code. However, if you cannot reasonably foretell in what order data will be accessed, you cannot worry too much now how it should be stored. Just document your assumptions and later on, if you have to change your approach, you will at least know why you did it the other way.

Many facets of programs parallel the spreadsheet. For example, string resources and languages form a spreadsheet such as that shown in Figure 3-2.

Figure 3-2. Spreadsheet of resources and languages

Typically the data in Figure 3-2 is stored with strings stored sequentially for each language. If you will be adding more languages, having the data stored in that manner makes sense. However, if you are always adding more resources, but never adding more languages, it could be more efficient to store the strings sequentially by resource.

THE SPREADSHEET CONUNDRUM

Recognize when you are making the row/column decision.


This spreadsheet conundrum is reflected in the organization of graphics packages. A package can be organized in two ways, which correspond to the rows and columns of Figure 3-3.

Figure 3-3. Graphics spreadsheet

3.11.1. A Graphics Example

For example, to draw a rectangle on a device, you could write the code as shown in Example 3-1.

Example 3-1. Shape drawing self
 class Rectangle         draw(DeviceContext a_context);          class Circle         draw (DeviceContext a_context);          Rectangle a_rectangle = new Rectangle();     Circle a_circle = new Circle();     a_rectangle.draw(a_device_context);     a_circle.draw(a_device_context); 

Example 3-1 represents selecting the column headings as the shape performing the drawing. The alternative is to select the row headings to perform the drawing, as shown in Example 3-2.

Example 3-2. Device drawing shape
 class DeviceContext         draw(Rectangle a_rectangle);         draw(Circle a_circle);          DeviceContext a_device_context = new ScreenDevice();     a_device_context.draw(a_rectangle);     a_device_context.draw(a_circle); 

In Example 3-1, it is easy to add additional drawing shapes , such as a triangle . You create a class for the new shape and implement a draw method. In Example 3-2, it is harder to create a new shape. You need to add a method to draw the shape to every implementation of the device context.

3.11.2. Who's in Charge

The same conundrum applies to the question of "Who's in Charge." For example, you could arrange the operation of dialing a phone number as shown in Example 3-3.

Example 3-3. Phone does the dialing
 class Phone         dial(PhoneNumber a_number)          Phone a_phone = new Phone();     a_phone.dial(a_phone_number) 

Or you could arrange the operation as shown in Example 3-4.

Example 3-4. PhoneNumber does the dialing
 class PhoneNumber         dial(Phone a_phone)          PhoneNumber a_phone_number = new PhoneNumber();     a_phone_number.dial(a_phone); 

The distinction regarding who is in charge of dialing a phone number might not make any difference until you come across a situation in which you might want to send another set of digits to the phone (such as an account number):

 class AccountNumber         IntegerString number; 

In Example 3-3, you would add another method to Phone. For example :

 class Phone         dial(PhoneNumber a_number)         dial(AccountNumber a_number) 

In Example 3-4, you would add a dial() method to the AccountNumber class to keep operations consistent with the PhoneNumber class.

 class AccountNumber         dial(Phone a_phone); 

The tradeoff between these two approaches is not clear-cut . In one instance, you are asking a Phone to dial a set of digits. In the other, you are asking the object to dial itself on a Phone you provide. If you prefer one way, document your reason in your design journal. Later on, you might come across a situation in which the other approach seems more appropriate. You can review your previous decision and see whether the reasoning still makes sense in light of the new context.

The spreadsheet conundrum appears in many forms, which I will note throughout this book. Recognizing when you are making a tradeoff between two ways of organizing a class structure can help you to make better design decisions.

 <  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