CSC262 Feb01

slide version

single file version

Contents

  1. How to Write Programs
  2. Problems
  3. System Development Steps
  4. Analysis
  5. Design: Classes
  6. Implementation and Testing
  7. Deployment
  8. Design: Class Relationships - class members
  9. Design: Class Relationships - local or parameter class declarations
  10. Design: Class Relationships - Inheritance
  11. Design Details
  12. Design: How to Solve a Problem
  13. Polya's Principles
  14. Classes and Problem Descriptions
  15. Classes, Responsibilities, Collaborations (CRC)
  16. Discovering Classes
  17. Relationships
  18. Design Output
  19. Example: Invoice Generation
  20. Example: Discover Classes
  21. Responsibilities
  22. Invoice Responsibilities
  23. LineItem Responsibilities
  24. Invoice (revised) Responsibilities
  25. Product and Address Responsibilities
  26. Class Relationships
  27. Invoice Class Relationships
  28. LineItem Class Relationships
  29. Design Output
  30. Invoice.h Design Output
  31. LineItem.h Design Output
  32. Implementation
  33. InvoiceTest
  34. Sample Output
  35. How to ... : 1
  36. How to ...: 2
  37. How to ...: 3
  38. How to ...: 4

How to Write Programs[1] [top]

Good question! How?

Software engineering has been concerned with this question for a long time and a number of different processes have been proposed and used to try to meet the problems associated with developing software to be used some set of clients (that is, not just by the developer herself or himself).

Problems[2] [top]

What problems arise?

Software engineering processes (if they are any good) should help to direct larger system development to help solve these and other such problems, independently of the actual algorithms and data needed in any specific system.

System Development Steps[3] [top]

These steps are generally needed in any moderate to large scale system development and can be a useful guide even in small systems consisting of a single program.

Analysis[4] [top]

In this phase it is important for the developer and the client to agree on the requirements of the software system to be built.

This can be quite difficult as the client may not really know what is possible and what isn't or what he really needs.

The output of the analysis phase should be a detailed requirements document.

Design: Classes[5] [top]

This is the phase that the developer discovers the structures needed to develop a system to meet the requirements from the analysis phase.

The design phase should not commit to early to writing code. However, in the case of an object oriented system, the output of the design phase should be a set of classes with the member functions declared and comments describing what they should do. These classes with their operations (member functions) should be sufficient to develop a system that meets the requirements.

The implementation of the functions is not specified and data members of classes are not required yet. So it is important that the comment for each member function clearly states what the function must do (but not how).

Member function stubs can be written that include the parameters and return type. However, the bodies will be left blank or return a default value in place of the actual implementation that comes in a later phase.

Implementation and Testing[6] [top]

In this phase, you finally begin actual coding. But the design phase should have provided sufficient organization so that the parts will fit together as long as each class member function is implemented to fulfill its responsibility as specified in the comment generated in the design phase.

Each class should be "unit" tested. That is, test programs should be written to make sure each member function does what it should. If class A depends on class B, it is a good idea to make sure class B is unit tested first, then test class A. Otherwise, class A may fail tests simply because class B is not implemented correctly.

Deployment[7] [top]

If the software is going to be delivered to mulitple clients or if a client uses mulitple hardware platforms, some thought must be given to how the software will be delivered and installed on the client's machine(s). A number of long standing practices and tools have been developed for this purpose, but it is still quite challenging. Does a client exclusively use Windows and Microsoft environment? Linux?

Design: Class Relationships - class members[8] [top]

In addition, relationships between the set of classes should be made clear in the design phase. That is, some classes may need to use some of the other classes.

One class can use another class in several different ways:

In these examples the relationship is that one class uses another class by having a member of that class type.

This is called the has-a relation ship. One class has a member whose type is another class.

A PointSet has Points. The PointSet class will have a member (an array) to hold a collections of Points.

A Book has Chapters and an Index. A Book class might similarly, have a member that is an array of Chapter elements and another member of type Index.

The term aggregation is also used. For example, the class Book aggregates or has members of type Preface, Chapter, Appendix, and Index.

Design: Class Relationships - local or parameter class declarations[9] [top]

The Point class has a member function toString()

        
	string Point::toString() 
        {
          string result;
          ....

          return string;
        }
      

This function returns a string such as "(3,4)" representation of the Point value with member x value 3 and member y value 4.

So the Point class uses the string class, but the Point class does not have a string data member.

The relationship between Point and string is not aggregation. It is a weaker relationship, but the Point class does depend on the string class.

This dependency relationship will simply be referred to as "depends-on" or "uses".

Design: Class Relationships - Inheritance[10] [top]

One class can be an extension of another class. It has all the properties and member functions of the other class, but may add a few more. This class relationship is called inheritance and will be covered in later lectures.

For now the question is given a problem or system to build, how do we design a solution. In particular, what can guide us in choosing what classes should be created and used to solve the problem.

Design Details[11] [top]

Only the design details will be discussed in detail.

So we will consider requirements for a system will be given and the problem is to decide on a set of classes that will be needed to build such a system.

Is there some guide how to use the requirements to discover what classes might be appropriate?

Caution: Initially, when building classes, it may frequently occur that you realize you need another member function in the class. After some practice in building classes, there may be a tendency to compensate for this by including a number of member functions that you really don't need for this system. It isn't clear when to stop adding member functions for possible future systems. Remember the client who immediately says "Great! But can you add this other feature." (A feature that wasn't in the requirements.) Adding member functions that seem match the class type, indepently of whether they are actually needed for this system, can make the class easy to use for new features. The cautionary point is not to let this future planning delay the development for the current system. At some point you have to actually deliver the system to the client.

Design: How to Solve a Problem[12] [top]

The design phase should produce a set of classes that can be used to solve the problem posed by the requirements.

Who is this man and can he help?

(1887 - 1985)

Hint: You can't ask him to solve it.

Polya's Principles[13] [top]

  1. Understand the Problem
    • What are you asked to show?
    • Can you restate the problem in your own words?
    • Can you think of a picture or diagram that might help you understand the problem?

  2. Devise a Plan
    • Look for a pattern?
    • Draw a picture
    • Solve a simpler problem
    • Consider special cases
    • Have you seen the problem before or the same problem in a slightly different form?
    • Do you know how to solve a related problem? Could that help solve this problem?
    • Be ingenious!

  3. Carry out the Plan
    This is should be easy if the first two steps are done well.

Fine! But how does this help find appropriate structures to use if you haven't already solved the same or a similar problem or don't feel particularly ingenious at the moment?

You can try the following technique for discovering the set of classes you may need.

The technique is called CRC and consists of an approach to discovering classes and member functions to solve a problem you should already understand.

Classes and Problem Descriptions[14] [top]

Classes describe objects.

Function members describe actions or queries about properties of objects: Change your size! What is your size?

So a simple guide is classes will likely correspond to names of objects (nouns) in a problem description.

Function members of classes will correspond to actions or queries (verbs) in the problem description.

Of course, not all nouns will correspond to classes. Data members of classes will likely correspond to named properties of objects.

Classes, Responsibilities, Collaborations (CRC)[15] [top]

The technique for finding both classes and member functions tries to identify one class at a time

Discovering Classes[16] [top]

From a problem description (or the problem described again in your own words), make a list of candidate classes from the named objects (the nouns) in the description.

Decide which nouns are likely to be classes and which other nouns might be properties (data members) of classes.

Select one of the candidate classes and make a list of what responsibilities it might have.

For each responsibility think about whether it would need to use another class to accomplish that responsibility and if so indicate the other class as a collaborator of this class.

If you find a collaborator, make it be a separate candidate class and list its responsibilities

A class may or may not have collaborators.

Clearly this is an iterative process.

Relationships[17] [top]

If in the CRC process a class has a collaborator, it uses or depends on the collaborator class.

In this case identify what kind of relationship this is. That is, is it aggregation or simply use as a local variable or parameter of a member function.

Design Output[18] [top]

The output of this approach to the design should be a set of documented classes that includes the member functions and for each member function a comment describing what it should do.

The design output does not need to describe data members or the implementation of the function members of the classes.

UML diagrams could be provided or source code with comments for each member function.

Relationships between classes could be specified by simple UML diagrams.

Example: Invoice Generation[19] [top]

The problem is to provide a program that can print an Invoice such as the one below. Each invoice will contain the address of the customer. Each Line Item will print the product description, the quantity ordered, the price/item, and the total charge for that quantity of items. The total amount due for all line items on the invoice should also be printed.

                         Invoice

EZ Rental
8384 Dempsey
Brooklyn, NY 01234

Description            Qty             Price     Total
------------------------------------------------------
Power Cleaner           1              $49.95    49.95
PX Cleaner Fluid        8              $ 9.00    72.00 
M803 Vac Bags	       10              $ 2.08    20.80

Amount Due: $142.75

      

Example: Discover Classes[20] [top]

The nouns:

Description and Price are properties (data members) of the Product class.

Total and AmountDue are computed values rather than classes or data members.

This leads to candidate classes:

Each of these represents a useful concept for the problem. Make each one a class.

Responsibilities[21] [top]

The problem is to print an Invoice. Printing with cout means Invoice would use the ostream class. However, for more flexibility about where the printing takes place, make each class(es) responsible for providing a formatted string that could be printed to the terminal, to a file, etc.

Invoice Responsibilities[22] [top]

Invoice
responsibility collaborator(s)
format the invoice  

For handling each responsibility, will this class need to use another class?

The invoice format includes an address. So the Invoice class may need to use the Address class.

Invoice
responsibility collaborator(s)
format the invoice Address

Similarly, the format invoice responsibility will need to use the LineItem class - the Total for a Line Item will be the responsibility of the LineItem class. The Amount Due will be computed.

Invoice
responsibility collaborator(s)
format the invoice Address
  LineItem

LineItem Responsibilities[23] [top]

The LineItem class needs to format the item.

To format each item the total for that line item is needed. So the LineItem has that responsibility, too.

LineItem
responsibility collaborator(s)
format the line item ?
get line total  

How does the LineItem class get the line total?

It depends on the properties of a LineItem object. Quantity is not a property of a Product; it is a property of a LineItem.

That is, a LineItem consists of a Product and a Quantity.

What about price? The Price is a property of a Product.

So the LineItem will need to use the Product class to get the Product price:

LineItem
responsibility collaborator(s)
format the line item Product
get line total  

The get line total will need the quantity (as a parameter).

Invoice (revised) Responsibilities [24] [top]

Invoice
responsibility collaborator(s)
format the invoice Address
  LineItem
  Product

Product and Address Responsibilities[25] [top]

These classes must do all the work for formatting themselves. No collaborators:

The Product class responsibilities:

Product
responsibility collaborator(s)
format Product  

The Address class responsibilities:

Address
responsibility collaborator(s)
format Address  

Class Relationships[26] [top]

Each collaboration represents a class relationship.

But is each relationship class member (aggregation) or as a parameter or local variable (dependent use)?

Invoice Class Relationships[27] [top]

Invoice
responsibility collaborator(s)
format the invoice Address (class member)
  LineItem (Not a class member; but an array of these as member)
  Product (Not a class member; each LineItem has a Product)

So the Invoice class

LineItem Class Relationships[28] [top]

LineItem
responsibility collaborator(s)
format the line item Product (class member)
get line total  

So the LineItem class

Design Output[29] [top]

The result of this CRC design approach should be a set of classes with documented member functions.

For example, the initial Invoice.h and LineItem.h files will show the member functions that correspond to the responsibilities in the CRC design with comments!

Invoice.h Design Output[30] [top]


class Invoice
{
public:
  /**
   * Initialize an Invoice for up to maxItems LineItems
   */
  Invoice();
  Invoice(int maxItems);
  Invoice(const Address& addr, int maxItems);

  /**
   * Formats the invoice
   */
  string format();

  /**
   * Adds a line item for a product and quantity to
   * this invoice.
   */
  void add(const Product& p, int quantity);

};

LineItem.h Design Output[31] [top]


class LineItem
{
public:
  /**
   * Initialize LineItem
   */
  LineItem();
  LineItem(const Product& p, int quantity);

  /**
   * format the line item
   */
  string format();

  /**
   * get line item total amount
   */
  double getLineTotal();

};

Implementation[32] [top]

To implement the design:

In addition, we must provide tests for the class(es).

InvoiceTest[33] [top]

Here is a sample invoice test program:


int main()
{
  Address ezAddr("EZ Rental",
           "8384 Dempsey",
           "Brooklyn",
           "NY",
           "01234");

  Invoice ezInv(ezAddr, 10);

  ezInv.add(Product("Power Cleaner", 49.95), 1);
  ezInv.add(Product("PX Cleaner Fluid", 9.00), 8);
  ezInv.add(Product("M803 Vac Bags", 2.08), 10);

  cout << ezInv.format() << endl;
  
  return 0;
}

The responsibilities for formatting and printing the invoice are distributed over the classes as a result of the design phase. No one class has to do everything and so each class can be easier to understand and implement.

In general, this CRC technique can provide an effective way of modularizing a program using object oriented features. Classes are designed and Responsibilities are assigned to the chosen class objects with the goal of building a system according to specified requirements.

Sample Output[34] [top]

This test program should produce the invoice:

                         Invoice

EZ Rental
8384 Dempsey
Brooklyn, NY 01234

Description            Qty             Price     Total
------------------------------------------------------
Power Cleaner           1              $49.95    49.95
PX Cleaner Fluid        8              $ 9.00    72.00 
M803 Vac Bags	       10              $ 2.08    20.80

Amount Due: $142.75

      

How to ... : 1[35] [top]

How can you print the headings:

	Description       Qty       Price       Total
      

so that

  1. include <iomanip>
  2. Use cout << setw(25) to cause the next item output be in a field of 25 character positions
  3. cout.setf(ios::left, ios::adjustfield) causes an item that needs fewer character positions than the field to be placed in the left most positions (and padded with the fill character, i.e., blanks)
  4. cout.setf(ios::right, ios::adjustfield) causes an item that needs fewer character positions than the field to be placed in the right most positions (and padded with the fill character, i.e., blanks)

cout.setf(ios::left, ios::adjustfield);
cout << setw(15) << "Description";
cout.setf(ios::right, ios::adjustfield);
cout << setw(10) << "Qty"
     << setw(10) << "Price"
     << setw(10) << "Total" << endl;

How to ...: 2[36] [top]

How can you create a format string instead of printing the previous heading?

#include <sstream>

string format()
{
  stringstream ss;

  ss.setf(ios::left, ios::adjustfield);
  ss << setw(15) << "Description";
  ss.setf(ios::right, ios::adjustfield);
  ss << setw(10) << "Qty"
       << setw(10) << "Price"
       << setw(10) << "Total" << endl;

  return ss.str();

}
      

How to ...: 3[37] [top]

How can you print a line of 55 "-" without typing all 55 characters?

When an output value needs fewer character positions than the output field, the value is padding with the 'fill' character either on the left or on the right depending on the justification desired.

The fill character, by default, is a blank.

But you can change the fill character. For example, you can make the fill character be '-'.

Then to print 55 '-' characters:


cout.fill('-');
cout << setw(55) << "" << endl;
cout.fill(' ');



	
      

How to ...: 4[38] [top]

How do you print floats and doubles in fixed decimal format (not scientific notation) and with 2 decimal places?


cout.setf(ios::fixed);
cout.precision(2);