QuickBytes: Writing Concise, Extensible Methods
Tags: programming, tutorial, webdev, java
Published: 2024-03-22 17:05:06

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

```java
interface Bank {
    void transferMoney(Long timestamp,
                   BigDecimal amount,
                   String fromAccount,
                   String toAccount,
                   String memo);
}
```
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 

```java
bank.transferMoney(System.currentTimeMillis(), 
              BigDecimal.of("50.00"), 
              "XXX", "YYY", null);
```
which of the parameters is `fromAccount` and which one is `toAccount`? 
3. 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. 
```java
class TransferDetails { 
  final Long timestamp;
  final String fromAccount;
  final String toAccount;
  final String memo;
  //...
}

interface Bank { 
  void transferMoney(TransferDetails details);
}
```
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.
 
```java
final TransferDetails transferDetails = 
  TransferDetails.builder()
    .fromAccount("XXX")
    .toAccount("YYY")
    .timestamp(System.currentTimeMilis())
    .amount(BigDecimal.of("50.00"))
    .build();
bank.transferMoney(transferDetails)
```  

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. 

```java
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);
```

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