Visitor Design Pattern Continue

Visitor Design Pattern Cont..

Here in displayDetails method we can see many if else conditions and if we have more classes for different - different Account then these if else conditions will grow vertically. Each time we add some functionalities for these accounts classes, each time we need to write these many conditions and type checking logic which is not good.

So one solution can be suggested here is move this displayDetails method in Account class as abstract method without any argument, and let all derived classes implement/override this method.
Then we can simply call displyDetails method for each account as following

                   Account account = new CurrentAccount();
                   account.displayDetails();
                   //same way
                   account = LoanAccount();
                  account.displayDetails()

So far so good! But in this approach the problem is, if we need to display some other details or let say we want to display different Account opening Forms for different Account type. Then we will be either adding displayForm(Account account) method with if else conditions or we will be ending up adding one more method in the Account classes.

Adding methods in Account classes is not good and not advised, as Account class is just for representing data/information and should be used as datatype only. There should not be any business logic in any Account class.
Solution: This problem can be solved easily by applying Visitor pattern. 
  • Visitor pattern helps us in removing all if else conditions for hierarchy checking (from DisplayHandler class).
  • Visitor pattern helps us in keeping business logic separate from hierarchy classes  (from Account classes).
  • Visitor patterns helps us adding more functionality without changing  hierarchy classes  (Account classes).
Let see how we can remove if else conditions by applying visitor pattern here.

Step1. Create an interface as following
Visitor.java

public interface Visitor {
       //overloaded methods for each Account class in hierarchy.
void visit(CurrentAccount account);
void visit(SavingAccount account);
void visit(LoanAccount account);
}

Step2. Create a visitor implementation for display account details as following
DisplayAccountVisitorImpl.java

public class DisplayAccountVisitorImpl implements Visitor {
//display current account details
public void visit(CurrentAccount account) {
System.out.println("Account type: " + account.getAccountType());
System.out.println("Account Balance: " + account.getBalance());
System.out.println("Withdrawal Limit: " + account.getWithdrawalLimit());
}
//display saving account details
public void visit(SavingAccount account) {
System.out.println("Account type: " + account.getAccountType());
System.out.println("Account Balance: " + account.getBalance());
}
//display loan account details
public void visit(LoanAccount account) {
System.out.println("Account type: " + account.getAccountType());
System.out.println("Account Balance: " + account.getBalance());
System.out.println("Loanamount: " + account.getLoanAmount());
}
}

Step3. Create an interface as following

public interface AcceptVisitor{
       void accept(Visitor visitor);
}

Step4. Let all account classes implement this interface. or we can avoid step3 by adding an abstract method accept and let all account classes implement it. its up tu you what you want to choose.
Lets update the Account classes.

public abstract class Account implements AcceptVisitor {
protected int balance = 0;
public abstract String getAccountType();
public int getBalance() {
return balance;
}
abstract void accept(Visitor visitor); 
     // you can remove it from here and let derived classes implement the AcceptVisitor interface.
}

public class CurrentAccount extends Account {
private final int maxWithdrawalAmount = 10000;
public CurrentAccount() {
balance = 500;// basic amount to open an account
}
@Override
public String getAccountType() {
return "Current";
}
public int getWithdrawalLimit() {
return maxWithdrawalAmount;
}
public void accept(Visitor visitor) {
visitor.visit(this);
        }
}

public class SavingAccount extends Account {
public SavingAccount() {
balance = 1000;// basic amount to open an account
}
@Override
public String getAccountType() {
return "Saving";
}
public void accept(Visitor visitor) {
visitor.visit(this);
        }
}

public class LoanAccount extends Account {
private int paidAmount = 0;
private int loanAmount = 0;
public LoanAccount() {
loanAmount = 100000;// basic amount for a loan account
}
@Override
public String getAccountType() {
return "Loan";
}
public int getLoanAmount() {
return loanAmount;
}
@Override
public int getBalance() {
return balance = loanAmount - paidAmount;
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
}

Step4. Update DisplayHandler class as following

 public class DisplayHandler {
//display the account details
public void displayDetails(Account account) {
DisplayAccountVisitorImpl visitor = new DisplayAccountVisitorImpl();
account.accept(visitor);
}
 }

Your implementation is complete now, you can see there are no if else conditions or type checking.


How to test: To test the functionality we can have our main class as following
VisitorPatternTest.java

 public class VisitorPatternTest {
public static void main(String[] args) {
DisplayHandler example = new DisplayHandler();
//display current account details
Account account = new CurrentAccount();
example.displayDetails(account);
//display saving account details
account = new SavingAccount();
example.displayDetails(account);
//display loan account details
account = new LoanAccount();
example.displayDetails(account);
}
 }

Output:

Account type: Current
Account Balance: 500
Withdrawal Limit: 10000
Account type: Saving
Account Balance: 1000
Account type: Loan
Account Balance: 100000
Loan Amount: 100000

Now suppose we want to add some more functionality to display different Account opening Forms as discussed above before Solution. to achieve this we need to add a displayForms(Account account) method in DisplayHandler class, no need to add if else conditions just do as following.

 public class DisplayHandler {
//display the account details
public void displayDetails(Account account) {
DisplayAccountVisitorImpl visitor = new DisplayAccountVisitorImpl();
account.accept(visitor);
}
            //display the forms details
public void displayForm(Account account) {
DisplayFormVisitorImpl visitor = new DisplayFormVisitorImpl();
account.accept(visitor);
}
 }

After creating above class now create a DisplayFormVisitorImpl.java class similar to DisplayAccountVisitorImpl.java by implementing visitor interface and add display forms logic in each of overloaded methods.

This way we can avoid adding if else conditions and modifying Account classes each time a new functionality is added. 

No comments :

Post a Comment