Best Practices for Object Oriented Programming


Why coding standards are important in OOP?


Writing code for others, not for you.
Greater consistency.
Easier to understand.
Easier to maintain.
Reduces the overall cost of the application.
Code for people, not for machine.

GRASP in a Nutshell


Generalized Responsibility Assignment Software Patterns
This pattern outline few best practices that need to be considered before writing coding
The core intention of the practice is 3R (Roles, Responsibility and Reusability)
Information Expert: A class should only have the information necessary to fulfill its Responsibility.
Creator: Responsibility of creation of instance should be defined properly.
Low Coupling: Assign a responsibility so that coupling remains low.
High Cohesion: Assign a responsibility so that cohesion remains high.
Controller: Assign the responsibility for receiving and handling a system event message to a class that is either:
Representative of the entire subsystem (e.g. a Façade Controller).
These classes can be the main interface classes. (MVC)
Polymorphism: When related behaviours vary by type (class), assign the responsibility polymorphically to the specialization classes. In simple words, use Overloading.
Don’t Talk to Strangers: Do not couple two objects that have no obvious need to communicate.
In simple, when you are about to assign a responsibility, ask yourself the following question:

Is this responsibility related to the other responsibilities of this class?

If not, there is likely a need to assign the responsibility to another class. This may prompt you to create a new class if other responsibilities exist that are similar/related to this one.

Standards for a CLASS


A class is any uniquely identified abstraction - that is, a model - of a set of logically related instances that share the same or similar characteristics.

A class is a unique structure that defines the attribute data and the methods or functions that operate on that data.

As per UML Class is  Any uniquely identified abstraction that models a single thing, where the term object is synonymous with instance.Classes have attributes and methods.

(Define a "Class" without using the word "Object")

Following are the key points we need to think about before writing a class.
Class Name – The name of the class should be ‘NOUN’ and it should explain the abstract level purpose and the responsibility of the class.
Ex. HashMap – The name says this class hold a map [x à y] (literal meaning) and it uses some hashing principles for management.
Names like AddEmployee, CreateDatabase etc… should not be used. In such cases write classes like Employee [with add method] and DatabaseManager [with create method]
Packing a class – Since classes are uniquely identified entities, it should be packed in respective packages. Packages should not contain irrelevant classes.
Ex. com.ustr.myapp.dao is a package which contains all Data Access Object. This package should not contain any non-dao classes like ServiceUtil, MyAppException
          Standard: com.<companyid>.[corporate].appname.<sub packages>
Class Scope – Scope explains the visibility of the class. Provide appropriate visibility for each class. Ex. You have a Factory class FileWriterFactory which takes the responsibility of creating the FileWriters, the scope should be defined as shown in below diagram
         

Adding Members and Functions (Action or Messages) to a class


Members: Collection of properties which define a class structure. All the members should have concrete relationship with the class structure.
In simple, the class members should be relevant to the class.
Ex. Class ConnectionManager can have members like List of Connections, connection parameters. Resultset, Statement, List of Employee should not be the part of members. These types of members should be used only within the appropriate methods.
There is a common practice like, whenever a variable is used within more than one method, that variable is declared as class-member. We should not share the members unless it is meant for.
Note:
1.   In case if you need to share the members between methods, create the member in a method and pass it to next method.
2.   Don’t use a variable for multiple purposes. Re-use of variables are not recommended. To avoid this, it is always recommended to provide meaningful names for variables.
Member names should be self explanatory. Sometimes the usage can be identified by its type also. But it is always recommended to use meaningful names.

Ex. connectionList – List of connection
      isConnectionClosed – says whether a connection is closed.

Note: Do not use variables like i, j, flag, status etc. Instead use rowIndex, processStatus, isFileExist etc…
Use the appropriate type for the members. The type is used to identify the data value type stored.
It is recommended (depends on usage) that appropriate Generic type should be used for maintainability and scalability.

          Ex. Use Map colorMap instead of HashMap colorMap
In case if you are sure about the data type and if that never change in future, than it is always better to use appropriate type (non-generic).
Ex. Use Double empSalary instead of Number empSalary.
Use LinkedHashSet empDetails instead of Set empDetails.
[Note: in the second example, we can’t assign any other implementations of ‘Set’. This is not recommended when you use your own collections implementations or frameworks]

When you deal with primitive type, use the possible lowest data type.
Ex: use short age instead of int age. If you are storing 0 or 1 use boolean always.
Scope of a member: The scope refers to the visibility of the member. It is always recommended to provide the low visibility (private) unless demanded. The following table shows the visibility and the purpose.


Visibility      
Purpose
Usage
public
Can be accessed by anyone
Use only for shared constants
private
Can accessed only within the component
Recommended to use always
default
Package level access
protected
Used for inheritance
Only if the member is inherited.


Also in case if you need to give public access to a member, it is recommended to do using a ‘getter/setter’ method.
          private int age;
          public int getAge(){
                   //Can provide security check/condition
                   return age;
          }
          public void setAge(int age){
                   //Can provide security check/condition
                   this.age = age;
          }

Providing public access to members may corrupt the member.
Also note providing both getter and setter for all the members are not recommended. They should be provides only as per the requirement. See the below example.
          public class FileReader {
                   private int numOfFilesRead = 0;
                  
                   public void readFile(File f) {
                             …
                             …
                              numOfFilesRead++;
                   }
                  
                   public int getNumOfFilesRead(){
                             return this.numOfFilesRead;
                   }
          }

Note that the member ‘numOfFilesRead’ is used for internal manipulation. So providing a setter or public access may corrupt the value.
                  

Primitive Types: All the primitive type members can be created in ‘constructors’ itself. It can be done either by assigning default values or parameterized value or both.

          public class Employee {
                   private int empID;
                   private String name;
                   private String project;
                  
                   public Employee (String name) {
                             empID = EmpIDGenerator.createID();
                             this.name = name;
                             project = Project.POOL;
                   }
                   public void setProject(String project){
                             this.project = project;
                   }
          }

The setter method helps in ‘injecting’ new projects for the Employee.

Non-Primitive Types: Before explaining creation of non-primitive types (Object), we need to know what relationships between classes are.

Relationship – Relationship tells how two classes are related.
Following are some of the relationships which need to be applied:
          1. Dependency
          2. Association

Dependency: Is a semantic relationship between two components in which a change to the independent thing may affect the semantics of the dependent things. This is the weakest relationship we can have.

Figure 1 shows the dependency relationship.
Figure 1: Dependency relationship

In the above example HTMLWriter is depended on HTMLHelper just for writing HTML Characters, using getChar() method.
So if there is a change in getChar implementation, then the meaning (semantic) of HTMLWriter.write method will also change, but if there is any change in any other functions in HTMLHelper, it never affect the HTMLWriter Class, which is called ‘Dependency’ relationship. In simple only the functionality will change and not the structure of the Caller class.

Association: Is a structural relationship that describes a set of links, a link being a connection among objects.
Figure 2: Association relationship
In the above example, the FileWriter is fully dependent on File component. Here it is always good to use ‘associate’ instead of ‘dependent’. The structure of the FileWriter component is explained using its associated members.
So whenever the data type of ‘fileToWrite’ is changed or ‘File’ class is changed, the structure of ‘FileWriter’ will also be changed.
CAUTION: Change in structure of already designed (finalized) component is not a best approach in Object Orientated Development.
Where to create dependent object for dependency relationship? The objects should be created only whenever needed. In general, it should be created only within the methods. In case if you need to share such object create it and pass it as argument to other methods (private).

          HTMLWriter {
             public write(ch) {
                   HTMLHelper helper = new HTMLHelper();
                   print(helper.getChar());
                   doSomething(helper);
             }
             private doSomething(HTMLHelper helper){
                   ….
             }
          }

                  
Where to create dependent object for association relationship? The object should be created or initialized in the constructor itself. If in case the object is dependent, it can be passed as parameters for constructors or use setter methods for assignment (dependency injection).
If the association is very strong (composition) then the object creation should be created in constructor.
Note: As per programming concepts, the responsibility of destroying an object is given to the component which created it [in non-trust relationship]. So be caution when you share the associated objects with outside components (using getter methods).
It is recommended that for Objects, it is always good to return the cloned version of the object.
For collections, either pass the Collections.unmodifiableCollection collections or its Iterator.

                  
Example of Associate Object Creation


          HTMLWriter {
                   File fileToWrite;
                    HTMLWriter(String filename){
                              fileToWrite = new File(filename)
                   }
                    HTMLWriter(File file){
                              this.fileToWrite = file
                   }
                   //NO SETTER IS USED
                   write(ch){
                             …
                   }
          }

Note: In case if the associate object is initialized by non-constructor methods (like setter, initialize), then it is always recommended to check for existence before usage in public methods.

                   write(ch){
                             if(filetoWrite != null) ….
                   }
Destroying associated objects: It is always recommended to provide methods like close, destroy, finalize etc… for destroying strongest associated references.

Case study for Associated reference Creation/destroy

TIP TO THINK: Think about making conn=null in Connection Manager instead of closing the connection. The object inside JVM will be released but the reference to the db server will not be release [dead connection].  Also note who creates the connection and who destroys it. It should be done within the component itself, because the connection to DB Server is strongly associated with the components.
Sharing Members: In most of the cases we may need to share members across multiple instances of same class or between classes. The sharing can be done using ‘static’ keyword in Java.

It is not recommended to share a modifiable member by providing public access. For such cases use ‘private static’ and provide ‘getter/setters’.
public static int MAX_CONNECTION = 10; //Not recommended

instead use

private static int MAX_CONN = 10;
public static int getMaxConnections(){return this.MAX_CONN}

Note: Use static only when sharing public constants.
 public static final int MAX_CONNECTION = 10;
Methods: As per OOAD methods are the actions or messages that can be handled by a particular component.
Using irrelevant methods in a class may change the meaning of that class. Ex. We should not have public read() method for writer classes.
Points to be asked before declaring an action/method in a component,

          1. Is this action part of the component?
          2. What does this action do?
          3. What should be the appropriate name of the action?
          Note: The method name with its parameters should give an abstract of what the action is.
          Ex. Connection getConnection(user,passwd)
          4. Implementation should not exceed 50 to 100 lines.
          5. Use private methods where-ever necessary.


private: Can have any number of private methods (not complex). These methods are used to support and provide separate implementation for public/protected methods. Using private method will help readability and reusability.
public: Use only limited number of public methods. Each public method should explain various actions in that component. While writing public methods all the inputs should be validated before processing. More details will be given later.
protected: Methods that need to be inherited. Be caution here for security. There is a high chance to override non-private methods.
Following options can be given with above method types.
final: Recommended. Make all the methods and its parameter final, unless demanded by your application or used for inheritance.
shared (static): These are members related to class and not to objects. They are created for doing some common utility or share data. It is recommended to provide less number of static methods in normal classes.
Example
 public final boolean copy(final File srcFile, final File destFolder){
          boolean copyStatus = false;
          if(validateInput(srcFile,destFolder)){
                   if(!destFolder.exist())
                             destFolder.mkdir();
File destFile = new File(destFolder,getFileName(srcFile);
                   copyStatus = copyFile(srcFile,destFile);
          }
          return copyStatus;
}

private boolean validateInput(File srcFile,File destFolder){
          boolean result = false;
          boolean isNull = (srcFile == null) && (destFolder == null);
          //Can add more conditions or call other validate methods.
          //finally result can be ‘logical AND of all validations'
         
          result = isNull;
}

private boolean copyFile(File srcFile,File destFile){ .... }
TIPS:
Public methods should be at least (75%) readable by non-technical persons.

Use private methods wherever possible.

Use less inline documentation for explaining the logic.

Public methods should be simple. Complex logics within the public methods can be moved to private method.

In simple, anyone should understand the logic of a public method in less than 30 seconds.

For reusability write more generic methods.

Logging


It is not recommended to use System.out or any console based output statements for logging purpose.

As a standard it is always recommended to use Apache Log4j.

Levels in Log4j

DEBUG: Used only for development purpose.
          log.debug(“Value of index ”+ index);
WARN: Used when you need to inform the users about the information as WARNING.
          log.warn(“Dest folder is null, creating the folder”);
INFO: Just an information like input parameter values.
          log.info(“àInside copyFile method”);

ERROR: Normally used with exception. Used when some condition is false;
          log.error(“Number of threads exceeds the limit”);
          log.error(“Error while copying”,ioexception);
Documentation

Even though the class name, member name, method name etc are self-explanatory, it is always recommended to provide appropriate documentation (javadoc) where ever possible.

Class level, Method Level documentation should be provided for all classes and methods. Refer ‘javadoc’ documentation for more parameters about Java Documentation.

Exception Handling

Exceptions are not the errors, they are just an expected/un-expected situation occur in a program which can be handled properly. To know better about handling exception, consider the following scenario.
“You were asked by your manager to attend a very important meeting. This meeting happens regularly (at least 4 times a month).”

Workflow 1:
          Start from home
          Check for Car's Condition
          if not conditioned fix it
          Start the car
          Driving in progress
          reached at time and attend the meeting
Workflow 2:
          Start from home
          check for Car's Condition
          if not conditioned fix it
          start the car
          driving in progress
          Heavy traffic        
          Take deviation
          driving in progress
          Flat tire
          Replaced flatted tire
          driving in progress
          Your were late by 10 mins
          Inform your manager about the meeting and go back
The pseudo code can be something like,

 travelAndAttendMeeting() throws DrivingException,UnableToAttendException{
                   boolean needToFillGas = false;
                   boolean isCarConditioned = false;
                   try{
                             isCarConditioned = checkForCarCondition();
                   }catch(LimittedGasException e){ //Checked Exception
                             needToFillGas = true;
                             isCarConditioned = true;
                   }catch(NoGasException e){ //Checked Exception
                             return;
                   }
                   if(!isCarConditioned) fixTheProblem()
                   car.start();
                   Route r = getRoute();
                   try{
                             drive(r);
                   }catch(HeavyTrafficeException e){ //Runtime Exception
                             r = getNewRoute();
                   }catch(DrivingException e){
                             if(e instanceof FlatTiredException){
                                      replaceTyre();
                             } else {
                                      //Log it
                                      throw e;
                             }
                   }
                   try{
                             drive(r);
                             //Need not check for exception (assumption: No traffic)
                   }catch(DrivingException e){
                             if(e instanceof FlatTiredException){
                                      replaceTyre();
                             } else {
                                      //Log it
                                       throw e;
                             }
                   }
                  
                   attendMeeting();
          }

Let us now look into various type exceptions provided by Java and how do we handle exceptions.
Checked or Compiled-Time exception: I prefer to use the term checked exception rather than compiled time. Checked exceptions are the exceptional cases we need to check before planning. This happens while planning.
In the above code ‘LimittedGasException’ is a checked exception, which means this exception should be properly handled (checked) before the actual execution of the process happens.
Programmatically this exception is checked by the compiler during compilation process. For convenience, Java compiler helps you in remembering this, in case you forget to put try…catch. Hence, it is also called compile-time exceptions.
Unchecked-Runtime Exceptions: These are exceptions happens during the process. Some of these exceptions can be handled then and there; others need to inform the caller.
See this,
                   try{
                             drive(r);
                   }catch(DrivingException e){
                             if(e instanceof FlatTiredException){
                                      replaceTyre();
                             } else {
                                      //Log it
                                      throw e;
                             }
                   }

Since this person is able to handle FlatTiredException, he calls replaceTyre and fixed it, in other case he informs the caller.

How to decide whether to create a Checked or Uncheck exception


You can use the following thumb rule:
If you need others to check the exception before execution (Compile time) then create ‘Checked Exception’ and throw it from the method.
Ex. ‘LimitedGasException’, ‘FileNotFoundException’
If an error caused during the process (Runtime) and if you need to inform the caller, create ‘Un-Checked Exception’ and throw it from the method.
Ex. ‘DrivingException’, ‘ArrayIndexOutOfBoundsException’
To make it clear, take the array index out of range exception, this is caused only if the accessing index is greater than array size. Since this index is created in runtime, we can’t always check this during compile time.
Note: Runtime exceptions can be known only during the execution process. This may caused due to some logical errors. Be caution while catching the Run-time exception. One such error is ‘NullPointerException’.

TIPS

Tip1: Do not throw implementation or logic specific exceptions
In our case, we should not throw ‘LimittedGasException’, because that is the part of implementation. Whereas ‘DrivingException, UnableToAttendException’ are the part of functionality.

Another example,
          compress(InputStream in, OutputStream out){
                   //read from instream
                   //store it to a temporary location (for some reason)
                   //For that you have opened a FileOutputStream, which throws FileNotFoundException.
                   //Handle this exception here & DO NOT THROW
                   //compress
                   //Write to out stream
                   }
                  
Tip 2: Logging the exception
Exceptions should be logged in the place where it is raised (originated). Then you can either handle it there or re-throw the exception.
Ex.
          Web à Business à DAO à Exception Raised here
          In this case ‘log’ the exception in DAO Layer.

Tip 3: Do not throw layer specific exceptions outside that layer

Types of classes

(Util, Helper, Implementation, Data (VO/DTO))
Util Classes: These classes act as a utility, which normally contains independent utility methods.
Following are the rules that need to be followed when writing an Util Class.
1. Class should be static
          All the methods in the class should be static.
2. Should be non-instantiated class
          Use final with private constructor.
3. Should not have any relationship or dependency with any other application related classes
4. Should not expose any shared (static) members.
Ex.

          public final class WebUtil{
                   private WebUtil() {
                   }
                  
                   public static String convertToHTML(String text){
                            
                   }
                   public static void encrypt(PrintWriter writer, String text){
                  
                   }
          }

You can write generic UTIL class or Application Specific UTIL class. Considering reusability it is not recommended to combine it together for reusability.
Helper Classes: These are the classes which helps the classes to do some extra functionality. This class is more specific to Applications or Functionality. This class is more similar to ‘Util’.

Ex. If you have a view which displays all the available Projects in the database, it is always preferred to write a Helper instead of Util.

          EmployeeView
          {
                  
                   List<Project> getProjects(){
                             //DO NOT DIRECTLY ACCESS DAO FROM HERE
                             return ProjectHelper.getProjects();
                   }
          }

Note: All the rules used for creating ‘Util’ class is applied here also.
Implementation classes: Classes in which actual implementation is programmed. It is a best practice to follow an interface-oriented programming instead of concrete classes.

This helps in decoupling the dependency between layers and implementation.

Ex. interface Compression {
                   encrypt ()
                   decrypt ()
               }
public class CompressionImpl implements Compression {
          …
          }

Note: At any point of time we can change the implementation classes with different algorithms, without affecting the caller. Use Factory pattern to construct such implementation classes.
Data Holding Classes
(POJO’s Value Object’s or Data Transfer Objects)
These classes hold’s data for processing. The main differentiation between these classes and others are these classes will have data and logic related to the data, whereas other classes use these objects for its functionalities in an application.
Note: These are the classes we normally take into consideration while we learn basics of OOPs (Classes and Objects). Also these objects are re-usable components, so no business logics or function specific annotations are allowed.
Following are the rules that need to be followed.
          1. At least one level of this class should be insatiable.
          2. Proper constructors should be provided.
          3. Necessary ‘setters’ and ‘getters’ need to be provided.
          4. Need to override hashcode, equals and toString.
          5. If the objects are meant for comparing or sorting, implementation of ‘Comparable’ is recommended.
          6. Always give public access to data members through public getter methods.
          7. Use ‘Cloneable’ interface with caution.
         
Refer related documents on POJO, VO and DTO for the variations.

Ex.
public class Employee implements Comparable {
          private String empid;
          private String name;
          private float salary;
         public Employee (String name, float salary){
                   this.name = name;
                   this.empid = EmployeeHelper.getNextEmployeeID();
                   this.salary = salary;
          }
          public void modifySalary (float salary) {
                   //Don’t use any business logic here
                   //Logic should be used outside and call this
                   //method using the final value.
                   if(hasPermission()){
                             this.salary += salary;
                   }
          }
         
          //Override
          hashCode, equals, toString and compareTo
         
          //Refer to API Reference (http://java.sun.com/j2se/1.4.2/docs/api/).
          }
           

Overriding hashCode/equals/toString

It is important that we need to override all these three methods (even it is not used). To know more about the purpose and implementation of these methods refer following links.

Example Reference:
General Tips

1. It is not recommended to return ‘USER FRIENDLY” messages from Service layer. Instead return a status code and UI will use some builder to prepare the message

         
2. Re-visit (you will know how simpler your code is) and try to optimize to the maximum you can.
3. Main aspects you need to look far are 3R (Responsibility, Reusability and Readability)


         
In general create class using ‘KISS’ rule.
“Keep It Simple, Stupid”



---A good developer knows that there is more to development than programming---