12.6. The Color Class

 
[Page 387 ( continued )]

11.6. Class Design Guidelines

You have learned how to design classes from the preceding two examples and from many other examples in the preceding chapters. Here are some guidelines.

11.6.1. Cohesion

A class should describe a single entity, and all the class operations should logically fit together to support a coherent purpose . You can use a class for students, for example, but you should not combine students and staff in the same class, because students and staff have different operations.

A single entity with too many responsibilities can be broken into several classes to separate responsibilities. The classes String , StringBuffer , and StringBuilder all deal with strings, for example, but have different responsibilities. The String class deals with immutable strings, the StringBuilder class is for creating mutable strings, and the StringBuffer class is similar to StringBuilder except that StringBuffer contains synchronized methods for updating strings.

The Date , Calendar , and GregorianCalendar classes all deal with date and time, but have different responsibilities. The Date class represents a specific time. Calendar is an abstract class for extracting detailed calendar information from a specific time. GregorianCalendar implements a concrete calendar system.

11.6.2. Consistency

Follow standard Java programming style and naming conventions. Choose informative names for classes, data fields, and methods. A popular style is to place the data declaration before the constructor, and place constructors before methods.

Choose names consistently. It is not a good practice to choose different names for similar operations. For example, the length() method returns the size of a String , a StringBuilder , and a StringBuffer . But the size() method is used to return the size of a Collection and a Map . It is better to use the same name for consistency.


[Page 388]

In general, you should consistently provide a public no-arg constructor for constructing a default instance. If a class does not support a no-arg constructor, document the reason. A public default no-arg constructor is assumed if no constructors are defined explicitly. A constructor invokes its superclass no-arg constructor by default if a constructor does not invoke an overloaded constructor or its superclass's constructor.

If you want to prevent users from creating an object for a class, you may declare a private constructor in the class, as is the case for the Math class. The constructors in abstract classes should always be declared protected .

11.6.3. Encapsulation

A class should use the private modifier to hide its data from direct access by clients . This makes the class easy to maintain.

Provide a get method only if you want the field to be readable, and provide a set method only if you want the field to be updateable. For example, the Rational class provides get methods for numerator and denominator, but no set methods, because Rational is an immutable class.

A class should also hide methods not intended for client use. The gcd method in the Rational class in the preceding section is private, for example, because it is only for internal use within the class.

A class can present two contracts: one for the users of the class, and one for the extenders of the class. Make the fields private and the accessor and mutator methods public if they are intended for the users of the class. Make the fields or methods protected if they are intended for extenders of the class. The contract for extenders encompasses the contract for users. The extended class may increase the visibility of an instance method from protected to public , or may change its implementation, but you should never change the implementation in a way that violates the contract.

11.6.4. Clarity

Cohesion, consistency, and encapsulation are good guidelines for achieving design clarity. Additionally, a class should have a clear contract that is easy to explain and easy to understand.

Users can incorporate classes in many different combinations, orders, and environments. Therefore, you should design a class that imposes no restrictions on what or when the user can do with it, design the properties in a way that lets the user set them in any order and with any combination of values, and design methods that function independently of their order of occurrence. For example, the Loan class contains the properties loanAmount , numberOfYears , and annualInterestRate . The values of these properties can be set in any order.

Methods should be defined intuitively without generating confusion. For example, the substring(int beginIndex, int endIndex) method in the String class is somehow confusing. The method returns a substring from beginIndex to endIndex - 1 , rather than endIndex .

You should not declare a data field that can be derived from other data fields. For example, the following Person class has two data fields: birthDate and age . Since age can be derived from birthDate , age should not be declared as a data field.

   public class   Person {   private   java.util.Date birthDate;    int   age;  ... } 


[Page 389]

11.6.5. Completeness

Classes are designed for use by many different customers. In order to be useful in a wide range of applications, a class should provide a variety of ways for customization through properties and methods. For example, the String class contains more than fifty methods that are useful for a variety of applications. The Calendar class defines many time fields as constants, such as YEAR , MONTH , DATE , HOUR , HOUR_OF_DAY , MINUTE , SECOND , and DAY_OF_WEEK , and provides many methods for extracting date and time, and for setting a new date and time in the calendar.

11.6.6. Instance vs. Static

A variable or method that is dependent on a specific instance of the class should be an instance variable or method. A variable that is shared by all the instances of a class should be declared static . For example, the variable numberOfObjects in Circle2 in Listing 7.3, Circle2.java, is shared by all the objects of the Circle2 class, and therefore is declared static. A method that is not dependent on a specific instance should be declared as a static method. For instance, the getNumberOfObjects method in Circle2 and the gcd method in Rational are not tied to any specific instance, and therefore are declared as static methods.

Always reference static variables and methods from a class name (rather than a reference variable) to improve readability and avoid errors.

Do not pass a parameter from a constructor to initialize a static data field. It is better to use a set method to change the static data field. The following class in (a) is better replaced by (b).

Instance and static are integral parts of object-oriented programming. A data field or method is either instance or static. Do not mistakenly overlook static data fields or methods. It is a common design error to declare an instance method that should have been declared static. For example, the factorial(int n) method for computing the factorial of n should be declared static, because it is independent of any specific instance.

A constructor is always instance, because it is used to create a specific instance. A static variable or method can be invoked from an instance method, but an instance variable or method cannot be invoked from a static method.

11.6.7. Inheritance vs. Aggregation

The difference between inheritance and aggregation is the difference between an is-a relationship and a has-a relationship. For example, an apple is a fruit; thus, you would use inheritance to model the relationship between the classes Apple and Fruit . A person has a name; thus you would use aggregation to model the relationship between the classes Person and Name .


[Page 390]

11.6.8. Interfaces vs. Abstract Classes

Both interfaces and abstract classes can be used to generalize common features. How do you decide whether to use an interface or a class? In general, a strong is-a relationship that clearly describes a parent “child relationship should be modeled using classes. For example, since an orange is a fruit, their relationship should be modeled using class inheritance. A weak is-a relationship , also known as an is- kind-of relationship , indicates that an object possesses a certain property. A weak is-a relationship can be modeled using interfaces. For example, all strings are comparable, so the String class implements the Comparable interface. A circle or a rectangle is a geometric object, so Circle can be designed as a subclass of GeometricObject . Circles are different and comparable based on their radii, so Circle can implement the Comparable interface.

Interfaces are more flexible than abstract classes, because a subclass can extend only one superclass but can implement any number of interfaces. However, interfaces cannot contain concrete methods. The virtues of interfaces and abstract classes can be combined by creating an interface with an abstract class that implements it. Then you can use the interface or the abstract class, whichever is convenient . For this reason, such classes are known as convenience classes . For example, in the Java Collections Framework, which is introduced in Chapter 22, "Java Collections Framework," the AbstractCollection class is a convenience class for the Collection interface, and the AbstractSet class is a convenience class for the Set interface.

 


Introduction to Java Programming-Comprehensive Version
Introduction to Java Programming-Comprehensive Version (6th Edition)
ISBN: B000ONFLUM
EAN: N/A
Year: 2004
Pages: 503

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