Friday, December 15, 2006

Biller - Design Of A Receipt Generator

Problem Specification: SALES TAXES


Basic sales tax is applicable at a rate of 10% on all goods, except books, food, and medical products that are exempt. Import duty is an additional sales tax applicable on all imported goods at a rate of 5%, with no exemptions.


When I purchase items I receive a receipt which lists the name of all the items and their price (including tax), finishing with the total cost of the items, and the total amounts of sales taxes paid. The rounding rules for sales tax are that for a tax rate of n%, a shelf price of p contains (np/100 rounded up to the nearest 0.05) amount of sales tax.

Design

Receipt

A receipt can be viewed as a collection of sold items (hereinafter referred to as receipt-items) which contribute to the receipt’s characteristics like the total amount and the total amount of sales-tax.

Figure 1 – Receipt: Collection Of Receipt-Items

Application

In the application, a receipt has to be generated and displayed. The steps involved would be:

  1. Create a receipt-item.
  2. Add it to the receipt.
  3. Repeat 1 and 2 for all items to be added to receipt.
  4. Display the receipt.

The required attributes of the receipt (from the problem-specification) are an item-listing, total amount and total sales tax. All these are dependent on the individual receipt-items and hence must be characteristics of receipt-items.

Receipt-Item

A receipt-item must provide the following information:

  1. Description
  2. Cost
  3. Quantity
  4. Sales Tax (varies according to sales-tax policy)
  5. Price (The shelf-price depends on the sales-tax)

Figure 2 - Receipt Item

Since Sales-Tax and Price are dependent on extrinsic factors, we implement them as methods (the exact implementation-strategy will be detailed in the following sections). Items 1 to 3 may be implemented as attributes of the Receipt-Item abstraction.

Sales-Taxes

There are two types of sales-tax defined in the problem– Import Sales Tax and Standard Sales Tax - any or both or none of these may be applicable on an item. But essentially both imply an amount to be added as tax.

Figure 3 - Sales Taxes

Both these types of sales-tax have to be rounded up to the nearest 0.05, but at a rate defined by the specific type of sales-tax.

Computing the Price of a Receipt-Item

Essentially, the Price of a Receipt-Item = Cost + Sales-Tax(es)

The price of a receipt-item will depend on the sales-taxes applicable on it. But the tax-policy can vary widely (and wildly!). The implementation of a receipt-item should, ideally, not vary based on such an obviously extrinsic factor, if it can be avoided.

Hence we delegate the responsibility of calculating tax to Sales-Tax entities. And since the number of sales-taxes to be applied may vary from none to many, this entity needs to be a list that could vary. This can be achieved by having the Sales-Tax entity point to the next Sales-Tax entity (if any) and so on.

Figure 4 - Sales Tax Computation: Chain of Responsibility

The price of a receipt-item can be easily determined after sales-tax computation.

Thus, the receipt-item would be able to provide information about both its price and its sales-tax.

Receipt-Item Creation: Sales-tax policy isolation

As mentioned earlier, the sales-tax could vary from time to time. It would be beneficial if we could abstract it from the receipt-generation application. Indeed, why need a cashier be required to know the intricacies of taxation and idiosyncrasies of politicians? J The application shouldn’t change each time the policy does!

What this means in the context of our arrived-at design, where each Receipt-Item will be equipped with a Sales-Tax entity, is that we will need to fit it with different kinds of Sales-Tax entities based on the taxation-policy.

We move such taxation-policy dependent logic/functionality into a separate class (Receipt-Item-Creator) which can create an appropriately built Receipt-Item, once it is given basic intrinsic characteristics of the item being sold. (Surely, it is reasonable to expect the cashier to know things like whether an item is imported, whether it is a food-item, its cost etc.)

Figure 5 - Receipt Item Generation: Builder

Displaying the receipt

We can have a function in the application to display (say, displayReceipt) a receipt. This function is not made a part of Receipt itself because having it so would tie the Receipt class to the Application mechanism (console/window/printer for starters).

Conclusion

We are now capable of implementing the steps of the application to demonstrate receipt-generation.

  1. Create a receipt-item === ReceiptItemCreator.create
  2. Add it to the receipt. === Receipt.add
  3. Repeat 1 and 2 for all items to be added to receipt.
  4. Display the receipt. === Function in the application === Receipt.*,

The consolidated class-diagram for the system is given on the following page. Please note that it does not comprehensively list all the methods and attributes in each of the classes.

Figure 6 - Biller System Class Overview

Design Assumptions and Choices

  • The primary objective of the system is sales-tax computation.

  • Sales-tax is calculated per item. In the case where the quantity of the item is more than 1, this makes a difference, whether the sales-tax is applicable on the total cost or on the cost of each item individually. The data in the problem-definition is insufficient to come to a conclusion. Choosing to go with per-item sales-tax.

  • Items have not been classified as Taxable and Non-Taxable. Instead, we choose to embed a suitable Sales-Tax entity via the Receipt-Item-Creator.

  • A similar strategy for modifying the description of the item was also considered. But such modification would have been complex (“Imported box of chocolates” or “Box of imported chocolates”?). For the same reason, “imported” was not chosen as a modifier which would append “imported” to the description and Imported-Tax to the Sales-Tax.

  • It was possible to have a separate class for each Receipt-Item type. The resulting “class-explosion” would not have added much value to the system since we are primarily interested only in sales-tax. In the context of the system, there was not enough difference in the treatment of items based on such type to justify separate classes. The only drawback is that we lose the type (whether it was a book or food etc) in the Receipt.

  • The operation of the system, as mentioned in the algorithm, is simple enough that sequence diagrams are not needed.

Extensibility

  • New types of taxes may be applied by sub-classing Sales-Tax.

  • Major changes in taxation-policy can be handled by sub-classing Receipt-Item-Creator. Slight variations in the taxation-policy can be accommodated by modifying it.

  • If the type of the item (book/medicine/other) becomes of interest, the behavior can be added to the system by sub-classing Receipt-Item.

  • Changes will be localized to Receipt-Item-Creator. The application can remain mostly unchanged.



Thursday, November 23, 2006

Rounding To The Nearest

Programming languages and spreadsheets usually have a function to round to the nearest integer. But the task of rounding to the nearest something else comes up every now and again. Rounding up to lowest higher multiple of n, rounding down to the highest lower multiple of n , rounding to the closest n....

Algorithm roundToNearest
-------------------------------------
1. Multiply the amount by the reciprocal of the rounding base (N) you want to round to.
number = number * (1 / roundingBase)

2. Round the result using the built-in function.
number = roundToInteger(number),
where roundToInteger is whatever rounding function you want to use.
In case there is no such function available, you can simulate it. roundUp = integerPart of (number + 1), roundDown = integerPart of (number - 1), roundToClosest= integerPart of (number + 0.5) etc.

3. Multiply the resulting amount by the rounding base itself.
number = number * roundingBase

4. The resulting number is the original number rounded to the nearest rounding base.
result = number

Example 1 - Rounding 123.68 (up) to the next highest multiple of 40
Step 1: 123.68*(1/40) = 3.092
Step 2: 3.092 + 1 = 4.092, integer part is 4. (Did integerCeiling(3.092) )
Step 3: 4 * 40 = 160
Result: 160 is the answer.

Example 2 - Rounding 46.27 to the closest 0.06
Step 1: 46.27 * (1/0.06) = 771.16666666666666666666666666667
Step 2: Integer part of 771.16666666666666666666666666667 + 0.03, 771
Step 3: 771 * 0.06 = 46.26
Result: 46.26 is the answer.

Generalization (Compulsory Philosophical Reflection)
Often, what we have with us may not be exactly what our tool requires or expects. Adjust, make do and un-adjust! More technically, transform whatever is incompatible into a compatible form, do the required operation, apply the exact reverse transform. This is a strategy that is often needed in problem solving and. generally. it works!

Wednesday, November 15, 2006

U3D - Diagramming & Standardization

# Importance Of Diagramming
* Complexity
o Inadequacy of tables and text
o Things become clearer visually
* Communication
o Bigger projects, Bigger teams
o Typing Speed x Time != LoC
o Not enough that we know it
+ House plan

# Importance of Standardization
* My diagrams are the best!
o Time and effort
o Hard to be consistent
o Reinventing the wheel
o Onus is on us to do the explanation
* UML = "The" Unified Modeling Language

Friday, October 27, 2006

UML Triple Distilled

A few months back I had given some training in (the) UML for my colleagues. The course was named UML Triple Distilled (a la vodka and Martin Fowler's classic UML Distilled).

The objective of the session was to present the UML from a non object-oriented perspective, as a communication and diagramming tool. People won't often say this, but, really, you don't necessarily have to be an OO guru to benefit from the UML.

In the coming days, I will be putting up slides from the session over here along with some reflections on the slides. Here's the introductory slide.

Wednesday, October 04, 2006

SFINAE

Function templates participate in name resolution for overloaded functions, but the rules are different. For a template to be considered in overload resolution, the type has to match exactly. If the types do not match exactly, the conversions are not considered and the template is simply dropped from the set of viable functions. That's what is known as "SFINAE" — Substitution Failure Is Not An Error.

Monday, September 11, 2006

Subversion - What can be in a name?

Subversion is the configuration management tool that aims to be a replacement for CVS, the older *NIX . It's pretty decent, although it is not all that sophisticated. Just as well, because the project's purpose-statement does not position it as a do-it-all, suitable-for-all-users thing. It just does what it claims to, versioning and does it pretty well.

But what I love about the tool, more than its features and even though the lack of certain features sometimes irritates, is the name: Subversion. I think the name is just charming, to say the least.

First of all, the word "version" is present in the name and versioning is what it does. You could even think of it as a tool that helps manage "sub-versions" of an object. Think "sub-version" instead of version.

If you think the "sub-version" line of reason is juvenile, then take this. "sub-" as a prefix could mean "Under, lower, below, secondary, inferior". Choose the "under" meaning and lo, sub-version takes the "under version (control)" meaning. :-)

But there's more to it The meaning of the word "subversion" is
1. Destroying someone's (or some group's) honesty or loyalty; undermining moral integrity
2. The act of subverting; as overthrowing or destroying a legally constituted government
This refers to Subersion's aim to be a complete replacement for CVS, that of underming the CVS ethos entrenched in *NIX culture.

Thus the name not only describes what the tool does, but it also embeds within it allusions to its history, culture and ultimate goal... and what's more, the mischief is hidden within the multiple possible interpretations.

What's in a name, anyone?

Monday, June 19, 2006

Freeware UML Tool

Have found an open-source and freeware tool for making UML diagrams.
It's called StarUML. You can download it from the StarUML.com website.

I used it for sometime and found it to be rather stable and usable. It most definitely is not crippleware. Can't say the same about Poseidon or the other community-edition tools like Visual Paradigm or UMLStudio.

StarUML is definitely recommended, though it is not without its share of glitches.

Monday, May 29, 2006

Degenerate Classes

A degenerate class is one that has no methods at all.
In C++ it would have a pure virtual destructor.
In java such classes are called “Marker Interfaces”.

Decorator Pattern

An example to demonstrate the Decorator design pattern which I had contributed to Wikipedia a few months back. It has since been improved and changed over there but thought maybe I should maintain a copy... Read the latest version from here.

This Java example uses the Window/Scrolling scenario

class Window {

public void draw() {
// Draw window
}
public void setSize(int height, int width) {
// Set window size
}

// Other Window methods
}
The decorator is given the same interface as the component it decorates. The decorator must be explicitly mentioned as supporting the Window interface for it to be useful in a real scenario; it must have the same type.

Typically, the decorator's methods will only pass requests to the underlying decorated component, but it can also perform operations before and after the call. Then the decorator defines extra methods (decorations) to extend the decorated component's functionality.

class VerticalScroller extends Window {

private Window myWindow;

public VerticalScroller (Window baseWindow){
myWindow = baseWindow; // Save the reference, which we will use
}

public void draw() {
//draw the vertical scroller
myWindow.draw();
}

public void setSize(int height, int width) {
//implement vertical scroller-specific functionality
myWindow.setSize(height, width);
}
}

class HorizontalScroller extends Window {

private Window myWindow;

public HorizontalScroller (Window baseWindow){
myWindow = baseWindow; // Save the reference, which we will use
}

public void draw() {
//draw the horizontal scroller
myWindow.draw();
}

public void setSize(int height, int width) {
//implement horizontal scroller-specific functionality
myWindow.setSize(height, width);
}
}

Using the decorator:

We can add a VerticalScroller decorator to a Window, say, theWindow, by saying

theWindow = new VerticalScroller (theWindow);
//A vertical scroller gets added to theWindow
//Constructor internally saves a reference to the old undecorated theWindow

The object, theWindow, will have vertical scrolling functionality after this.

Monday, April 17, 2006

IsDerivedFrom

From GotW

// Example 3(a): An IsDerivedFrom helper
//
template
class IsDerivedFrom
{
private:
class Yes { char a[1]; };
class No { char a[10]; };

static Yes Test( B* ); // undefined
static No Test( ... ); // undefined

public:
enum { Is = sizeof(Test(static_cast(0))) == sizeof(Yes) ? 1 : 0 };
};

Wednesday, March 15, 2006

Jargon Clash

=====================
JARGON CLASH
(C) 2006 Thomas Jacob
=====================

Introduction
------------------
The construction of a system is a progression from one stage of clarity to the next.

1. We start with hazy requirements - It's a three-line requirement doc!
2. We try to gather more details - Now, what could this be?
3. We feel we have achieved enough clarity - Oh, that's what he meant!
4. We start to implement the system. - What could go wrong? It's clear and easy.
5. ...and yet, in the final count, the system fails to match the user's expectations/requirements.

Often, the problem lies in the inaccurate representation of facts and requirements. In this article, we explore one of the causes for this.

The Quest For The Holy Grail
-----------------------------------------
The importance of capturing the requirements correctly (as a "Requirement-Model") cannot be overemphasized. After all, the system is built to satisfy the requirements of the users. If we understand the requirements incorrectly, we have an inaccurate Requirement-Model, and the system (which is based on it) will not, cannot be successful.

A major obstacle in our quest for the perfect Requirement-Model is the lack of in-depth knowledge about the field in which the system will be used. This is unavoidable as one cannot be an expert in everything. And that's just as well because what's required of a maker of a paintbrush is that he should be able to make the paintbrush well, not that he should be able to paint well! There is no need for the designer of a CD-player to be a composer as well (though of course, it helps the cause if he "understands" music). It's horse for courses and that's why you have been chosen to develop the system and not the user.

Drinking From The Poisoned Cup!
-------------------------------------------
We need to close the gap in our understanding of the requirements (The system needs to be built, right?) by asking the users for further details. The users might explain things in a seemingly familiar language, but the words could mean something entirely different than the meaning you got. Beware of Jargon-Clash! The user and you, both are experts, and experts, almost as if by definition, tend to use jargon. What you hear and get may not be what they mean!

A trivial, stupid(?) and impossible(?) example: You are implementing a distributed system for a retailing chain. The user says that the client needs to retain the bill. You could very well take thisthis down and, justifiably, note that the bill information needs to be stored at the client-side and go "Wow! Finally...I get to do distributed databases!". Probably all the user would have meant was the the customer (client for the user) needed to be given a printed bill!

Another, closer-to-reality example. The term "ATM" would mean "Automated Teller Machine" (the cash-dispensing type) to the "normal" user but to a networking professional, ATM would mean "Asynchronous Transfer Mode", a technology for networking. What then would an ATM network be? And couldn't there be ATM networks which use ATM technology for the network? Think about the very-many ambiguous conversations possible!

The Jargon-Clash isn't limited to the "unsavvy user, savvy developer" system-construction scenario either. In fact, the more similar the jargon of the user and the jargon of the developer, the higher the probability and impact of Jargon-Clash is. What's worse is that the Jargon-Clash will be less obvious. The effects may become evident only at an advanced stage of the project.
Example: Computer networking and telecom are closely related, especially in this age of Internet telephony. But the term TCP, for a network programmer, would refer to the TCP/IP communication protocol and to the telecom profession would mean a Trunk Control Program.

It also helps if the user too could be made aware of Jargon-Clash. Jargon-Clash could lead to destruction of mutual respect between the user and the developer. But surely, everybody knows what pinging is! How can he be so stupid? The situation now would be, as Strother Martin's character wryly observed in the film Cool Hand Luke, "What we've got here is failure to communicate." Every concern, every requirement is not conveyed and the accuracy of the Requirement-Model is affected.

For want of a nail, a kingdom can be lost and for lack of an accurate Requirement-Model, a system will be doomed to fail.

Poison Is The Cure
-------------------------
Pass everything the user says through the filter of Jargon-Clash awareness.

Try to agree upon a common vocabulary and clearly define each term of relevance. Make a glossary or a project-specific dictionary. It is very important to disambiguate, not only for the accurate elicitation of requirements from user, but also for the ease of implementation.

Construct new terms (a new jargon) if needed, if doing so would -
a) make things less ambiguous, and
b) improve the effectiveness of communication.
Alternate styles of capitalization or the merging of words would suffice in many cases to remind the reader/user that we are using the term in a special meaning.

It bugs readers no end to see repeating groups of words. Think "documentation of user-requirements" instead of Requirement-Model or "the problems caused by the common terms in the user's and developer's jargon" instead of Jargon-Clash. Abbreviate effectively, but do not make things too abstruse.

Validate your Requirement-Model with the user if possible, and then, using the mutually-agreed-upon non-ambiguous vocabulary. If properly validated, the Requirement-Model would simplify the onerous task of system-development considerably.

Summary
------------
Ambiguous terms corrupt the Requirement-Model.
Equivocal Requirement Model = A Failed System.
The corruption propagates and gets magnified.
The developed system turns out to be different from what is required.
Users ditch the system. Wasted time, wasted effort.

Beware of Jargon-Clash.
Disambiguate, at any cost.
Even by inventing a new jargon!

- Thomas Jacob

=================================================================

Saturday, March 04, 2006

Advice for language designers

I strongly felt then, as I still do, that there is no one right way of writing every program, and a language designer has no business trying to force programmers to use a particular style. - Stroustrup

dynamic_cast prerequisite

A dynamic cast is performed run-time. A prerequisite for using the dynamic cast operator is the existence of at least one virtual member function in the base class.

Tuesday, February 28, 2006

On The Naming Away - I

The importance of the names that we give variables and functions cannot be overemphasized. I would even go so far as to say that, at times, proper naming is more important than the functionality being coded itself! Sadly, in the mad rush to get things working, this aspect of coding is often neglected. :-(

Remember: code is also for reading!

Sunday, February 19, 2006

Template Friend Functions

code snippet picked up from the net. don't remember where.


#include
using namespace std;
template
class SomeClass{
private:
T member;
public:
...
friend ostream& operator<<(ostream &,SomeClass &);
};

template
ostream& operator<<(ostream &os, const SomeClass&some){
os<<"( " << some.member <<") ";
return os;
}

int main(int argc, char* argv[])
{
SomeClass sc(5);
cout << sc; // Problem????
return 0;
}


It reported that operator << was not found.

The problem is that in the class definition, a friend was declared as a function, not a template function. To make the code link properly, the friend declaration should be changed to:

friend ostream& operator<< <>(ostream &,SomeClass &);


Just another little C++ twist.

Thursday, February 02, 2006

Attack Of The Clones

Presque vu: is a French term which means "almost seen". It is the feeling of very nearly, but not quite, remembering something.

Here are three operating system projects that attempt to recreate or provide the look-and-feel and user-experience of other operating systems. They are driven by fervent nostalgia, but could they, will they, ever be "even better than the real thing?

1. ReactOS - Aims to provide an implementation of a Windows® compatible OS
ReactOS homepage

2. Haiku - Aims at the re-creation of the Be Operating System.
Haiku OS homepage"

3. CoLinux - port of the Linux kernel that allows it to run alongside another operating system on a computer. Run Linux on Windows® 2000/XP.
Cooperative Linux Homepage

Monday, January 23, 2006

Indirection Is Flexibility

Indirection (n)
1. "indirect procedure or action".
2. "deceitful action that is not straightforward"

Indirection in system design is when instead of explicitly specifying how exactly something is done, we delegate the responsibility to another person/entity. We buy belts that are looser than needed, just in case we get fatter, don't we?

Let's say somebody who's new in town asks us what he should do to start a bank account. We could -
a) tell him to go to the bank and meet the manager there.
b) give him the exact list of required documents, the order in which to submit them and to whom.

Take into account the always-changing laws and the idosyncrasies of different banks, and the advantages of the first approach are self-evident! That's indirection for you. We would of course give him the address of the bank and possibly also the title of the official (manager, if he is a life-newbie as well).

Indirection helps us surmount change. In programming, we achieve indirection through the use of pointers and references instead of actual objects. Indirection is closely related to the concept of abstraction.

OO perspective: Instead of specifying the exact type of the implementor, we just mention that it will be of a certain type,say, Base . This is the foundation on which most design patterns are constructed. We can later on replace this reference with a more specialized object (derived from Base), if needed, to provide extra/changed functionality.

Other examples:
The use of virtual machines like .NET's CLR or Java's JVM instead of the actual processor gives us portability as a useful side-effect.
The use of pointers in C/C++ gives us space-efficiency.

There ain't no such thing as a free lunch. Indirection gives us flexibility at the expense of performance. No matter how trivial the cost is, there is a price to be paid. You can't have your cake and eat it too!

Sunday, January 08, 2006

OS Development Resources

A new year's present for the wannabe OS developer

http://www.osdever.net/tutorials.php?cat=0&sort=1

May you not fail for the lack of guidance!