Wednesday, March 17, 2010

Application Design/Implementation: Writing Modular and Clean

Some time back, I was going through a piece of Java code written by someone (my apologies, no wrong intention/no offence). My objective was to find out scenario specific details by looking at the code (a kind of small reverse engineering job). When I started, I thought it was a simple task assuming it’s a Java class and it would be quite modular and clean enough to understand. As soon as I opened the class file, all my assumptions were gone. First, it was a class file with around 2000 lines. Just for curiosity, I looked at some other class files in the application and found that there are class files up to approximately 10000 lines of code. It looked like; there was a prize for generating biggest class file.

Keeping the size aspect aside, I started looking at the class file of my interest. At this point I was bit relaxed that at this class file is relative much smaller as compared to other class files in the project. As soon as I started looking at the methods, I was lost. There were only few methods in the class (4 or 5) and the method of interest was of around 1000 lines. We can simply assume that 1000 lines of code couldn’t be straight forward and sequential. This method/class was no different. I couldn’t make out any thing about the flow of the logic. After scratching my head for couple of hours and drawing few interesting sketches about the flow, somehow I made a conclusion and handed over my summary.

Once this task was done, I started thinking about Object Oriented Analysis & design (OOAD) principles. When I think about OOAD, the very first thing I think of a modular and clean design. Even if I ignore the OOAD aspect and think about standard code quality aspect, this kind of code clearly falls in poorest design/implementation category. This code is not at all maintainable, as it’s hard to understand. I am sure, if the original developer looks at this code after some gap, he can’t make much from it himself. I can’t think of a single reason of writing such a code which makes our own life tough.

Writing a modular, cleaner and simple code is not a big magic. If we follow some simple design/implementation principles in mind, we will always result into nice, clean and modular code without doing anything especially for it. Let’s look at some of the key design/implementation principles and its applicability through an example of student result calculator functionality. I am considering Java class in the example, but this concept can be easily applied in other programming languages as well such as PL/SQL, C/C++ etc.

1. Complete Picture of the functionality: This is the first task to start with. Get a clean picture of the class/module required functionality. This helps significantly in identifying the design/implementation approach.

Let’s consider our example of deciding result and calculating marks percentage of a student. Assume there are 3 subjects with max score of 100 each and 40 is the passing mark in each subject. This gives a clear picture of required functionality at high level.

2. Core vs. Secondary responsibility of the class: Before starting with the code, get a clean understanding of primary vs. core responsibility of the class. Only primary responsibility should be implemented as part of the current class. The secondary responsibility should be implemented in a helper/util class.

In our example, validating that all subjects have corresponding marks should be implemented as validator helper class (e.g. MarksValidator); calculating the percentage should be implemented as util class (e.g. MarksUtil) class and the core flow of the responsibility remains in the primary class (e.g. StudentResult). Thus we will have three classes instead of one with clear defined responsibilities.


3. Resolution of not having bigger methods/classes: Looking at the application functionality, we should make a resolution of not designing/implementing classes/methods of more lines than a predefined size. Typically any class shouldn’t exceed more than 100 lines and any method shouldn’t exceed more than 10 lines. We can defined out own limits but it should definitely be small (e.g. a class shouldn’t go in 1000 lines and method in 100 lines)

4. Top-down Design/Implementation: Once we have identified classes with defined responsibilities, we can start implementing the functionality following top-down approach. By top-down, I mean start implementing higher level steps in primary method and support each/applicable steps with secondary methods.

In our example, let consider StudentResult class. We can have high level implementation of the class as below:

            public class StudentResult{
                        private Student student;
                       private List subjects;
                     
                      public void declareResultAndPercentage(){
                             String result= getResult();
                             System.out.println(“Result=”+ result);
                            Double percentage = getPercentage();
                            System.out.println(“Percentage=”+ percentage);
                     }
          }
If we look at the class, it’s very straight and self descriptive. In my main flow, I have two helper method calls with clear expectation.

5. Incremental Design/Implementation: By incremental, I mean that unwind the steps in incrementally. We should not jump on the entire method functionality and implement in one method itself as I mentioned in the beginning. Rather unwind the functionality one level at a time. Sub level logic should move to a next level method class.

Considering our example, our first level method has only two high level steps e.g. finding the result and calculating the percentage. Now let’s move to second level method implementation as below.

            public String getResult(){
                 String result = failed;
                 boolean isAllMarksAvailable=
MarksValidator.isAllMarksAvailable(subjects);
    boolean isPassedInEverySubject=
 MarksValidator. isPassedInEverySubject(subjects);

                if(isAllMarksAvailable && isPassedInEverySubject ){
                        result = “Passed”;
            }
            return result;
        }
      
Similarly I can unwind my second method as below:
           
public double getPercentage(){
        List marks = getAllMarks();
       Double percentage = MarksUtil.calculatePercentage(marks);
      return marks;
}

At this point of time, we are not done but completed another level of implementation. I can look back and see, there are some next level unimplemented methods (i.e. getAllMarks()) in the same class and some in secondary classes (i.e. isAllMarksAvailable(subjects) & isPassedInEverySubject(subjects) in MarksValidator class and calculatePercentage(marks) in MarksUtil class). We can follow the same pattern until we are done.

Advantage: Needless to say, a modular code is more clean, readable and maintainable (less error prone). If a developer wants to refer a particular part of functionality, he can pin point it very easily without spending much time or looking for big documentation. Scope of any fix is limited and impact can be clearly identified.

3 comments:

  1. i don't understand what are you saying because i want to find how to write application and just type in search engine and there is no document which is related to application other than letter writing specification

    ReplyDelete
  2. please give me the procedure how to make my minor project and what are the best topics of dealing project .how many types language must know to make project .

    ReplyDelete
  3. Not sure, I got your questions properly. There is no thumb rule for application design, which applies to all different kinds of the applications. One has to carefully analyse the application requirements and then assess the suitable technologies to meet the requirement.

    Would you mind proving brief description of your minor project and then put-up your specific questions? I will try answering your questions accordingly.

    ReplyDelete