Software Engineering Course Assignment
Submitted by Raghav Mittal [2018701023]
IIIT-H
JavaArgs is a java version of Args that is described in the book "Clean Code: A Handbook of Agile Software Craftsmanship" by Robert C. Martin a.k.a. Uncle Bob:older_man:.
This piece of code has been originally forked from the Args program described in: Uncle Bob . I have further tried to implement a clean coding practice on this source code written in Java☕ as part of Assignment 📝.
- Usage 🏃✔️
1.1Schema
1.2 ArgsMain.java - Understanding the code
2.1 JavaDocs 📃 - Objective - Clean code 🎯
3.1 Code coverage ✅
3.3 Linting improvements 🚩
3.2 Code smells ♨️ - Licence 📄
Usage 🏃✔️
You will need to implement a main class to run this file.
Refer to the following schema to implemnt main class:
Schema types | Argument data types |
---|---|
char | boolean |
char* | string |
char# | Integer |
char## | double |
char[*] | one element of a string array. |
Example schema: (f,s*,n#,a##,p[*])
Coresponding command line:
"-f -s Bob -n 1 -a 3.2 -p e1 -p e2 -p e3"
You may use the following code to implement the main class ArgsMain.java
package com.cleancoder.args;
import java.util.Map;
public class ArgsMain {
/**
* This method is used to add two integers. This is
* a the simplest form of a class method, just to
* show the usage of various javadoc Tags.
* @param args This is the first paramter to addNum method
*/
public static void main(String[] args) {
try {
Args arg = new Args("l,p#,d*,b##, c[*], m&", args);
boolean logging = arg.getBoolean('l');
int port = arg.getInt('p');
double dob = arg.getDouble('b');
String[] cs = arg.getStringArray('c');
Map<String, String> ma = arg.getMap('m');
String directory = arg.getString('d');
executeApplication(logging, port, directory, dob, cs, ma);
} catch (ArgsException e) {
System.out.printf("Argument error: %s\n", e.errorMessage());
}
}
private static void executeApplication(boolean logging, int port, String directory, double dob, String[] cs, Map<String, String> ma) {
System.out.printf("logging is %s, port:%d, directory:%s, double:%f, stringArray:%s, map:%s",logging, port, directory, dob, cs[0], ma);
}
}
JavaDocs 📃
To understand the code better follow the JavaDocs given for the code.
Javadocs for tests are not added. For more discussion on JavaDocs for test check this Stackoverflow.
Objective - Clean Code 🎯
The goal of this exercise is to implement clean code practices in the code for JavaArgs and that include the following :
For the above changes some tools were used which needs to be mentioned here :
- Code smell: Jdeodrant integration in Eclipse.
- Code Coverage: Junit
- Linting: CheckStyle integration in Eclipse
Code Coverage: ✅
- Code coverage has been imporved from 88.5% to 91% overall.
- Code coverage is 97% if tests are not considered. As per the discussion online, tests are generally not covered during the coveraged for more information check this Stackoverflow link.
Below mentioned are the refactored pieces of code that helped imporve Code Coverage:
Unused lines were removed❌ as they were affecting it significantly.
// these lines were removed
public ArgsException() {}
public ArgsException(String message) {super(message);}
public void setErrorCode(ErrorCode errorCode) { this.errorCode = errorCode;}
testOkMessage()
was added➕ because OK enum was not covered
// following lines were added
public void testOkMessage() throws Exception {
ArgsException e = new ArgsException(OK, 'x', null);
assertEquals("TILT: Should not get here.", e.errorMessage());
}
Linting Improvements 🚩
Following unused line was removed❌
public ArgsData() {}
Return statement shifted from outside of switch case to default case inside switch case.
-
Removed wildcards from all import statement in the project. [also from java utils]
-
There are various ways to add imports in java and one of the two ways is adding imports with
wildcards (*)
, I chose to remove all the wildcards and manually add imports taking a reference from the following discussion Stackoverflow. -
Followed some code conventions where I added
static
imports before all the util imports and arranged alphabetically.
Code Smells ♨️
Major code smells were found in the code. In order to remove those code smells, modifications🔶 were done in Args.java
and a new file ArgsData.java
was added➕.
The changes that are stated below are in-coherence with the following manual which clearly states the decomposition methodology. For details refer to Jdeodrant.
Smell type | Refactoring Type | Variable Criteria | File modified🔶/added➕ |
---|---|---|---|
Long method | Extract method | m | Args.java modified |
God Class | Extract method | schema | ArgsData.java added;Args.java modified |
File ArgsData.java
was created➕ in order to decompose the god class. Following Code from Args.java
has been decompsed into ArgsData.java
private void parseSchemaElement(String element) throws ArgsException {
char elementId = element.charAt(0);
String elementTail = element.substring(1);
validateSchemaElementId(elementId);
if (elementTail.length() == 0)
marshalers.put(elementId, new BooleanArgumentMarshaler());
else if (elementTail.equals("*"))
marshalers.put(elementId, new StringArgumentMarshaler());
else if (elementTail.equals("#"))
marshalers.put(elementId, new IntegerArgumentMarshaler());
else if (elementTail.equals("##"))
marshalers.put(elementId, new DoubleArgumentMarshaler());
else if (elementTail.equals("[*]"))
marshalers.put(elementId, new StringArrayArgumentMarshaler());
else if (elementTail.equals("&"))
marshalers.put(elementId, new MapArgumentMarshaler());
else
throw new ArgsException(INVALID_ARGUMENT_FORMAT, elementId, elementTail);
}
private void validateSchemaElementId(char elementId) throws ArgsException {
if (!Character.isLetter(elementId))
throw new ArgsException(INVALID_ARGUMENT_NAME, elementId, null);
}
public boolean getBoolean(char arg) {
return BooleanArgumentMarshaler.getValue(marshalers.get(arg));
}
public String getString(char arg) {
return StringArgumentMarshaler.getValue(marshalers.get(arg));
}
public int getInt(char arg) {
return IntegerArgumentMarshaler.getValue(marshalers.get(arg));
}
public double getDouble(char arg) {
return DoubleArgumentMarshaler.getValue(marshalers.get(arg));
}
public String[] getStringArray(char arg) {
return StringArrayArgumentMarshaler.getValue(marshalers.get(arg));
}
public Map<String, String> getMap(char arg) {
return MapArgumentMarshaler.getValue(marshalers.get(arg));
}
}