Guidelines for Class Design

Page last modified 08:15, 19 Dec 2008 by Ruud | Page History

3.1 An overview of the date classes in the java library

Java has a package for producing dates. The data class implements a very straightforward abstraction, a point in time, measured in milliseconds. This is done by the date class. 

Special topic 3.1 Total ordering

Total ordering defines how elements can be sorted. The totality condition means that all elements can be compared with each other.   
The date class has a total ordering of dates that can be compared with each other. The calendar class assigns a name to each date-object. For example the GregorianCalendar class inherits from the calendar class, but also IslamicCalendar can be designed. 

3.2 Design a Day Class

A day object encapsulates a particular day. Strong methods are necessary, such as daysFrom returns the number of days between 2 days. addDays computes the number of days from a day. We also want a constructor that can construct a day object, with year, month and day. 
Special topic 3.2 Operator overloading
Operator overloading is when you uses operators instead of methods to do adding, multiplying etc.
For example:     x + y * z               instead of                               x.add(y.multiply(z))

3.3 Three implementations of the day class

The first implementation is inefficient, because of recursively increment or decrement in 1 day at a time.
The second implementation stores a number instead of year, month, day. This number corresponds to the number of days after its epoch.  This is quit inefficient because it takes a long time to compute where the year, day, month
The third implementation keeps track of both the number and day, month, year. The conversion should be lazy, which means it only converts when it is really necessary.
The source code of these implementations can be found in the OOM-book, page 100-109.

3.4 The importance of Encapsulation

Encapsulation is the grouping together of data and functionality. Data encapsulation provides a mechanism for restricting the range of the program that is affected by a change to a small subset, namely the methods of a class. Once that subset has been updated to track a change, the programmer can state with confidence that no other portion of the program needs attention in this regard.
Teachers definition: Encapsulation is allowing acces to the internal fields of an object only through object methods.

3.4.1 Accessors and Mutators

Mutator methods are there to change an object(setsomething()). Accessor methods read its instance fields(getsomething()). To make sure no fields  can be changed when you don’t want it you can return the cloned object instead of the object itself. This way you have acces to it, but it can’t be changed.

3.4.2 Final Instance Fields

You can mark an instance field as final to indicate that it doesn’t change once it has been constructed. But you have to watch out, only the field is protected and not the object.

3.4.3 Separation of Accessors and Mutators

Methods to return an object should be kept separate from methods that change the state of an object, they should be of type void.  Sometimes it can be convenient for a user to change the state of an object en return it in one method.

3.4.4 Side effects

A side effect is data modification when you call the method. Optimal would be if the method didn’t change the object at all, but in objected-oriented programming it is accepted that mutator methods have a side effect, namely the mutation of the implicit parameter. A method can also modify the explicit parameters and the accessible static fields. 

3.4.5 The law of Demeter

The law of Demeter state that a method should only use:
-          Instance fields
-          Parameters
-          Objects that it constructs new

Methods should not use other objects to work with. However, you should not take this law like a mathematical law, but you should consider it when you are programming.

3.5 Analyzing the Quality of an Interface

Desgin and implementation of classes can be approached from two points of view. The programmer, who likes convenient coding and fast algorithms, and the user, who has to understand what it does to use it. 

3.5.1 Cohesion

A class is an abstraction of a single concept. All class operations logically fit together to support  single, coherent purpose. Cohesion is a measure of how strongly-related and focused the various responsibilities of a software module are. Cohesion is an ordinal type of measurement and is usually expressed as "high cohesion" or "low cohesion" when being discussed.

3.5.2 Completeness

A class interface should be complete, that means it should support all operations that are a part of the abstraction that the class represents. 

3.5.3 Convenience

An interface might be complete, but it should be convenient too. 

3.5.4 Clarity

The interface of a class should not be confusing. Lack of clarity can come from unnecessary call protocols. Whenever a method explanation is difficult, you should think about whether that is really necessary.

3.5.5 Consistency

Operations should be consistent with respect to names, parameters and return values, and behavior. That means it all should work the same.

3.6 Programming by contract

When you ensure that all constructors of a class create only objects with valid state and that all mutator operations preserve the valid state, then we never have invalid objects. No operation should waste time checking for invalid methods. This is call programming by contract.

3.6.1 Preconditions

A precondition is a condition that must be true before the service provider promises to do its part of the bargain. If the precondition is not true and the service is still requested, the provider can choose any action that is convenient for it. A postcondition is a condition that the service provider guarantees upon completion. 
An important issue of a precondition is that it needs to be checkable, so you can’t use any private field of another class.

3.6.2 Assertions

When a precondition is violated you can do assertions. The command assert checks whether a condition is true. If it is true, the execution continues, else a AssertionError is thrown. Also an explanation can be supplied.
                                                                              Assert condition : explanation;

3.6.3 Exceptions in the Contract

Unlike the assertions, exceptions can be handy too. In depends on the situation to use a exception or assertion.

3.6.4 Postconditions

A postcondition is any condition that a method promises to fulfill after it is called. For these you should not use exceptions, but only assertions.

3.6.5 Class invariants

A class invariant is a logical condition that holds for all objects of a class, except possibly those that are undergoing mutation. In  other words a condition must be true before and after the method, but it hasn’t to be true inside the method. 
To prove an invariant you need to check that:
1.       It is true after every constructor has completed execution.
2.       It is preserved by every mutator

3.7 Unit testing

Unit test is the test of a single class. Design test cases during implementation and run tests after every implementation change. When you find a bug, add a test case that catches it.

 

Tag page
Pages that link here
Page statistics
632 view(s), 9 edit(s), and 8026 character(s)

Comments

You must login to post a comment.

Attach file

Attachments