Clean code in Java, Java EE: How to get rid of code and design debt

Post on 12-Apr-2017

558 views 2 download

Transcript of Clean code in Java, Java EE: How to get rid of code and design debt

Clean Code in Java, Java EE : How to get rid of code & design debt

Madhura Oak

Project ManagerIntellect Design Arena Ltd.

Code Debt

1. Use of method to get switch case value

private static final String _ALPHABETS = "ALPHABETS";private static final int _ALPHABETS_NO = 1;

private int getSwitchId(String value) {if(value.equals(_ALPHABETS)) {

return _ALPHABETS_NO;}...

}

int switchId = getSwitchId(data);switch(switchId) {

case _ALPHABETS_NO: ...

}

Clean Code – Use enum

private enum DataType { ALPHABETS,ALPHA_NUMERIC,NUMERIC,SWIFT,ALL,DATE,TIME

}

switch(DataType.valueOf(data)) {case ALPHABETS:

...}

2. Mixing error handling with output

public void create(Map<String,Object> results) throws Exception {//validate conditionif(condition) {

results.set("ERROR_CODE","CONDITION VIOLATED"); }

}

Map<String,Object> results = new HashMap<>();create(results);if(results.containsKey("ERROR_CODE")) {

...}

Clean Code – Use Exceptionspublic void create(Map<String,Object> results) throws Exception {

//validate conditionif(condition) {

throw new ConditionViolatedException();}

}

try {Map<String,Object> results = new HashMap<>();create(results);

}catch(ConditionViolatedException exp) {

}

3. If..else blocks without braces

if(condition) do something

else do something else

Use curly braces for if..else blocks

if(condition) {//do something

}else {

//do something else}

4. Avoid creation of unused objects

public String getColumns(List<Field> fields) {String column = "";StringBuilder columns = new StringBuilder();for(Field field : fields) {

column = field.getColumn();columns.append(column);columns.append(",");

}return columns.toString();

}

5. Redundant code

List<String> countries = new ArrayList<String>();countries.add(countryCode);if (countries != null && !countries.isEmpty()) {

for (String countryCode : countries) {//do some action

6. Unused EJB References

@EJB(name = "RetryTimer", mappedName = "ejblocal:retryTimer")private IRetryTimer retryTimerBean;

Clean Code – Ensure your code is minimal

Less code means less maintenance. Presence of unused variables could not only add to the clutter in code but also would cause unnecessary memory allocation.

7. Variables declared in procedural style – at the start of method

public String getColumns(List<Field> fields) {String column = "";StringBuilder columns = new StringBuilder();for(Field field : fields) {

column = field.getColumn();columns.append(column);columns.append(",");

}return columns.toString();

}

Clean Code - Declare variable only where required

public String getColumns(List<Field> fields) {StringBuilder columns = new StringBuilder();for(Field field : fields) {

String column = field.getColumn();columns.append(column);columns.append(",");

}return columns.toString();

}

8. Use of String literals in multiple classes

if(validate.equals("Y")) {

}

Clean Code – Option 1- Create a class for constants

public class Constants {public static final String Y = "Y";

}

if(validate.equals(Constants.Y)) {

}

Clean Code – Option 2 – Define constants in properties file

Constants.propertiesVALIDATION_FLAG=Y

public class Constants {private ResourceBundle bundle = ResourceBundle.getBundle("Constants");public static final String VALIDATION_FLAG =

bundle.getProperty("VALIDATION_FLAG");}

//usage in codeif(validate.equals(Constants.VALIDATION_FLAG)) {

}

9. Using StringBuffer as local variable

public String getUpdateQuery() {StringBuffer updateQuery = new StringBuffer();...return updateQuery.toString();

}

Clean Code – Use StringBuilder for local variables

public String getUpdateQuery() {StringBuilder updateQuery = new StringBuilder();...return updateQuery.toString();

}

10. Declaring redundant default constructor

public class RuntimeDAO {public RuntimeDAO() {}

}

11. Using default constructor to initialize member variables

private List<String> documentTypeIds;

public class RuntimeDAO {public RuntimeDAO() {

documentTypeIds = new ArrayList<>();}

}

Clean Code – Use default constructor only when required

private List<String> documentTypeIds = new ArrayList<>();

public class RuntimeDAO {

}

12. Names do not reveal intentionprivate void addRequiredProdDbTable(

Map<String,List<IwFieldData>> tableFieldMap) {Map<String,String> txnTables = mProductEntity.getTXNTables();for(String tableName : txnTables.keySet()) {

if(!tableFieldMap.containsKey(tableName) && !mProductEntity.isMultiOptTable(tableName)) {

tableFieldMap.put(tableName, new ArrayList<IwFieldData>());}

}}

Clean Code – Use intention revealing names

private void addNonMultiOptTxnTables(Map<String,List<IwFieldData>> tableFieldMap) {

Map<String,String> txnTables = mProductEntity.getTXNTables();for(String tableName : txnTables.keySet()) {

if(!tableFieldMap.containsKey(tableName) && !mProductEntity.isMultiOptTable(tableName)) {

tableFieldMap.put(tableName, new ArrayList<IwFieldData>());}

}}

13. clone() does shallow cloningpublic class Field implements Cloneable {

private List<String> columnValues;}

Field fieldCopy = (Field)field.clone();//both field and fieldCopy refer to same columnValues list

Clean Code – Deep Cloningpublic class Field implements Cloneable {

private List<String> columnValues;

public Field createCopy() {try {

Field copy = clone();copy.columnValues =

(List<String>)columnValues.clone();}catch(CloneNotSupportedException exp) {

//do nothing}

}}

14. Using Date to calculate time difference

Date startDate = new Date();/* code */Date endDate = new Date();long executionTime = startDate.getTime() – endDate.getTime();

Clean Code – Use System.currentTimeMillis()

long startTime = System.currentTimeMillis();/* code */long endTime = System.currentTimeMillis();long executionTime = endTime – startTime;

15. Incorrect way of comparing string

if (sleepTime != null && sleepTime != "") {

Clean code – Use equals whether String is not null and not empty

if ("".equals(sleepTime)) {

If sleepTime is null it returns false, so null check is not needed.

16. Common codecatch(InterruptedException exp) { AppException exp = new AppException();

exp.addError(Constants.ERROR_CODE, "EXP005");exp.addError(Constants.ERROR_MESG, exp.getMessage());throw exp;

}catch(SQLException exp) {

AppException exp = new AppException();exp.addError(Constants.ERROR_CODE, "EXP011");exp.addError(Constants.ERROR_MESG, exp.getMessage());throw exp;

}catch(Exception exp) { AppException exp = new AppException();

exp.addError(Constants.ERROR_CODE, "EXP003");exp.addError(Constants.ERROR_MESG, exp.getMessage());throw exp;

}

Clean Code – Refactor common code in private method

private AppException createAppException(String errorCode) {AppException exp = new AppException();exp.addError(Constants.ERROR_CODE, errorCode);exp.addError(Constants.ERROR_MESG, exp.getMessage());return exp;

}

//codecatch(InterruptedException exp) {

throw createAppException("EXP005");}catch(SQLException exp) {

throw createAppException("EXP011");}catch(Exception exp) {

throw createAppException("EXP003");}

Initializing constant values multiple times

public class Constants {ResourceBundle resbundle; //read from properties file

public static String TIMEOUT =resbundle.getProperty("TIMEOUT");

}

Every time Constants.TIMEOUT is referred, it will fetch the property from the file.

Clean Code – Declare constants with final keyword

public class Constants {ResourceBundle resbundle; //read from properties file

public static final String TIMEOUT =resbundle.getProperty("TIMEOUT");

}

Final variables are initialized only once

Collections

1. Not using generics, Use of Iterator

private List getTableColumns(Map tableFieldListMap) {Set tables = tableFieldListMap.keySet();Iterator iterator = tables.iterator();while(iterator.hasNext()) {

String tableName = (String)iterator.next();...

}}

Clean Code – Use generics, for..each loop

private List<String> getTableColumns(Map<String,Field> tableFieldListMap) {for(String tableName : tableFieldListMap.keySet()) {

...}...

}

2. Using collection framework classes as method parameters and

variablesprivate ArrayList fieldDetails = new ArrayList();private HashMap linkedFields = new HashMap();

private void setTransactionParams(Hashtable params) {...

}

Clean Code – Use collection framework interfaces, use generics

private List<Field> fieldDetails = new ArrayList<>();private Map<String,FieldProperties> linkedFields = new

HashMap<>();

private void setTransactionParams(Map<String,Object> params) {...

}

3. Collections.synchronizedMappublic class ServiceLocator {

Map<String,Object> cache;

public ServiceLocator() {cache = Collections.synchronizedMap(

new HashMap<String, Object>()); }

}

Clean Code – ConcurrentHashMap has better performance than Collections.synchronizedMappublic class ServiceLocator {

ConcurrentMap<String,Object> cache;

public ServiceLocator() {cache = new ConcurrentHashMap<String, Object>());

}}

4. Use of Hashtable

Map<String,String> parameters = new Hashtable<>();

Clean Code – ConcurrentHashMap has better performance than Hashtable

Map<String,String> parameters = new ConcurrentHashMap<>();

5. Using classnames as method parameters

private void setParameters(HashMap<String,String> parameters) {

}

Clean Code – Use interfaces instead of classes

private void setParameters(Map<String,String> parameters) {

}

Exception Handling

1. Using e.printStackTrace() to print exception stack trace

When log4j is configured in application, do not use e.printStackTrace() as it could print the exception stack trace in log files other than the intended ones.

Clean Code – Use logger.fatal to print exception stack trace

logger.fatal(e);

2. Printing exception stack trace on console

When a java class is called from console, exception stack trace is printed on console when an exception occurs.

Clean Code – Print user friendly error messages on console

Printing an exception stack trace on console is of no use if your application is being used by a client. It will keep your client wondering what went wrong.

Instead of printing exception stack trace on console, handle all exceptions and throw a user friendly exception message which tells the client what went wrong and what corrective action needs to be taken to avoid its occurrence.

3. NumberFormatException is not handled when Integer.parseInt() is

called

String strPeriod = null;//read string value for periodint period = Integer.parseInt(strPeriod);

Clean Code - While using Integer.parseInt() always remember to

handle NumberFormatException

String strPeriod = null;//read string value for periodint period = 0;try {

int period = Integer.parseInt(strPeriod);}catch(Exception exp) {

…}

4. Logging exception stack trace multiple times

//method 1catch(SQLException exp) {

log.fatal("Exception occurred:", exp);throw exp;

}

//method 2try {

//call method 1}catch(Exception exp) {

log.fatal("Exception occurred:", exp);}

Clean Code – Log exception stack trace only once where it occurs.

//method 1catch(SQLException exp) {

log.fatal("Exception occurred:", exp);throw exp;

}

//method 2try {

//call method 1}catch(Exception exp) {

}

Design Debt

1. Data Access Object (DAO) does multiple things

Anemic EJBs with only transaction boundary

DAO with database manipulation code,

server side pagination andbusiness logic

Clean Code - DAO should do only database manipulation

EJB with business logic DAO

Value List Handler for server side pagination

2. Singleton DAO

Session Facade DAO

A single DAO instance is used by multiple Session Façade objects implemented as stateless session beans or POJOs. This slows down application performance as multiple concurrent threads are accessing a single DAO instance.

Clean Code – DAO should not be singleton

Session Facade DAO

Every Session Façade object (EJB or POJO) can create an instance of DAO.

3. Remote EJB is looked up multiple times in code

for(int i = 0; i < j; i++) {Hashtable<String, String> env = new Hashtable<>();env.put("java.naming.factory.initial",

Environment.APP_INITIAL_CONTEXT_FACTORY);env.put("java.naming.provider.url", Environment.APP_CONTEXT_FACTORY_URL);Context ctx = new InitialContext(env);home = (FeedUploadEJBRemote)ctx.lookup("FeedUploadEJB");…

}

Clean Code – Use Service Locator if you are using EJB 2.x with local/remote

client or EJB 3.x with remote client

Client Service Locator

EJB lookup

EJB object

Clean Code – For EJB 3.x with local client use Dependency

Injection

@EJBprivate FeedUpload feedUpload;

4. Database calls to fetch static values are made within while loop

while(condition) {sleepTime = genericDao.getStaticValue("SLEEP_TIME");

}

Clean Code – Use a cache to store static values. They should read from database only

once ideally during the start of the application. When the static values are modified they should be simultaneously

written in both cache and database. A pub-sub design can also be used to periodically

update cache.

Cache DAOClient

5. Tightly coupled DAOs

EmployeeDAO DepartmentDAO

EmployeeMgmtEJB

Clean Code – Wire DAOs at Service Layer

EmployeeDAO DepartmentDAO

EmployeeMgmtEJB

Thank you!