architecturesoftware-designabstract-factorycreation-pattern

Abstract Factory Design Pattern in Software Design & Architecture


I am working to understand Abstract Factory design pattern and facing an error in many examples. As in below class diagram:

Abstract Factory Design Pattern

Here in this example Abstract Factory class which must be an interface have the responsibility to create Abstract Product which is Bank and Loan in this example. When we call two methods get_Bank() and get_Loan() to create objects then we have to implement the get_Loan() method in Bank_Factory class and get_Bank() method in Loan_Factory class due to abstraction which breaks the Interface segregation principle. Now many internet examples create class diagram but when they are writing code, they change their code according to class diagram which creates confusion to understand especially for me.

Now I want to take this example but modify or correct this to finish the error that I have seen. I am trying to much but every time I stuck-up in different situation.

class Governmental_Bank implements Bank
{  
    private final String bank_Name;  
    public Governmental_Bank ()
    {  
        this.bank_Name="NBP";  
    }  
    
    @Override
    public String get_Bank_Name() 
    {  
        return this.bank_Name;  
    }     
} 

class PrivateBank implements Bank
{  
    private final String bank_Name;  
    public PrivateBank ()
    {  
        bank_Name = "HBL";  
    }  
    
    @Override
    public String get_Bank_Name() 
    {  
        return bank_Name;  
    }     
}

public interface Bank 
{
    String get_Bank_Name();
}

public class Education_Loan extends Loan
{
    @Override
    public void get_Interest_Rate(double rate)
    {  
        this.rate=rate;  
    }      
}

public class Business_Loan extends Loan
{
    @Override
    public void get_Interest_Rate(double rate)
    {  
        this.rate=rate;  
    }   
}

public class Home_Loan extends Loan
{
    @Override
    public void get_Interest_Rate(double rate)
    {  
        this.rate=rate;  
    }  
}

public abstract class Loan
{  
    protected double rate;  
    abstract void get_Interest_Rate(double rate); 
 
    public void calculateLoanPayment(double loanamount, int years)  
    {           
            double EMI;  
            int n;  
            n=years*12;  
            rate=rate/1200;  
            EMI=((rate*Math.pow((1+rate),n))/((Math.pow((1+rate),n))-1))*loanamount;  
            System.out.println("your monthly EMI is "+ EMI +" for the amount "+loanamount+" you have borrowed");     
    }  
}// end of the Loan abstract class.

class Bank_Factory extends Abstract_Factory
{  
    @Override
    public Bank get_Bank(String bank)
    {  
        if(bank == null)
        {  
            return null;  
        }  
        if(bank.equalsIgnoreCase("GovernmentalBank"))
        {  
            return new Governmental_Bank();  
        } 
        else if(bank.equalsIgnoreCase("PrivateBank"))
        {  
            return new PrivateBank();  
        }
        return null;
    } 
    @Override 
    public Loan get_Loan(String loan) 
    {  
        return null;  
    }  
}//End of the BankFactory class.

class Loan_Factory extends Abstract_Factory
{  
    @Override
    public Bank get_Bank(String bank)
    {  
        return null;  
    }  
    @Override    
    public Loan get_Loan(String loan)
    {  
        if(loan == null)
        {  
            return null;  
        }  
        if(loan.equalsIgnoreCase("Home"))
        {  
            return new Home_Loan();  
        } 
        else if(loan.equalsIgnoreCase("Business"))
        {  
            return new Business_Loan();  
        } 
        else if(loan.equalsIgnoreCase("Education"))
        {  
            return new Education_Loan();  
        }  
    return null;  
    }  
}

public abstract class Abstract_Factory
{  
    public abstract Bank get_Bank(String bank);  
    public abstract Loan get_Loan(String loan);  
}

class Abstract_Factory_Pattern
{  
    public static void main(String args[])
    {  
        Scanner input = new Scanner(System.in);
        System.out.print("Enter Bank name (GovernmentalBank/PrivateBank): ");  
        String bankName = input.nextLine();  
  
        System.out.print("\n");  
        System.out.print("Enter loan type (Home, Business, or Education) : ");  
        String loanName = input.nextLine();  

        Abstract_Factory bankFactory = Factory_Creator.get_Factory("Bank");  
        Bank bank = bankFactory.get_Bank(bankName);  
  
        System.out.print("\n");  
        System.out.print("Enter the interest rate for "+bank.get_Bank_Name()+ ": ");  
        double rate = input.nextDouble();  
        System.out.print("\n");  
        System.out.print("Enter the loan amount you want to take: ");  
        double loanAmount = input.nextDouble(); 
        System.out.print("\n");  
        System.out.print("Enter the number of years to pay your loan amount: ");  
        int years = input.nextInt();  
    
        Abstract_Factory loanFactory = Factory_Creator.get_Factory("Loan");  
        Loan loan=loanFactory.get_Loan(loanName);  
        loan.get_Interest_Rate(rate);  
        loan . calculateLoanPayment ( loanAmount , years) ;  
    }  
}//End of the Abstract Factory Pattern

I have tried too much to apply Interface Segregation Principle on this example but failed, I hope that someone will solve my issue or guide me.


Solution

  • As per wikipedia definition:

    The abstract factory software pattern provides a way to encapsulate a group of individual factories that have a common theme without specifying their concrete classes

    That is not what is happening in your diagram. The choice from Factory_Creator.get_Factory should not determine if the returned factory is a bank or loan one, it should represent some other higher level concept which is concerned about banks and loans. For example country:

    Factory_Creator
      get_Factory(string country):
        if(country == "US")
          return new USBankingSystemFactory();
        if(country == "CAN")
          return new CanadaBankingSystemFactory();
        ....
    
    abstract class Abstract_Factory
        abstract BankFactory get_BankFactory();  
        abstract LoanFactory get_LoanFactory();  
    
    abstract class BankFactory
        public abstract Bank get_Bank(String bank)
    
    abstract class LoanFactory
        public abstract Bank get_Loan(String loan)
    
    USBankingSystemFactory extends Abstract_Factory
        public abstract Bank get_BankFactory() => new USBankFactory(); 
        public abstract Loan get_LoanFactory() => new USLoanFactory();
    ...
    

    In your case there is no reason to introduce the abstract factory, just use two separate factories which are not connected.