DEV Community

RyTheTurtle
RyTheTurtle

Posted on

QuickBytes: Writing Concise, Extensible Methods

Creating short, extensible method signatures is a great way to build maintainable, readable code. Having too many parameters to a method is a common mistake people make when defining method signatures that hurts readability and extensibility.

Consider the following method for transferring money between accounts

interface Bank {
    void transferMoney(Long timestamp,
                   BigDecimal amount,
                   String fromAccount,
                   String toAccount,
                   String memo);
}
Enter fullscreen mode Exit fullscreen mode
  1. the method has five input parameters, which makes each invocation difficult to read and easy to exceed line lengths
  2. it's easy to mix up the consecutive parameters of the same type. Looking at
bank.transferMoney(System.currentTimeMillis(), 
              BigDecimal.of("50.00"), 
              "XXX", "YYY", null);
Enter fullscreen mode Exit fullscreen mode

which of the parameters is fromAccount and which one is toAccount?

  1. Extending this method is difficult. Let's say we need to send an email notification upon successful transfer of the money. With this current interface, we can
    1. add another string parameter emailAddress, but then we will be forced to update every place our transferMoney function is called to include the new parameter. This is a breaking API change that forces callers to pass an extra parameter. The best case is that this interface is only used in our own code and we just have to go update every invocation of transferMoney in our own code. If this is code consumed by external callers, we have bigger problems.
    2. add a new method for transferring money that has an extra parameter for emailAddress. Not a big deal, but the more we add additional parameters or functionality, the more the interface is polluted with many variations of essentially the same operation.

We can improve this by bundling up the parameters in to an object.

class TransferDetails { 
  final Long timestamp;
  final String fromAccount;
  final String toAccount;
  final String memo;
  //...
}

interface Bank { 
  void transferMoney(TransferDetails details);
}
Enter fullscreen mode Exit fullscreen mode

This improved method signature is easier to read and extend. Combined with a builder pattern, parameters are easily understood and labeled even when we have many parameters of similar types.

final TransferDetails transferDetails = 
  TransferDetails.builder()
    .fromAccount("XXX")
    .toAccount("YYY")
    .timestamp(System.currentTimeMilis())
    .amount(BigDecimal.of("50.00"))
    .build();
bank.transferMoney(transferDetails)
Enter fullscreen mode Exit fullscreen mode

Additionally, adding a new parameter to the method does not require breaking changes or overloading our method signature. We can simply add the new field to the input object and callers are free to add or ignore the new field without breaking existing invocations of the transferMoney method.

class TransferDetails { 
  final Long timestamp;
  final String fromAccount;
  final String toAccount;
  final String memo;
  final String emailAddress;
  //...
}

interface Bank { 
  void transferMoney(TransferDetails details);
}

//...
// our previous invocation still works 
final TransferDetails transferDetails = 
  TransferDetails.builder()
    .fromAccount("XXX")
    .toAccount("YYY")
    .timestamp(System.currentTimeMilis())
    .amount(BigDecimal.of("50.00"))
    .build();
bank.transferMoney(transferDetails)

// but we can also start sending in email address 
final TransferDetails transferDetails = 
  TransferDetails.builder()
    .fromAccount("XXX")
    .toAccount("YYY")
    .timestamp(System.currentTimeMilis())
    .amount(BigDecimal.of("50.00"))
    .emailAddress("someone@gmail.com") 
    .build();
bank.transferMoney(transferDetails);
Enter fullscreen mode Exit fullscreen mode

Use parameter objects as a simple way to make your methods easier to read and extend. Happy coding :)

Top comments (0)