Class Design Guidelines
Tags: java, programming, learning, beginners
Published: 2024-06-17 20:43:16

Class design guidelines are helpful for designing sound classes. This section summarizes some of the guidelines.

## 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 are different entities.

A single entity with many responsibilities can be broken into several classes to separate the responsibilities. The classes **String**, **StringBuilder**, and **StringBuffer** 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.

## 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.

Make the names consistent. 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**. It would be inconsistent if different names were used for this method in these classes.

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. If no constructors are defined explicitly, a public default no-arg constructor with an empty body is assumed.

If you want to prevent users from creating an object for a class, you can declare a private constructor in the class, as is the case for the **Math** class.

## 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 getter method only if you want the data field to be readable, and provide a setter method only if you want the data field to be updateable. For example, the **Rational** class provides a getter method for **numerator** and **denominator**, but no setter method, because a **Rational** object is immutable.

## 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 how or when the user can use 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 causing confusion. For example, the **substring(int beginIndex, int endIndex)** method in the **String** class is somewhat confusing. The method returns a substring from **beginIndex** to **endIndex – 1**, rather than to **endIndex**. It would be more intuitive to return a substring from **beginIndex** to **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;
private int age;
 ...
}`

## 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 40 methods that are useful for a variety of applications.

## Instance vs. Static

A variable or method that is dependent on a specific instance of the class must 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 **CircleWithPrivateDataFields** in [here](https://dev.to/paulike/data-field-encapsulation-4i7b) is shared by all the objects of the **CircleWithPrivateDataFields** class and therefore is declared static. A method that is not dependent on a specific instance should be defined as a static method. For instance, the **getNumberOfObjects()** method in **CircleWithPrivateDataFields** is not tied to any specific instance and therefore is defined as a static method.

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 setter method to change the static data field. Thus, the following class in (a) is better replaced by (b).

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jz2ya446dg79ey3g80qq.png)

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 define an instance method that should have been static. For example, the **factorial(int n)** method for computing the factorial of **n** should be defined 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.

## Inheritance vs. Aggregation

The difference between inheritance and aggregation is the difference between an is-a 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**.

##  Interfaces vs. Abstract Classes

Both interfaces and abstract classes can be used to specify common behavior for objects. 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.