3
\$\begingroup\$

I have written this logic for logging, but is getting replication for all fields (last name, DOB,city, state, ZIP, etc). Here only validation logic is going to differ, whereas logging functionality will remain the same.

public boolean validFirstName(UserInputDetails record) {
        if (CommonUtil.isNullOrEmpty(record.getFirstName())) {
            record.setErrorCode(ErrorCodes.FIRSTNAME_NULL.getErrorCode());
            if(record.isDetailedLoggingEnabled()){
                Map<String,String> dtlLogMap = new HashMap<>(5);
                dtlLogMap.put("transId",String.valueOf(record.getTransactionId()));
                dtlLogMap.put("firstName",record.getFirstName());
                dtlLogMap.put("validationName","VALID_FIRSTNAME_CHECK");
                dtlLogMap.put("validationStatus","Errored");
                dtlLogMap.put("errorCode",record.getErrorCode());
                DetailedLogger.log(dtlLogMap);
            }
            return false;
        }
        
        if ( record.getFirstName().length() >=50) {
            record.setErrorCode(ErrorCodes.FIRSTNAME_INVALID_LENGTH.getErrorCode());
            if(record.isDetailedLoggingEnabled()){
                Map<String,String> dtlLogMap = new HashMap<>(6);
                dtlLogMap.put("transId",String.valueOf(record.getTransactionId()));
                dtlLogMap.put("firstName",record.getFirstName());
                dtlLogMap.put("firstNameLength",returnStringifNotNull(record.getFirstName().length()));
                dtlLogMap.put("validationName","VALID_FIRSTNAME_LENGTH_CHECK");
                dtlLogMap.put("validationStatus","Errored");
                dtlLogMap.put("errorCode",record.getErrorCode());
                DetailedLogger.log(dtlLogMap);
            }
            return false;
        }
        if(record.isDetailedLoggingEnabled()){
            Map<String,String> dtlLogMap = new HashMap<>(4);
            dtlLogMap.put("transId",String.valueOf(record.getTransactionId()));
            dtlLogMap.put("firstName",record.getFirstName());
            dtlLogMap.put("validationName","VALID_FIRSTNAME_CHECK");
            dtlLogMap.put("validationStatus","passed");
            DetailedLogger.log(dtlLogMap);
        }
        return true;
    }
\$\endgroup\$
  • 3
    \$\begingroup\$ i'm not sure how to say but have you considered putting some logic in methods? \$\endgroup\$ – Martin Frank Jul 22 at 5:02
  • \$\begingroup\$ Adding key value combination in hashmap and logging will be the same for validation success Or failure. Only key and value will be change... is there any way to write this code in jeneric method to avoid duplication of code? \$\endgroup\$ – Vasanth Jul 22 at 6:18
  • 3
    \$\begingroup\$ if (record.getFirstName().length() <=50) { return false; } Are you sure you want to fail when a first name is shorter than 50 characters? \$\endgroup\$ – Marc Jul 22 at 6:34
  • 1
    \$\begingroup\$ Have you tested this code? Does it produce the results you expect? If the results are wrong, it's not ready for review yet. \$\endgroup\$ – Mast Jul 22 at 13:22
  • 2
    \$\begingroup\$ Please do not update the code in your question to incorporate feedback from answers, doing so goes against the Question + Answer style of Code Review. This is not a forum where you should keep the most updated version in your question. Please see what you may and may not do after receiving answers. \$\endgroup\$ – Zeta Jul 24 at 8:58
5
\$\begingroup\$

as mentioned in the comments it might be helpful to extract methods to remove redundant code - you might want to read a more detailed explanation on "methods in java" to gain basic knowledge on methods.

tl;dr

"A Java method is a collection of statements that are grouped together to perform an operation"

let's collect your your statements that all checks have in common

Map<String,String> dtlLogMap = new HashMap<>(4);
dtlLogMap.put("transId",String.valueOf(record.getTransactionId()));
dtlLogMap.put("firstName",record.getFirstName());
dtlLogMap.put("validationName","VALID_FIRSTNAME_CHECK");
dtlLogMap.put("validationStatus","passed");
DetailedLogger.log(dtlLogMap);

we can extract a lot of stuff here into a method named Map<String,String> createLog(Record record, String checkResult) - noteworthy is, that we only group things together, that belong together - we do not (yet) extract the logging action - to maintain the Single Responsibility-principle. That makes it also a lot easier to find proper method names (instead of creating an obfucasting method name createAndPrintLog())

private Map<String,String> createLog(Record record, String checkName, String checkResult){
    Map<String,String> logs= new HashMap<>(4);
    logs.put("transId",String.valueOf(record.getTransactionId()));
    logs.put("firstName",record.getFirstName());
    logs.put("validationName",checkName);
    logs.put("validationStatus",checkResult);
    return logs;
}

note that a method has parameters that let you control the output of the method: the parameters should be self-explaining (Tell - dont Ask)

we can now apply this method in your checks and remove redundant code

...
if(record.isDetailedLoggingEnabled()){
    //here: method call instead of redundancy
    Map<String,String> dtlLogMap = createLogs(record,"VALID_FIRSTNAME_CHECK",  "Errored");
    dtlLogMap.put("errorCode",record.getErrorCode());
    DetailedLogger.log(dtlLogMap);
}
...

use more methods to remove more reduncancy!

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Its correct for single field ie firstName, If we apply this method ie createLogs() for more then one fields (last name, DOB,city, state,zip code) - there should be mapping, So question is how i can write the Generic method to receive the field name,field value ,error code ,message , transaction id ,status ? these arguments may grow or shrink based on validation pass or failure . Please refer the code \$\endgroup\$ – Vasanth Jul 24 at 8:02
  • \$\begingroup\$ thanks for pointing that part out! these requirements were not described clearly in your question Believe in your ideas! - if you feel you need some kind of Mapping then create a Mapper. Always keep in mind to segregate concerns: Things from the Model (in your case: UserInputDetails) should know how to create its proper log, while the results don't belong to that model \$\endgroup\$ – Martin Frank Jul 24 at 8:18

Not the answer you're looking for? Browse other questions tagged or ask your own question.