Think with Enlab

Diving deep into the ocean of technology

Stay Connected. No spam!

How to Apply Top-Down Approach in Programming

 

Writing code becomes much easier when requirements are broken down into actionable items with given system architecture, database design, and UI sketch. In this article, I’m going to share with you how to apply the top-down approach in your programming work and some useful tips to improve your efficiency at work as well as the quality of codes.

 

Top-Down Approach in Programming

What is a Top-down Approach?

A top-down approach is about breaking down a system into the subsystems that make it up. The process can be repeated to break down subsystems into low-level elements like classes and methods. This approach can be applied to all levels, from high-level system architecture to low-level functionality implementation, just to remember to start from the top. It doesn’t necessarily always be the absolute top.

How to apply the Top-down Approach in Programming?

Defining the necessary steps before implementing a method will give you a clear insight into the method and help you structure your codes well. Here are what you should do to achieve this:

  • Stage 1: Break down the method's logic into steps using comments, as shown in the example below.
  • Stage 2: Generate dependent methods, classes, enums, etc., used in stage 1. For now, just generate empty dependent methods or classes and don’t bother implementing them during this stage.
  • Stage 3: Once you have the code skeleton, implement dependent methods one by one and run the unit test.

An Example of Top-down Approach in Programming

Below is an example of how to implement a method to perform a payment schedule. 

Firstly, write a kind of Pseudocode (C# code actually, but it doesn’t compile since it lacks undefined submethods) that represents the business logic of the payment schedule.

 

private ProccesingResult<IList<PaymentScheduleDetail>> SchedulePayments(PaymentSchedule schedule)
{
       // 1. Validate schedule input
       //  (In case method IsScheduleValid doesn't have more than 5 lines, write it inline)

       ProccesingResult<IList<PaymentScheduleDetail>> result = IsScheduleValid(schedule);
       if (result.ErrorCode != ErrorCodes.None)
                       return result;
       // 2. Call EPIC API to schedule the payments
       //  (If you need to have more than 5 lines, define a method SchedulePaymentViaEPIC(), 
       // else write it  inline)
       if (SchedulePaymentViaEPIC(schedule))
       {
                     // 3. Upon success, call EPIC API to get payments just scheduled to return to client
                     // (If you need to have more than 5 lines,  define a method
         // SchedulePaymentViaEPIC(),  else write it inline)
                     result.Data = GetPaymentsViaEPIC(schedule);
       }
       //
       // The 3 steps (1., 2., 3.) defined above are steps of the method's workflow. 
      // Do not write any line of code unless you have defined this workflow.
       //
       return result;
}

 

Secondly, as the method skeleton is written, go on to generate required sub-methods and classes.

 

private IList<PaymentScheduleDetail> GetPaymentsViaEPIC(PaymentSchedule schedule)
{
       throw new NotImplementedException();
}
private bool SchedulePaymentViaEPIC(PaymentSchedule schedule)
{
       throw new NotImplementedException();
}
private ProccesingResult<IList<PaymentScheduleDetail>> IsScheduleValid(PaymentSchedule schedule)
{
       throw new NotImplementedException();
}
internal enum ErrorCodes
{
       None = 0
}
internal class ProccesingResult<T>
{

}
internal class PaymentScheduleDetail
{

}

 

Finally, once all sub-methods and classes are generated (not implemented yet), continue to implement them one by one and conduct unit testing to ensure it works as expected.

private ProccesingResult<IList<PaymentScheduleDetail>> IsScheduleValid(PaymentSchedule schedule)
{
       var result = new ProccesingResult<IList<PaymentScheduleDetail>>();
       // TODO: Validation logic implementation
       // Below is sample code to demonstrate the usage of ProccessingResult class
       result.ErrorCode = ErrorCodes.InvalidInput;
       result.ErrorMessage = "Fields are missing input or in invalid format";
       result.ErrorData = "RequiredFields:Field1,Field2;InvalidFormatFields:Field3,Field4";

       return result;
}
internal enum ErrorCodes
{
       None = 0,
       InvalidInput = 1
}
internal class ProccesingResult<T>
{
       public T Data { get; set; }
       public ErrorCodes ErrorCode { get; set; }
       public string ErrorMessage { get; set; }
       public string ErrorData { get; set; }

       public ProccesingResult()
       {
                       ErrorCode = ErrorCodes.None;
       }
}

 

More Useful Tips

Creating a class

You need to know clearly what the responsibility of a class is and which layer/project to place that class. Don’t forget to stick with the Single Responsibility Principle.

Implementing a method

Define only one single responsibility for each method and make it clear by using a method signature. Besides, it’s important to know exactly the necessary steps (workflow) of that method before writing it.

Refactoring codes

To deliver quality code (well-structured, good readability, and maintainability), refactoring must be done frequently. Code refactoring is the process of reviewing codes and finding room for improvement. To refactor code effectively, you should find answers to these questions:

  • When: Only refactor when you have finished implementing the feature/functionality and it works as expected.
  • How often: Anytime when you finish implementing a functionality (a requirement item).
  • What to improve: Look at these aspects:
    • Are classes in the right places? Validate if they are placed in the correct layer of the system architecture.
    • Do methods belong to the right classes? A class normally takes a high-level responsibility for a given duty, and its methods take low-level responsibility for such duty. So the responsibilities of a class and its methods should be highly correlated. For instance, the class PurchaseOrderService should contain only methods that take responsibility for processing purchase orders, like submitting an order or approving an order.
    • Do methods follow the Single Responsibility Principle?
    • Can classes be abstracted, and methods can be inherited? Look for classes and methods that take similar responsibilities and see if base classes or helper classes can be created to provide inheritance and reuse.
    • Is there any duplicated code? Look for duplicated coding lines and see if methods can be extracted from these same lines and reused in all places.
  • How to refactor: Refactor small items one by one to ensure no big bang happens. Once an item is refactored, run automated test suites if you have one; otherwise, manually test it to ensure that your new code functions as the old code does.

Resolving issues

Issues often steal a lot of your time. How fast you can resolve an issue depends on your technical knowledge and your past experiences resolving a similar problem. I don't see any other better way than enhancing your technical knowledge and codifying your past experiences.  

Want to know what you should do? Check these out:

  • Keep learning fundamental knowledge of the technologies you're working on. For instance, if you're working with ASP.NET WebAPI, this document is a good reference for you. Once you understand the fundamentals of the technologies, you will quickly understand the issues and be able to point out the root causes. If you know the root causes, you're very close to resolving them.
  • Share what you have learned about the fundamentals with other colleagues to help you consolidate the knowledge.
  • Maintain your experiences: Our experiences can’t be transferred to others, but our knowledge can be. It’s always good to have a handbook recording all issues you have ever faced and resolved to reference any time again. Make sure that these issues are organized in pairs of issue-solution to ease your search for known issues later on.

 

Final thoughts,

The top-down approach is to go from the general to the specific. By doing this, you can solve any complex problems and easily understand what your code units do without looking into the very details.

Hopefully, the knowledge and the essential tips I shared with you in this article will be useful in improving your coding efficiency. 

Thank you for reading! 

 

CTA Enlab Software

 

Reference

About the author

Vinh Tran

Hi, my name's Vinh and I'm the CEO of Enlab Software. I'm passionate about building world-class digital products, helping my team become mature in their careers, and creating an environment where people find purpose and meaning in life.
Frequently Asked Questions (FAQs)
What is the top-down approach in software engineering, and how is it applied in programming?

The top-down approach in software engineering involves decomposing a system into subsystems or components, from high-level architecture down to low-level functionalities. In programming, it’s applied by breaking down the method’s logic into steps, generating dependent methods or classes (even as placeholders initially), and then implementing and testing these components one by one, ensuring each part functions correctly before moving on to the next.

What are the key stages of the top-down approach when implementing a method in programming?

The key stages include:

Breaking down the method’s logic into steps using comments or pseudocode.

Generating required sub-methods, classes, or enums, which might initially be placeholders.

Implementing and unit testing each dependent method or class one by one, ensuring each component works as expected before proceeding.

What are some important considerations when creating a class or implementing a method using the top-down approach?

When creating a class, ensure it adheres to the Single Responsibility Principle and is placed correctly within the system architecture. For method implementation, define a clear, single responsibility for the method, and understand the necessary steps or workflow of the method before writing it. This helps in maintaining clear, concise, and maintainable code.

How does code refactoring fit into the top-down approach, and what should be considered during the refactoring process?

Code refactoring is integral to maintaining quality code in the top-down approach. It involves reviewing code to find improvements, focusing on the placement of classes, ensuring methods follow the Single Responsibility Principle, abstracting classes for inheritance and reuse, and eliminating duplicate code. Refactor in small, manageable increments and test thoroughly after each change to ensure functionality remains intact.

How can developers effectively resolve issues and enhance their problem-solving skills within the top-down approach framework?

Effectively resolving issues requires a deep understanding of the technology stack and learning from past experiences. Enhance problem-solving skills by continuously learning fundamental knowledge of the technologies in use, sharing insights with colleagues, and maintaining a handbook of resolved issues for future reference. Understanding fundamental concepts aids in quickly pinpointing issue root causes, leading to faster and more effective problem resolution.

Up Next

Big Data Technologies Transforming Software Development
July 05, 2024 by Dat Le
In the rapidly evolving world of software development, Big Data stands out as a transformative force....
June 27, 2024 by Dat Le
In today's rapidly evolving digital landscape, secure coding practices are paramount to safeguarding applications from a...
June 20, 2024 by Dat Le
In the rapidly evolving digital landscape, the role of User Interface (UI) and User Experience (UX)...
Leveraging UX Design Principles in Software Development
June 17, 2024 by Dat Le
In the dynamic world of software development, one element has emerged as crucial to success: User...
Roll to Top

Can we send you our next blog posts? Only the best stuffs.

Subscribe