0

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.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
ABU-ZHAR
  • 37
  • 6

2 Answers2

1

I think you have to collect the two methods (get_bank() and get_loan()) in the abstractfactory class to something like this:

public abstract class Abstract_Factory
{  
    public abstract SomeSuperClass get_Instance(String bank);
}

this you can achieve the abstraction needed; otherwise, the methods are specific to implementation, which is not an abstraction at all.

then you have to find some superclass between bank and loan.

If you don't have this abstraction, so there is no need to have the abstractfactory class, let BankFactory and LoanFactory implement directly the factory creator; this way you have one method to implement in each factory.

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;
    } 
}

// -- 
class Loan_Factory extends Abstract_Factory
{  
    @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;  
    }  
}

To apply the Interface Segregation Principle, you can follow these steps:

  • Identify large, complex interfaces that may contain methods that are not used by every class that implements them.

  • Split the interface into smaller, more specific ones, each with a clear and well-defined purpose.

  • Implement the new, smaller interfaces in the classes that need them.

  • Refactor the code so that classes only depend on methods they actually use.

  • Continuously evaluate your interfaces and make changes as needed to ensure they follow the Interface Segregation Principle.

More explanation: Let's consider the following example:

interface Shape {
    void draw();
    void resize();
}

class Circle implements Shape {
    public void draw() {
        // code to draw a circle
    }
    public void resize() {
        // code to resize a circle
    }
}

class Square implements Shape {
    public void draw() {
        // code to draw a square
    }
    public void resize() {
        // code to resize a square
    }
}

In this example, Shape interface defines two methods draw and resize. The Circle and Square classes both implement these methods. However, the resize method might not make sense for every type of shape. For example, a line cannot be resized.

To apply the Interface Segregation Principle, we can create a new interface Resizable which only defines the resize method:

interface Shape {
    void draw();
}

interface Resizable {
    void resize();
}

class Circle implements Shape, Resizable {
    public void draw() {
        // code to draw a circle
    }
    public void resize() {
        // code to resize a circle
    }
}

class Square implements Shape, Resizable {
    public void draw() {
        // code to draw a square
    }
    public void resize() {
        // code to resize a square
    }
}

Now, classes that only need the draw method can implement only the Shape interface, and classes that need the resize method can implement the Resizable interface. This way, we've reduced the coupling between classes and made the interfaces more specific to the needs of different classes, making the code easier to maintain and less prone to errors.

By following the Interface Segregation Principle, we've created more specific and smaller interfaces, which in turn leads to a more flexible and scalable architecture.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Moussa ELaQ
  • 119
  • 5
1

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.

Guru Stron
  • 102,774
  • 10
  • 95
  • 132