Upload
madhura-oak
View
562
Download
2
Embed Size (px)
Citation preview
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!