Puy Web
Profile Blog
EN TH
Blog Code Smell Long Method Part 1
Code Smell Long Method Part 1
Coding Jun 20, 2019

Code Smell Long Method Part 1

Long Method is one of the Code Smells in the Bloaters category. Its defining characteristic is having far too many lines of code. As a rule of thumb, any function or method exceeding 10 lines should be reviewed to see if it might cause tangled code in the future.

We usually don't notice it happening because we just keep adding new lines into a function or method as requirements grow. But before we know it, the code is as long as a kite string, making future modifications an absolute headache.

Many people might wonder: Doesn't refactoring code and creating more files negatively affect performance? It's true that refactoring often increases the number of files, methods, and other structural elements. However, let's look at it from another angle. When code becomes completely unmaintainable—to the point where you think "it's better to rewrite this entirely," or "just don't touch it," or it's simply impossible to read—isn't it much better to have clean code that you can easily manage and scale without any issues?

The refactoring techniques used to solve the Long Method problem include:

  • Extract Method
  • Replace Temp with Query
  • Introduce Parameter Object
  • Preserve Whole Object
  • Replace Method with Method Object
  • Conditionals and Loops: Decompose
  • ConditionalConditionals and Loops: Extract Method

This article will cover the first four techniques: Extract Method, Replace Temp with Query, Introduce Parameter Object, and Preserve Whole Object.

Extract Method

This involves reducing the amount of code by extracting a chunk of it and creating a new method to use instead. (Many modern IDEs actually have a built-in feature for this!)

Problematic Code: You have a block of code dealing with a single, cohesive concept (for example, printing logic) that can easily be separated into a new method.

class Order {
  private String productId;
  private float pricePerQty;
  private float tax;
  private float totalPrice;

  public void addToCart(String productId, float pricePerQty, float tax) {
    this.productId = productId;
    this.pricePerQty = pricePerQty;
    this.tax = tax;
    this.totalPrice = this.pricePerQty + (this.pricePerQty * this.tax);

    // print detail
    System.out.println("product id:" + productId);
    System.out.println("price per qty:" + pricePerQty);
    System.out.println("tax:" + tax);
    System.out.println("totalPrice:" + totalPrice);
  }
}

Problematic Code Usage

public static void main(String[] args) {
  Order order = new Order();
  order.addToCart("P01", 100, 0.03f);
}

Refactored Code: Separate that cohesive block of code into its own distinct method.

class Order {
  private String productId;
  private float pricePerQty;
  private float tax;
  private float totalPrice;

  void addToCart(String productId, float pricePerQty, float tax) {
    this.productId = productId;
    this.pricePerQty = pricePerQty;
    this.tax = tax;
    this.totalPrice = this.pricePerQty + (this.pricePerQty * this.tax);

    printCartDetail(this.productId, this.pricePerQty, this.tax, totalPrice);
  }

  void printCartDetail(String productId, float pricePerQty, float tax, float totalPrice) {
    System.out.println("product id:" + productId);
    System.out.println("price per qty:" + pricePerQty);
    System.out.println("tax:" + tax);
    System.out.println("totalPrice:" + totalPrice);
  }
}

Refactored Code Usage

public static void main(String[] args) {
    Order order = new Order();
    order.addToCart("P01", 100, 0.03f);
}

Replace Temp with Query

This technique reduces the use of local variables inside a method by relying on a helper method instead.

Problematic Code: Creating a local, temporary variable to store the result of a calculation, and then using that variable later in the code.

class Order {
  private int qty;
  private float pricePerQty;
  private float tax;

  float calculateTotalPrice(int qty, float pricePerQty, float tax) {
    this.qty = qty;
    this.pricePerQty = pricePerQty;
    this.tax = tax;

    float productPrice = qty * pricePerQty;
    float productTax = productPrice * tax;
    float totalPrice = productPrice + productTax;

    return totalPrice;
  }
}

Problematic Code Usage

public static void main(String[] args) {
  Order order = new Order();
  order.calculateTotalPrice(1, 100, 0.03f);
}

Refactored Code: Move the logic of those temporary variables into a separate method, and simply call that method whenever you need the value.

class Order {
  private int qty;
  private float pricePerQty;
  private float tax;

  float calculateTotalPrice(int qty, float pricePerQty, float tax) {
    this.qty = qty;
    this.pricePerQty = pricePerQty;
    this.tax = tax;

    float totalPrice = productPriceWithTax(qty, pricePerQty, tax);
    return totalPrice;
  }

  float productPriceWithTax(int qty, float price, float tax) {
    return qty * price + (qty * price * tax);
  }
}

Refactored Code Usage

public static void main(String[] args) {
  Order order = new Order();
  order.calculateTotalPrice(1, 100, 0.03f);
}

Introduce Parameter Object

When you find yourself passing the exact same group of parameters repeatedly across multiple methods, you can fix this by creating a dedicated class (an object) to store that data. You then pass this object instead of the long list of individual parameters.

Problematic Code: Repeatedly calling the same set of parameters across several methods.

class Order {
    public void addToCart(String productId, Integer productQty, String userId) {
      ...
    }
    public void updateProductInCart(String productId, Integer productQty, String userId) {
      ...
    }
}

Problematic Code Usage

public static void main(String[] args) {
  Order order = new Order();
  order.addToCart("Beer", 1, "Admin");
  order.updateProductInCart("Beer", 5, "Admin");
}

Refactored Code: Create a new class to store the data, and use it as the method parameter instead of the old parameter list.

class Order {
  public void addToCart(OrderDetailItem item) {
    ...
  }
  public void updateProductInCart(OrderDetailItem item) {
    ...
  }
}

class OrderDetailItem {
  private String productId;
  private Integer productQty;
  private String userId;

  public OrderDetailItem(String productId, Integer productQty, String userId) {
    this.productId = productId;
    this.productQty = productQty;
    this.userId = userId;
  }
}

Refactored Code Usage

public static void main(String[] args) {
  Order order = new Order();

  OrderDetailItem addOrderDetailItem = new OrderDetailItem("Beer", 1, "Admin");
  order.addToCart(addOrderDetailItem);

  OrderDetailItem updateOrderDetailItem = new OrderDetailItem("Beer", 1, "Admin");
  order.updateProductInCart(updateOrderDetailItem);
}

Preserve Whole Object

This occurs when you extract specific values from an object, store them in temporary variables, and then pass those variables into a method. You can fix this by simply passing the entire object.

Problematic Code & Usage: You can see that values are extracted from the userLocation object first, and those temporary variables are passed on for further use.

private UserLocation userLocation = new UserLocation();

...

public static void main(String[] args) {
  City masaraCity = new City();
  
  ...
    
  double latitude = userLocation.getLatitude();
  double longitude = userLocation.getLongitude();

  boolean isCityNearby = masaraCity.nearbyUser(latitude, longitude);
}

class City {
  boolean nearbyUser(double latitude, double longitude) {
    ...
  }
}

class UserLocation {
  private double latitude;
  private double longitude;

  public double getLatitude() {
    return latitude;
  }
  public double getLongitude() {
    return longitude;
  }
}

Refactored Code & Usage: Instead of creating variables to hold the values before passing them, just send the whole object through the parameter.

UserLocation userLocation = new UserLocation();

...

public static void main(String[] args) {
  City masaraCity = new City();
  
  ...

  boolean isCityNearby = masaraCity.nearbyUser(userLocation);
}

class City {
  boolean nearbyUser(UserLocation userLocation) { 
    ...
  }
}

class UserLocation {
  double latitude;
  double longitude;

  public double getLatitude() {
      return latitude;
  }
  public double getLongitude() {
      return longitude;
  }
}

I'll save the remaining three techniques for the next article! :)

Share this article:

Related Articles

Fix -bash: ng: command not found
Coding
Jul 15, 2019

Fix -bash: ng: command not found

When running any ng command, you might encounter the following error message: "ng: command not found".

Read More
gRPC with Spring Boot 101
Coding
Jul 10, 2019

gRPC with Spring Boot 101

gRPC is an RPC (Remote Procedure Call) framework originally developed by Google in 2015 and is now a part of the Cloud Native Computing Foundation (CNCF). It utilizes the HTTP/2 protocol, which is significantly faster than HTTP/1.1, and uses Protocol Buffers (Protobuf) as its Interface Definition Language (IDL) to compile the code needed for development.

Read More
FlutterBuilder
Coding
Jul 03, 2019

FlutterBuilder

Inside the Widget build(BuildContext context) function, you will notice that when rendering a Widget, we cannot directly use async / await or .then() from a Future and wait for the data to arrive before displaying it.

Read More
Code Smells & Code Refactoring
Coding
Jun 15, 2019

Code Smells & Code Refactoring

Has anyone ever written code that ended up tangled like the messy ball of yarn on the left? Have you ever wondered why it gets so tangled, even when we often think we've designed it perfectly? So, what do we need to do to make our code look as neat and beautiful as the spool of thread on the right? In the upcoming articles, I'll be gradually writing more about this for you all to read, haha! ^^y

Read More