Adding a withdraw command

Let’s add a command for withdrawing funds from your account:

final class WithdrawCommand extends BigDecimalCommand {
  ...

  @Inject
  WithdrawCommand(Outputter outputter, Account account) {  ...  }

  @Override
  public void handleAmount(BigDecimal amount) {
    BigDecimal newBalance = account.balance().subtract(amount);
    if (newBalance.signum() < 0) {
      // output error
      return;
    } else {
      account.withdraw(amount);
      outputter.output("your new balance is: " + account.balance());
    }
  }
}

And add it to our UserCommandsModule, since this is another command that needs a user to be logged in.

Now, let’s say we want to make some of the values configurable:

There are various ways you could do this, but for this tutorial, we want to request each of those values in WithdrawCommand’s constructor.

Let’s add parameters for the two values to the command’s constructor and update the withdrawal logic to check them:

final class WithdrawCommand extends BigDecimalCommand {
  ...

  @Inject
  WithdrawCommand(
    Outputter outputter,
    Account account,
    BigDecimal minimumBalance,
    BigDecimal maximumWithdrawal) {
    ...
  }

  @Override
  public void handleAmount(BigDecimal amount) {
    if (amount.compareTo(maximumWithdrawal) > 0) {
      // output error
      return;
    }

    BigDecimal newBalance = account.balance().subtract(amount);
    if (newBalance.compareTo(minimumBalance) < 0) {
      // output error
    } else {
      account.withdraw(amount);
      outputter.output("your new balance is: " + account.balance());
    }
  }
}

Now let’s create a new module with @Provides methods for the two values:

@Module
interface AmountsModule {
  @Provides
  static BigDecimal minimumBalance() {
    return BigDecimal.ZERO;
  }

  @Provides
  static BigDecimal maximumWithdrawal() {
    return new BigDecimal(1000);
  }
}

And add the module to our CommandProcessorFactory component:

@Component(modules = {..., AmountsModule.class})
interface CommandProcessorFactory {
  ...
}

If you try to compile now, you’ll find that Dagger raises an error: there are two binding methods for BigDecimal and it doesn’t know which it should use.

To differentiate between different things of the same Java type in a case like this, we use qualifiers. A qualifier is an annotation that can be used to give Dagger information it can use to distinguish between instances of the same type. Qualifiers are annotations that are themselves annotated with @Qualifier. Here’s how we might define annotations for qualifying our BigDecimal values:

@Qualifier
@Retention(RUNTIME)
@interface MinimumBalance {}

// @MaximumWithdrawal defined the same way

And here’s how we’ll apply them in our module:

@Module
interface AmountsModule {
  @Provides
  @MinimumBalance
  static BigDecimal minimumBalance() {
    return BigDecimal.ZERO;
  }

  @Provides
  @MaximumWithdrawal
  static BigDecimal maximumWithdrawal() {
    return new BigDecimal(1000);
  }
}

Now we can update WithdrawCommand’s constructor to depend on the qualified BigDecimals rather than just plain BigDecimal.

final class WithdrawCommand implements Command {
  ...

  @Inject
  WithdrawCommand(
    Outputter outputter,
    Account account,
    @MinimumBalance BigDecimal minimumBalance,
    @MaximumWithdrawal BigDecimal maximumWithdrawal) { ... }

  ...
}

The code should compile again and we can make changes in AmountsModule to adjust the minimum balance and maximum withdrawal amounts if desired.

CONCEPTS

  • @Qualifier annotations are used to differentiate between instances of the same type that are unrelated.
    • Contrast this with @IntoSet and @IntoMap, where the collected objects are used together.
  • Qualifiers are often, but certainly not always, used with common data types such as primitive types and String, which may be used in many places in a program for very different reasons.

Previous · Next