< Zurück | Inhalt | Weiter >

Does this attribute or method belong here?

If you find yourself specifying nearly identical methods in more than one class, this should make you ask if the classes should have a common base class from which they should inherit, or if there should be a new unrelated class that they all share by composition.

If the functionality is the same for a set of classes, and the classes are specific instances of a more general type, the method should be on the general class. For example, a changeName() method should probably be on Person, not on Employee or Customer, because the functionality is the same for all three classes. By contrast, a changeEmployeeNumber() method should be only on Employee. It should not be on Person, because not all Persons are Employees. There may also be methods that are com- mon to both Employee and Customer types, but are radically different in implementation. For example, a changePassword() method might change a password in a system-wide LDAP server for an Employee, but might just change a record in a Web site database for a Customer. This is easily done by writing separate methods in each class.

But should you add a changePassword() method on Person? If you want to be able to call the method when treating either a Customer or an Employee as a Person, then you should. But you don’t have to implement the method on Person. You can declare Person.changePassword as ab- stract, and then, if you call the method on a Person, it will call the correct method based on what type of Person (Employee or Customer) the Person is. Note that if a class contains any abstract methods, the class itself must be declared abstract and it cannot then be instantiated. Also note that this is often best accomplished not through abstract classes, but through interfaces (see Eckel, pp. 321–322).

These are by no means the only considerations that come to bear on what classes to create and how to arrange and implement them, but they do represent a good start. They are a foundation on which you can build best practices out of your own experience and environment.

Whole books have been written on the topics of object-oriented analysis and object-oriented design. CRC cards are only one part of an array of tech- niques that can be applied to OOA/OOD. The Unified Modeling Language (UML) is popular in many MIS circles. UML consists of a variety of different diagrams which are used to model parts of an object-oriented design. They are:


image

Am I Mature? Or Are You My Mommy?

Let us point you at one more business buzzword link. Even though we think this particular site and their work are being ill-applied by many well-intentioned IT managers, there is still a great deal of value in the Carnegie Mellon Capability Maturity Model (http://www.sei. cmu.edu/cmm/). At the very least it provides an objective way to assess the level of process sophistication you have in your organization.

The CMM defines five levels of maturity:

1. Initial

2. Repeatable

3. Defined

4. Managed

5. Optimizing

If we may grossly oversimplify (and why should we stop now?), “Initial” means you do things differently every time. You just make your best guess about what the right thing to do is, and you do it. “Repeatable” means that you have hit upon a method that appears to work, and you use it consistently. “Defined” means that somebody has written it down. “Managed” means that the process is actively maintained and supervised in an effort to adapt it to changing circumstances. “Optimizing” means that measurements (“metrics”) are made that objectively assess the pro- cess, and ensure that continuous improvement takes place and can be so proven.*

What we have shown you in this chapter probably falls in the Repeat- able category, a long way from the engineering and management nirvana of Optimizing.


* The problem that seems to come up with this system is that very bad processes may be very mature and very good processes may be relatively immature. Obviously, however, an Optimizing process must be steadily moving towards the good.

image

12.7 Analysis Paralysis 287

• Class Diagram

• Sequence Diagram

• Collaboration Diagram

• Use Case Diagram

• Activity Diagram

• Component Diagram

• Deployment Diagram

Using the simple but effective technique of CRC cards can be a good place to start, but you may soon want to move up the OOA/OOD ladder to use tools like Umbrello6 to make UML diagrams, and perhaps to use the whole UML toolset.7 Many organizations that we know of will pick and choose various techniques and tools. No matter how far down the road of formal software en- gineering you go, you must at least make some effort to have a repeatable process that incorporates continuous improvement.


12.7 ANALYSIS PARALYSIS


The catchy phrase “analysis paralysis” has become a cliché. (And how could it not, being so catchy?) What it refers to, of course, is the tendency to become bogged down in details; or the tendency to refuse to start implementation until you are certain that your design is “right.”

This is where using a “spiral” development model can pay off. By doing frequent small releases, you can expose subtle design flaws at an earlier stage in development. Often, you can (to trot out another trendy term) “refactor” a small part of your design or implementation. If you have clean object interfaces, this can often be done with minimal disruption because a good object model hides implementation details within classes.

In most cases it is best, once you have the use cases and requirements, to proceed to a prototype object model and learn by doing.


image

6. http://uml.sourceforge.net/index.php

7. http://www.uml.org/


12.8 REAL SOFTWARE ENGINEERING


Let’s take a moment here and ask a fundamental question. Is this the best way to make software? And there is another fundamental, but subtly and important- ly different question: Is this the right way to make software?

There are techniques and methods of Software Engineering that do ap- proach the ideal of “zero defects.” NASA uses such procedures for manned spacecraft. Coders for medical devices do likewise. The outline method we have suggested here doesn’t come close to such methods. So, is what we have de- scribed the best way to make software? No, it is not. So why don’t we all use those zero defect methods? That is easy to answer: cost. It is expensive. Virtually no MIS shop on the planet would be willing to pay the price it takes to get that stability and certainty. The price isn’t just dollar cost, either. The Space Shuttle, for example, has computers that still use magnetic core memory, a technology that was old in the 1970s. Why? Because the restrictions imposed by their change control systems would essentially require the entire shuttle to be redesigned and retested if they made such a change.8

But this isn’t an either-or. You do not have to apply either a full-fledged software engineering methodology, or use nothing at all. Instead, you have to apply some design, development, and maintenance processes that improve the probability of success and reduce the cost of failure. When we recommend version control, requirements gathering, use cases, and CRC cards, we are giv- ing you a bare-bones set of methods that will help to write fairly successful software at reasonable cost in reasonable amounts of time.

To some of you, this will be old news. If you are at level 2 or above on the Capability Maturity Model (see the sidebar in Section 12.6), then you already have some process. But you would be surprised how many business out there do not even have source code control in place. To some of you, what we suggest here will be primitive compared to processes you already have. The point is, no one’s level of control and process is “right” (to us, that means “cost-justified”) for all cases. But using no method at all is a risk too great for any business.


image

8. An exaggeration to be sure, though maybe not as much as you might think, but you get our point.


12.9 CORE CLASSES


So, let’s meet our core Java classes. Here they are, in all their glory (Exam- ples 12.1, 12.2).


12.10 REVIEW


We have discussed a simple approach to object-oriented analysis and design through the use of CRC cards. The ideal outcome is a design with the smallest possible number of classes that model real-world objects while meeting all the requirements.


12.11 WHAT YOU STILL DONT KNOW


We could list the names of a number of formal software engineering method- ologies, but we won’t bother. If this chapter has served as your only introduc- tion to object-oriented analysis and software engineering, let’s just say you have a lot of reading to do. But beyond that, there is something you need that is much more subtle and difficult to pin down: experience. The only way to get good at analysis and design is to do it. It helps to do it in conjunction with ex- perienced people, because they can save you time and pain in acquiring your experience. This chapter is the simplest of foundations. The books give you knowledge. Experience gives you wisdom.


12.12 RESOURCES


Kent Beck and Ward Cunningham, “A Laboratory for Teaching Object- Oriented Thinking”, in OOPSLA’89 Conference Proceedings, New Orleans, Louisiana, October 1–6, 1989. The special issue of SIGPLAN Notices 24, no. 10 (October 1989) is also available online at http://c2.com/doc/oopsla89/ paper.php#cards.

More on the Capability Maturity Model can be found at

http://www.sei.cmu.edu/cmm/.

Information on the Unified Modeling Language can be found at

http://www.uml.org/.


image

Example 12.1 The Account class

package net.multitool.core;


import net.multitool.util.*; import java.util.*;

import java.sql.*;


public class Account

{

private String name; // A name to identify this account

private User owner; // The user assigned to this account

private SAMoney total; // Total amt originally allocated to

// this account

private SAMoney balance; // amt remaining unallocated to any

// subaccounts

private Account parent; // The account which contains this

// account as a child

private HashMap children; // The collection of subaccounts,

// by name private static Connection dbConn = null; // JDBC connection

private ArrayList payments; // TODO: unimplemented

private SAMoney unspent; // TODO: unimplemented


/**

* Create an account, with a pool of dollars to budget.

* Use this constructor to create the master account.

* Use createSub to create children of this account.

*/ public

Account(String name, User owner, String total) throws NumberFormatException

{

this.name = name; this.owner = owner;

this.total = new SAMoney(Double.valueOf(total).doubleValue()); this.balance = new SAMoney(Double.valueOf(total).doubleValue());

// N.B. must not be the same object

this.parent = null; this.children = new HashMap();

}


// Static that connects to the DB and either returns the top account,

// or creates it for us.

public static Account getTopAccount() throws SQLException { Account topAccount = null;


dbConn = DriverManager.getConnection("jdbc:postgresql:budgetPro?user=mschwarz");


if (dbConn != null) {

// We have a database connection.

} else {

// We don't and we must create a top account.

}


return topAccount;

}


// Simple getter; returns the name. public String

getName() { return name; }


// Simple getter; returns the total pool of money that this account represents. public SAMoney

getTotal() { return total; }


// Simple getter; returns the balance. public SAMoney

getBalance() { return balance; }


// Simple getter; returns the parent account. public Account

getParent() { return parent; }


// Simple getter; returns the owner of this account, as a User object. public User

getOwner() { return owner; }


// Census - how many children. public int

size() { return children.size(); }


/**

* Get to all the children, via an iterator.

*/

public Iterator getAllSubs()

{

return children.values().iterator();

}


/**

* Create a new subaccount (i.e., child)

* given a name and an amount.

* The child is connected to the parent, and

* the parent's balance is reduced by the amount

* allocated to the child.

*/


public Account

createSub(String name, String amt) throws NumberFormatException

{

Account acct = new Account(name, owner, amt);


// Reduce the parent's unallocated funds. balance = balance.subtract(acct.getTotal());


// Connect the accounts to each other. acct.parent = this;

children.put(name, acct); return acct;

} // createSub


/**

* Looks up and returns the account with the given name.

*/

public Account getSub(String name)

{

return (Account) children.get(name);


} // getSub


} // class Accoun


image


The Umbrello UML modeller is an Open Source tool for creating the various UML diagrams. You can find it at http://uml.sourceforge.net/ index.php. We also recommend their online documentation as a good brief introduction to UML and to Umbrello. It can be found from the main Umbrel- lo page, or directly at http://docs.kde.org/en/HEAD/kdesdk/umbrello/.


12.13 EXERCISES


1. Imagine a public library. Carry out the CRC nomination process for a system to track library members and the collection. What list of objects do you come up with? What abstract classes do you find? Which did you discard and why?

2. Extend the purpose of the library program to include generating mailings to members with overdue materials. Did you add classes? Did you add methods and/or members? To which classes did you add them?


image

Example 12.2 The User class

package net.multitool.core;


import net.multitool.util.*; import java.util.*;


public class User

{

private String name;

private Account home; // TODO: implement


public

User(String username)

{

name = username;

}


public String toString()

{

return name;

}


} // class User


image


3. A new requirement is added. The system must allow for books, audio recordings, and movies to be checked out for different lengths of time. Did you add classes? Did you add methods and/or members? To which classes did you add them?