Skip to content

Software Engineering course assignment 1 repository , Clean Code for the book "The Art of Clean Coding"

Notifications You must be signed in to change notification settings

GeekyShiva/CSE461-Assignment-1

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

JavaArgs

Clean Code Assignment

Submitted By: Shivang Shekhar
Roll No: 2019900021



JavaArgs Awesome is a java version of Args that is described in a book by Uncle Bob in his book called Clean Code.

Disclaimer ⚡

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 given in Software Engineering course taught (in Spring 2020) at International Institute of Information Technology

How to Run ⚡

Use the eclipse IDE to run JavaArgs

Before You Run ⚡

Before you Run the code, you might also nees to check for environment setup for Java:

Intall or Update Java ⚡

Run:

  * sudo add-apt-repository ppa:openjdk-r/ppa
  * sudo apt-get update -q 
  * sudo apt install -y openjdk-11-jdk 

For Tests ⚡

Run:

 * Run the command given below from the root folder of this repo
 * 'java -cp "lib/junit-4.13.jar:lib/hamcrest-core-1.3.jar:build/jar/args.jar" ./test/com/cleancoder/args/ArgsTest.java testCreateWithNoSchemaOrArguments'

Usage ⚡

Once you have set-up the basic requirements and your environment is ready, 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);
  }
}

Instructions: ⚡

Add ArgsMain.java file in at the follwing path : root/src/com/cleancoder/args in your code and use the below schema to operate:

Schema ⚡

Schema:

 - char    - Boolean arg.
 - char*   - String arg.
 - char#   - Integer arg.
 - char##  - double arg.
 - 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

Objective ⚡

The goal of this exercise is to implement clean code practises in the code for JavaArgs and that include the following :

  1. Removing Lints
  2. Imporving Code-coverage
  3. Refactor Control Flow
  4. Refactor Classes and Methods
  5. End to End Test ..* Add tests for uncovered code ..* Remove duplicate tests ..* Add Test Annotation [Decorators] ..* Refactor if/else/try/catch conditions to improve coverage
  6. Remove Code Smells

There is a list of comprehensive actions performed under the above-mentioned points and are discussed below in detail.

Understanding the Code ⚡

Class Diagram Args ⚡

image

Or you may find it in eclipse at

src/ArgsClassDiagram.ucls

JavaDocs ⚡

To understand the code better follow the JavaDocs given for the code.

JavaDocs for tests are not available so as to reduce the on-screen clutter of code and I have tried to make classes/methods/ arg names to be more descriptive in-order to save the clutter

For more discussion on JavaDocs for test check this Stackoverflow 💡 Link.

Clean Code ⚡

In this section I am going to discuss about the code cleaning that has been done for the JavaArgs Code.

As stated above there are some major practises that were utilised to clean the code, namely:

1. Removing Code Smells 
2. Imporving Code-coverage
3. Refactoring Control Flow
4. Refactoring Classes and Methods
5. End to End Testing
..* Add tests for uncovered code 
..* Remove duplicate tests 
..* Add Test Annotation [Decorators]
..* Refactoring if/else/try/catch conditions to improve coverage
6. Removing Code Lint 

For the above changes some tools were used which needs to be mentioned here :

  • 📌 Code Smell : I used Jdeodrant 💚 integration to remove code smells and lints.
  • 📌 Code Coverage: I used Junit ❤️
  • 📌 Linting : I used CheckStyle ❤️ to remove lint from code

Now lets deep dive into the knitty-gritty of the various aspects of code cleaning:

Code Coverage ⚡

  • 🔘 Code coverage has been imporved from 88.5 % 🔻 to ** 91% ** ✔️ overall.
  • 🔘 Code coverage is at a at a whopping 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.

Code Coverage Improvements ⚡

Below mentioned are the refactored pieces of code that helped imporve Code Coverage:

❌ Unused lines for methods were removed to imporve coverage as they were affecting significantly.

From ArgsException.java 🔴 --remove

public  ArgsException() {}

public  ArgsException(String message) {super(message);}

🔂

public void setErrorCode(ErrorCode  errorCode) {  this.errorCode = errorCode;}

From ArgsExceptionTest.java ➕ Test for OK condition

testOkMessage() was not available and OK enum was not covered.

Refer below to the piece of code added :

public void testOkMessage() throws Exception {

ArgsException e = new ArgsException(OK, 'x', null);

assertEquals("TILT: Should not get here.", e.errorMessage());

}

Linting Improvements ⚡

From ArgsData.java 🔴 --remove

public ArgsData() {}

❌ 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.

〽️ In file Argsexception.java Return statement shifted from outside of switch case to default case inside switch case.

default:

return ""; // return statement shifted here

}

Code Smells ⚡

The vanilla code form base repository had some major code smells in mainly in the follwing files

🔻 Args.java 🔻 ArgsData.java

So inorder to remove code smells I handled the following changes.

Those were in

  1. God Class ..* for schema Extract Class
  2. Long Method ..* Extract Method for variable criteria 'm'

The changes that are stated below are in-coherence with the following manual which clearly states the decomposition methodology. For details refer to Jdeodrant

Name Refactoring Type Variable Criteria
Long method Extract method m

if (m == null) {

throw new  ArgsException(UNEXPECTED_ARGUMENT, argChar, null);

Also,

➕ File ArgsData.java was created in order to decompose the following class:

Name Refactoring Type Variable Criteria
God Class Extract Class Schema

❌ Following Code from Args.java has been decomposed 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)); 

  } 

} 


Tests ⚡

➕ Following test code has been added in order to cover all the test conditions and remove any potential smells if present at all

public void testOkMessage() throws Exception {
    ArgsException e = new ArgsException(OK, 'x', null);
    assertEquals("TILT: Should not get here.", e.errorMessage());
  }

also to handle concatinated type strings as arguments.

  @Test 
  public void testContinuousFlags() throws Exception {
    Args argsData = new Args("x#,y##", new String[]{"-xy", "20", "4.5"});
    assertTrue(argsData.has('x'));
    assertTrue(argsData.has('y'));
    assertEquals(20, argsData.getInt('x'));
    assertEquals(4.5, argsData.getDouble('y'),.001);
  
  }
}

MalformedMap has been modified as well since it was incomplete in the code.

 @Test
  public void malFormedMapArgument() throws Exception {
    try {
      new Args("f&", new String[] {"-f", "key1:val1,key2"});
      fail();
    } catch (ArgsException e) {
      assertEquals(MALFORMED_MAP, e.getErrorCode());
      assertEquals('f', e.getErrorArgumentId());
    }
  }

Bug ⚡

The JavaArgs program is not fully bug free but has a bug in the schema which is described below :

The arguments essentially read only single command line character that is passed and overlooking the characters passed after.

For example

Args args = new Args("x,y", new String[]{"-x", "alpha", "-y", "beta"});

In the above code the arguments taken and tested are primarily alpha and not beta if passed as serial arguments.

Also, in this code if the command line arguments parsed are

-s &amp

value returned is an address of character and not the output.

In some cases the same has been observed in * character.

Fix ⚡

Fix for this would be changing the schema definition handling and parsing in the code

License

MIT

About

Software Engineering course assignment 1 repository , Clean Code for the book "The Art of Clean Coding"

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages