Strategy - Design Pattern in Java

In Strategy pattern, we create objects which represent various strategies and a context object whose behavior varies as per its strategy object.

This allows us to change behavior of a code or an algorithm during run time. 

 

UML diagram

The strategies are implemented in classes that implement an interface (here: "Overdraw"), which defines what methods a strategy should have. The existing application can be extended by adding instance variable with the type of the interface "Overdraw".

 

Code

This application manages accounts with the type "prepaid account" and "credit account". "Prepaid account" cannot be overdrawn, that is why it should use the class "NotOverdrawable" as a "Overdraw" object. "Credit account" can be overdrawn, that it is why it uses the class "Overdrawable" as a "Overdraw" object.

 

Base Application

This is the application where we will use our strategies, that are created in the next section. 

public class Account {

    protected  Integer accountId;
    protected String accountType;
    protected Integer balance;
    public Overdraw overdrawAbility;

    public Account() {
        
    }

    public Account(Integer accountId, String accountType, Integer balance) {
        this.accountId = accountId;
        this.accountType = accountType;
        this.balance = balance;
    }
    
    public String doOverdraw(Integer amount, Integer currentBalance) {
        return this.overdrawAbility.overdrawAccount(amount, currentBalance);
    }

    public Integer getAccountId() {
        return accountId;
    }

    public void setAccountId(Integer accountId) {
        this.accountId = accountId;
    }

    public String getAccountType() {
        return accountType;
    }

    public void setAccountType(String accountType) {
        this.accountType = accountType;
    }

    public Integer getBalance() {
        return balance;
    }

    public void setBalance(Integer balance) {
        this.balance = balance;
    }

    public Overdraw getOverdrawAbility() {
        return overdrawAbility;
    }

    public void setOverdrawAbility(Overdraw overdrawAbility) {
        this.overdrawAbility = overdrawAbility;
    }
public class CreditAccount extends Account {

    public CreditAccount(Integer accountId, Integer balance) {
        super(accountId, "Credit Account", balance);
        super.overdrawAbility = new Overdrawable();
    }

    public String changeInterests(double interest) {
        if (interest < 0.01) {
            return "Interest value must be bigger than 0.01";
        }
        if (interest < 2.5) {
            return "Interest changed to the value: " + interest;
        } else {
            return "Max. interest value for your account type is " + interest + ". Interest value where changed to this max. values.";
        }
    }
}
public class PrepaidAccount extends Account {

    public PrepaidAccount(Integer accountId, Integer balance) {
        super(accountId, "Prepaid Account", balance);
        super.overdrawAbility = new NotOverdrawable();
    }

    public String topUp(Integer value) {
        if (value <= 0) {
            return "Recharge failed. Value must be bigger than 0.";
        }
        if (value < 1000) {
            return "OK. Recharge sucessful. Amount " + value + " was added.";
        } else {
            return "Recharge failed. Max. Recharge value is " + 1000;
        }
    }

}
Extensions of the application

We implement here the strategy functionalities. 

public interface Overdraw {
    String overdrawAccount(Integer amount, Integer balance);
}
public class Overdrawable implements Overdraw{

    @Override
    public String overdrawAccount(Integer amount, Integer balance) {
        return "Now. You have overdrawn an amount of " + amount + ". Your balance: " + (balance - amount);
    }
    
}
public class NotOverdrawable implements Overdraw {

    @Override
    public String overdrawAccount(Integer amount, Integer balance) {
        if (amount > balance) {
            return "Account cannot be overdrawn!";
        } else {
            return "You have withdrawn an amount of " + amount + ". Your balance: " + (balance - amount);
        }
    }

}

 

We can then use our created strategies. This is an example on how you can do that. 

public class AccountPatternExample {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        Account prepaidAccount = new PrepaidAccount(1, 4944);
        Account creditAccount = new CreditAccount(2, 4303);
        System.out.println("Prepaid-Konto: " + prepaidAccount.doOverdraw(66666, prepaidAccount.getBalance()));
        System.out.println("Kredit-Konto: " + creditAccount.doOverdraw(1500, 200));
        creditAccount.setOverdrawAbility(new NotOverdrawable());
        System.out.println("Kredit-Konto: " + creditAccount.doOverdraw(1500, 200));
    }

}

If you change the referenced class of the instance variable "OverdrawAbility" in the class object "creditAccount", then this will change the behavior of the method "doOverdraw". 

 

Application on Github:

https://github.com/a-dridi/strategy_design_pattern_example