DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Enabling Behavior-Driven Service Discovery: A Lightweight Approach to Augment Java Factory Design Pattern
  • Builder Design Pattern in Java
  • Java 8/9 Best Practices — Part-1 (Static Factory Methods)
  • Using Python Libraries in Java

Trending

  • Integration Isn’t a Task — It’s an Architectural Discipline
  • Beyond ChatGPT, AI Reasoning 2.0: Engineering AI Models With Human-Like Reasoning
  • Why High-Performance AI/ML Is Essential in Modern Cybersecurity
  • A Deep Dive Into Firmware Over the Air for IoT Devices
  1. DZone
  2. Coding
  3. Java
  4. Abstract Factory Pattern In Java

Abstract Factory Pattern In Java

By 
Brijesh Saxena user avatar
Brijesh Saxena
DZone Core CORE ·
Oct. 05, 20 · Tutorial
Likes (8)
Comment
Save
Tweet
Share
11.2K Views

Join the DZone community and get the full member experience.

Join For Free

In the continuation of my previous article (Strategy vs. Factory Design Patterns in Java), I am here with an article on Abstract Factory Pattern. In this article, I will try to highlight the usage of the pattern along with the difference with the Factory Pattern.

Abstract Factory Pattern

  • The abstract factory pattern is a creational pattern which provides a way to encapsulate a group of individual factories that have a common purpose or theme; without specifying their concrete classes.
  • The Abstract Factory patterns work around a super-factory which creates other factories.
  • This super-factory is also known as a factory of factories.
  • The Abstract Factory pattern is responsible for creating a factory of related objects without explicitly specifying their classes. 
  • Each generated factory can give the objects as per the Factory pattern.
  • So in short, Abstract factory provides an interface to create different concrete factories. Each such factory works with a family or group objects. and hence Abstract factory provides a way to work with multiple such factories. So, we can also call it as factory of factories.

Abstract factory UML diagram

As we can see in the above class diagram, there is a car factory which is in business for ages. So it has an old car factory called ClassicCarFactory. Later, the company started manufacturing some new modern cars and hence started another factory called ModernCarFactory. So, for the that, we used AbtractCarFactory as Abstract Factory Design Pattern. In that way, we will make sure to have no or minimum changes on the client end. 

Suppose by the success in the modern cars, the company decided to also start manufacturing the sports cars and started another factory for that, the design will be as below by using the same pattern.

Abstract factory example UML diagram

So, the Abstract Factory design pattern solves problems like: 

  • How can an application be independent of how its objects are created?
  • How can a class be independent of how the objects it requires are created?
  • How can families of related or dependent objects be created?

To make it easy to understand and to highlight similarities and differences with Factory Pattern, I am using the same example of Account Management what I took in the article Strategy vs. Factory Design Patterns in Java.

In the example, I only did some re-structuring/packaging to make code more cleaner and easy to understand. May be its not best example to list here. But my intention is to show the implementation and use of the Abstract Factory and not any other accounting system. So, please excuse if it does not look good to you.

Account-Management Example Using the Abstract Factory Pattern

We have the AccountType interface to define account-types with the pre-defined or fixed rate of interest.  Here, I have added more account types to make use or more Factory Classes.

Java
xxxxxxxxxx
1
18
 
1
package org.trishinfotech.abstractfactory.acct;
2
3
public enum AccountType {
4
5
    SAVING(2.0d), CURRENT(1.0d), HOME_LOAN(3.0d), VEHICLE_LOAN(4.0d), PERSONAL_LOAN(5.0d), RECURRING_DEPOSIT(2.5d), FIXED_DEPOSIT(3.5d);
6
7
    private double rate;
8
9
    AccountType(final double rate) {
10
        this.rate = rate;
11
    }
12
13
    public double getRate() {
14
        return rate;
15
    }
16
17
}


I am implementing an abstract base class for the account and creating the various flavors of Account by sub-classing it.

Below is the code for the  Account. Please note that I have defined the class as abstract to force the sub-classing of it.

Java
xxxxxxxxxx
1
90
 
1
package org.trishinfotech.abstractfactory.acct;
2
3
public abstract class Account {
4
5
    protected long accountNo;
6
    protected String accountHolderName;
7
    protected AccountType accountType;
8
    protected InterestStrategy interestStrategy;
9
    protected double amount;
10
11
    public Account() {
12
        super();
13
    }
14
15
    public Account(long accountNo, String accountHolderName, AccountType accountType) {
16
        this();
17
        this.accountNo = accountNo;
18
        this.accountHolderName = accountHolderName;
19
        this.accountType = accountType;
20
    }
21
22
    public long getAccountNo() {
23
        return accountNo;
24
    }
25
26
    public void setAccountNo(long accountNo) {
27
        this.accountNo = accountNo;
28
    }
29
30
    public String getAccountHolderName() {
31
        return accountHolderName;
32
    }
33
34
    public void setAccountHolderName(String accountHolderName) {
35
        this.accountHolderName = accountHolderName;
36
    }
37
38
    public AccountType getAccountType() {
39
        return accountType;
40
    }
41
42
    public void setAccountType(AccountType accountType) {
43
        this.accountType = accountType;
44
    }
45
46
    public InterestStrategy getInterestStrategy() {
47
        return interestStrategy;
48
    }
49
50
    public void setInterestStrategy(InterestStrategy interestStrategy) {
51
        this.interestStrategy = interestStrategy;
52
    }
53
54
    public double getAmount() {
55
        return amount;
56
    }
57
58
    public void setAmount(double amount) {
59
        this.amount = amount;
60
    }
61
62
    public void deposit(double amount) {
63
        // check for only positive/valid amount
64
        if (amount > 0.0d) {
65
            this.amount += amount;
66
        }
67
    }
68
69
    public void withdraw(double amount) {
70
        // check for only positive/valid amount and also for below than the available
71
        // amount in account
72
        if (amount > 0.0d && amount < this.amount) {
73
            this.amount -= amount;
74
        }
75
    }
76
77
    public abstract double getInterest(int term);
78
79
    @Override
80
    public String toString() {
81
        StringBuilder builder = new StringBuilder();
82
        builder.append("Account [accountNo=").append(getAccountNo()).append(", accountHolderName=")
83
                .append(getAccountHolderName()).append(", accountType=").append(getAccountType()).append(", rate=")
84
                .append((getAccountType() != null) ? getAccountType().getRate() : 0.0d).append(", interestStrategy=")
85
                .append(getInterestStrategy().getName()).append(", amount=").append(getAmount()).append("]");
86
        return builder.toString();
87
    }
88
89
}


In this example, since the we are gonna create many sub-types of Account class, I am writing a new enum InterestStrategy to provide interest calculation algorithms.

Java
xxxxxxxxxx
1
37
 
1
package org.trishinfotech.abstractfactory.acct;
2
3
public enum InterestStrategy {
4
5
    SIMPLE ("Simple Interest") {
6
        @Override
7
        public double getInterest(AccountType accountType, double amount, int term) {
8
            if (accountType != null) {
9
                return ((amount * term * accountType.getRate()) / 100);
10
            }
11
            return 0.0d;
12
        }
13
    },
14
    COMPOUND ("Compound Interest") {
15
        @Override
16
        public double getInterest(AccountType accountType, double amount, int term) {
17
            if (accountType != null) {
18
                return amount * Math.pow(1.0 + accountType.getRate() / 100.0, term) - amount;
19
            }
20
            return 0.0d;
21
        }
22
    };
23
    
24
    private String name;
25
    
26
    InterestStrategy(String name) {
27
        this.name = name;
28
    }
29
    
30
    public abstract double getInterest(AccountType accountType, double amount, int term);
31
32
    public String getName() {
33
        return name;
34
    }
35
    
36
}


Now, we have to create the types for the account. I create the SavingAccount, which is tied up with the compound interest algorithm.

Java
xxxxxxxxxx
1
20
 
1
package org.trishinfotech.abstractfactory.acct.saving;
2
3
import org.trishinfotech.abstractfactory.acct.Account;
4
import org.trishinfotech.abstractfactory.acct.AccountType;
5
import org.trishinfotech.abstractfactory.acct.InterestStrategy;
6
7
public class SavingAccount extends Account {
8
9
    public SavingAccount(long accountNo, String accountHolderName) {
10
        super(accountNo, accountHolderName, AccountType.SAVING);
11
        setInterestStrategy(InterestStrategy.COMPOUND);
12
    }
13
14
    @Override
15
    public double getInterest(int term) {
16
        return this.getInterestStrategy().getInterest(accountType, amount, term);
17
    }
18
19
}


You can compare the above code of SavingAccount  with the code in the article  Strategy vs. Factory Design Patterns in Java to understand the use of enum. And to understand the code written inside of enum you may refer to my article Java Enums: How to Make Enums More Useful.

Next, I created the CurrentAccount, which is tied up with the simple-interest algorithm.

Java
xxxxxxxxxx
1
20
 
1
package org.trishinfotech.abstractfactory.acct.saving;
2
3
import org.trishinfotech.abstractfactory.acct.Account;
4
import org.trishinfotech.abstractfactory.acct.AccountType;
5
import org.trishinfotech.abstractfactory.acct.InterestStrategy;
6
7
public class CurrentAccount extends Account {
8
9
    public CurrentAccount(long accountNo, String accountHolderName) {
10
        super(accountNo, accountHolderName, AccountType.CURRENT);
11
        setInterestStrategy(InterestStrategy.SIMPLE);
12
    }
13
14
    @Override
15
    public double getInterest(int term) {
16
        return this.getInterestStrategy().getInterest(accountType, amount, term);
17
    }
18
19
}


So, basically, the types of accounts not only have pre-defined rates (as in the strategy) but also the pre-defined interest calculation algorithms, which are tightly coupled. Therefore, the account instance will have defined functionality.

Because the above two accounts SavingAccount and CurrentAccount are type of saving, I put them into a sub-package called saving. This is something I did just to organize the code and there is no relation of this with the design pattern I am discussing here.

Now, I will write more concrete sub-classes of Account to represent each of the AccountType we have created.

Below is the code of LoanAccount , which is an abstract class and a sub-type of Account. I made it abstract just to have different flavors of loan-types like Home Loan, Vehicle Loan or Personal Loan.

Java
xxxxxxxxxx
1
36
 
1
package org.trishinfotech.abstractfactory.acct.loan;
2
3
import org.trishinfotech.abstractfactory.acct.Account;
4
import org.trishinfotech.abstractfactory.acct.AccountType;
5
6
public abstract class LoanAccount extends Account {
7
8
    protected int termInMonths;
9
10
    public LoanAccount(long accountNo, String accountHolderName, AccountType accountType, int termInMonths) {
11
        super(accountNo, accountHolderName, accountType);
12
        this.termInMonths = termInMonths;
13
    }
14
15
    // since its a loan account, reversing the withdraw and deposit to make it responsibility
16
    // just to keep the example as simple as possible
17
    @Override
18
    public void deposit(double amount) {
19
        super.withdraw(amount);
20
    }
21
22
    @Override
23
    public void withdraw(double amount) {
24
        super.deposit(amount);
25
    }
26
27
    public int getTermInMonths() {
28
        return termInMonths;
29
    }
30
31
    public void setTermInMonths(int termInMonths) {
32
        this.termInMonths = termInMonths;
33
    }
34
    
35
}


So with LoanAccount sub-class, I placed all of the common code I needed to operate any kind of loan account like any loan requires a term to clear. Also, because I like to avoid the much of the coding, I reversed the deposit and withdraw with respect to amount. I mean deposit in loan means clearing the amount and withdraw from loan means a kind of disbursement of the amount to the builder of the home or the vehicle supplier.

To organize the loan related code, I use loan as a sub-package.

Now I created HomeLoanAccount, which is tied up with the compound-interest algorithm. 

Java
xxxxxxxxxx
1
19
 
1
package org.trishinfotech.abstractfactory.acct.loan;
2
3
import org.trishinfotech.abstractfactory.acct.AccountType;
4
import org.trishinfotech.abstractfactory.acct.InterestStrategy;
5
6
public class HomeLoanAccount extends LoanAccount {
7
8
    public HomeLoanAccount(long accountNo, String accountHolderName, int termInMonths) {
9
        super(accountNo, accountHolderName, AccountType.HOME_LOAN, termInMonths);
10
        setInterestStrategy(InterestStrategy.COMPOUND);
11
    }
12
13
    @Override
14
    public double getInterest(int term) {
15
        return this.getInterestStrategy().getInterest(accountType, amount, term);
16
    }
17
18
}


Next I created VehicleLoanAccount, which is tied up with the compound-interest algorithm.

Java
xxxxxxxxxx
1
19
 
1
package org.trishinfotech.abstractfactory.acct.loan;
2
3
import org.trishinfotech.abstractfactory.acct.AccountType;
4
import org.trishinfotech.abstractfactory.acct.InterestStrategy;
5
6
public class VehicleLoanAccount extends LoanAccount {
7
8
    public VehicleLoanAccount(long accountNo, String accountHolderName, int termInMonths) {
9
        super(accountNo, accountHolderName, AccountType.VEHICLE_LOAN, termInMonths);
10
        setInterestStrategy(InterestStrategy.COMPOUND);
11
    }
12
13
    @Override
14
    public double getInterest(int term) {
15
        return this.getInterestStrategy().getInterest(accountType, amount, term);
16
    }
17
    
18
}


Next I created PersonalLoanAccount, which is tied up with the compound-interest algorithm.

Java
xxxxxxxxxx
1
19
 
1
package org.trishinfotech.abstractfactory.acct.loan;
2
3
import org.trishinfotech.abstractfactory.acct.AccountType;
4
import org.trishinfotech.abstractfactory.acct.InterestStrategy;
5
6
public class PersonalLoanAccount extends LoanAccount {
7
8
    public PersonalLoanAccount(long accountNo, String accountHolderName, int termInMonths) {
9
        super(accountNo, accountHolderName, AccountType.PERSONAL_LOAN, termInMonths);
10
        setInterestStrategy(InterestStrategy.COMPOUND);
11
    }
12
13
    @Override
14
    public double getInterest(int term) {
15
        return this.getInterestStrategy().getInterest(accountType, amount, term);
16
    }
17
18
}


Similarly for deposit type of account, I use sub-package called deposit to organize the code. 

Below is the code of DepositAccount where I placed all of the common code I needed to operate any kind of deposit account like there is restriction on  amount and number of withdraw and deposit.  

Java
xxxxxxxxxx
1
35
 
1
package org.trishinfotech.abstractfactory.acct.deposit;
2
3
import org.trishinfotech.abstractfactory.acct.Account;
4
import org.trishinfotech.abstractfactory.acct.AccountType;
5
6
public abstract class DepositAccount extends Account {
7
8
    protected int termInMonths;
9
    private boolean withdrawDone;
10
    
11
    public DepositAccount(long accountNo, String accountHolderName, AccountType accountType, int termInMonths) {
12
        super(accountNo, accountHolderName, accountType);
13
        this.termInMonths = termInMonths;
14
    }
15
16
    public int getTermInMonths() {
17
        return termInMonths;
18
    }
19
20
    public void setTermInMonths(int termInMonths) {
21
        this.termInMonths = termInMonths;
22
    }
23
24
    @Override
25
    public void withdraw(double amount) {
26
        // ignore the given amount since we can withdraw whole amount at the maturity
27
        if (!withdrawDone && this.amount > 0.0d) {
28
            this.amount = 0.0d;
29
            withdrawDone = true;
30
        } else {
31
            System.out.println("Sorry! We can withdraw whole amount only once from a Deposit Account!");
32
        }
33
    }
34
}


Next I created FixedDepositAccount, which is tied up with the simple-interest algorithm.

Java
xxxxxxxxxx
1
30
 
1
package org.trishinfotech.abstractfactory.acct.deposit;
2
3
import org.trishinfotech.abstractfactory.acct.AccountType;
4
import org.trishinfotech.abstractfactory.acct.InterestStrategy;
5
6
public class FixedDepositAccount extends DepositAccount {
7
8
    private boolean depositDone;
9
    public FixedDepositAccount(long accountNo, String accountHolderName, int termInMonths) {
10
        super(accountNo, accountHolderName, AccountType.FIXED_DEPOSIT, termInMonths);
11
        setInterestStrategy(InterestStrategy.SIMPLE);
12
    }
13
14
    @Override
15
    public double getInterest(int term) {
16
        return this.getInterestStrategy().getInterest(accountType, amount, term);
17
    }
18
19
    @Override
20
    public void deposit(double amount) {
21
        if (!depositDone && amount > 0.0d) {
22
            this.amount = amount;
23
            depositDone = true;
24
        } else {
25
            System.out.println("Sorry! We can deposit only once in a Fixed Deposit Account!");
26
        }
27
    }
28
    
29
}


Next I created RecurringDepositAccount, which is tied up with the compound-interest algorithm.

Java
xxxxxxxxxx
1
19
 
1
package org.trishinfotech.abstractfactory.acct.deposit;
2
3
import org.trishinfotech.abstractfactory.acct.AccountType;
4
import org.trishinfotech.abstractfactory.acct.InterestStrategy;
5
6
public class RecurringDepositAccount extends DepositAccount {
7
8
    public RecurringDepositAccount(long accountNo, String accountHolderName, int termInMonths) {
9
        super(accountNo, accountHolderName, AccountType.RECURRING_DEPOSIT, termInMonths);
10
        setInterestStrategy(InterestStrategy.COMPOUND);
11
    }
12
13
    @Override
14
    public double getInterest(int term) {
15
        return this.getInterestStrategy().getInterest(accountType, amount, term);
16
    }
17
18
}


So now we are ready with all the flavors of account we are gonna support in the application.

As we have noticed that we have 3 categories of Account sub-types as below:

  • Saving (in package saving)
    • SavingAccount
    • CurrentAccount
  • Loan (in package loan)
    • LoanAccount
      • HomeLoanAccount
      • VehicleLoanAccount
      • PersonalLoanAccount
  • Deposit (in package deposit)
    • DepositAccount
      • FixedDepositAccount
      • RecurringDepositAccount

Now, its time to do Step 1 to implement Abstract Factory for the design pattern we are discussing. We can create AbstractFactory as interface (to server multiple inheritence in java) or as abstract class. Both approach are OK. 

Step 1: Here I am creating it as abstract class AbstractAccountFactory:

Java
xxxxxxxxxx
1
11
 
1
package org.trishinfotech.abstractfactory;
2
3
import org.trishinfotech.abstractfactory.acct.Account;
4
import org.trishinfotech.abstractfactory.acct.AccountOpeningDetails;
5
6
public abstract class AbstractAccountFactory {
7
    
8
    public abstract Account createAccount(AccountOpeningDetails opendingDetails);
9
    
10
}


Please note that the abstract method inside factory class returns the highest-level of the account class/interface to show that all the concrete implementation of factory will have a same purpose or theme. Here, I also changed multiple parameters to a single POJO class called AccountOpendingDetails. This is to avoid lengthy parameter list as well as validating parameter values

Java
xxxxxxxxxx
1
60
 
1
package org.trishinfotech.abstractfactory.acct;
2
3
public class AccountOpeningDetails {
4
    private long accountNo;
5
    private String accountHolderName;
6
    private AccountType accountType;
7
    private int termInMonths;
8
9
    
10
    public AccountOpeningDetails(long accountNo, String accountHolderName, AccountType accountType) {
11
        super();
12
        this.accountNo = accountNo;
13
        this.accountHolderName = accountHolderName;
14
        this.accountType = accountType;
15
    }
16
17
    public AccountOpeningDetails(long accountNo, String accountHolderName, AccountType accountType, int termInMonths) {
18
        this(accountNo, accountHolderName, accountType);
19
        this.termInMonths = termInMonths;
20
    }
21
22
    public long getAccountNo() {
23
        return accountNo;
24
    }
25
26
    public void setAccountNo(long accountNo) {
27
        this.accountNo = accountNo;
28
    }
29
30
    public String getAccountHolderName() {
31
        return accountHolderName;
32
    }
33
34
    public void setAccountHolderName(String accountHolderName) {
35
        this.accountHolderName = accountHolderName;
36
    }
37
38
    public AccountType getAccountType() {
39
        return accountType;
40
    }
41
42
    public void setAccountType(AccountType accountType) {
43
        this.accountType = accountType;
44
    }
45
46
    public int getTermInMonths() {
47
        return termInMonths;
48
    }
49
50
    public void setTermInMonths(int termInMonths) {
51
        this.termInMonths = termInMonths;
52
    }
53
54
    public boolean isNotValid(boolean requiredTermInMonths) {
55
        return (accountNo <= 0l || accountHolderName == null || accountHolderName.trim().isEmpty()
56
                || accountType == null || (requiredTermInMonths ? termInMonths > 0 : false));
57
    }
58
59
}


You can see that the AccountOpeningDetails deals with the same parameters as fields and provides the validation on that as well. This is just for simplicity of the code.

Step 2: Now, let's take a look at the most important step. We have to define the factory class (AccountFactory) for the Account based on the given saving account-type i.e SAVING and CURRENT. I am placing the class into the same package of the class which the factory deals with i.e. saving; again just for organizing the code.

Java
xxxxxxxxxx
1
30
 
1
package org.trishinfotech.abstractfactory.acct.saving;
2
3
import org.trishinfotech.abstractfactory.AbstractAccountFactory;
4
import org.trishinfotech.abstractfactory.acct.Account;
5
import org.trishinfotech.abstractfactory.acct.AccountOpeningDetails;
6
import org.trishinfotech.abstractfactory.acct.AccountType;
7
8
public class AccountFactory extends AbstractAccountFactory {
9
10
    @Override
11
    public Account createAccount(AccountOpeningDetails openingDetails) {
12
        if (openingDetails == null || openingDetails.isNotValid(false)) {
13
            throw new IllegalArgumentException("Account Opening Details are not valid!");
14
        }
15
        Account account = null;
16
        AccountType type = openingDetails.getAccountType();
17
        switch (type) {
18
        case SAVING:
19
            account = new SavingAccount(openingDetails.getAccountNo(), openingDetails.getAccountHolderName());
20
            break;
21
        case CURRENT:
22
            account = new CurrentAccount(openingDetails.getAccountNo(), openingDetails.getAccountHolderName());
23
            break;
24
        default:
25
            System.err.println("Unknown/unsupported account-type.");
26
        }
27
        return account;
28
    }
29
}


Please note that the AccountFactory is now a sub-type/implementation of the AbstractAccountFactory.

Next I created LoanAccountFactory for the AccountType HOME_LOAN, VEHICLE_LOAN, and PERSONAL_LOAN and placed under that package loan.

Java
xxxxxxxxxx
1
35
 
1
package org.trishinfotech.abstractfactory.acct.loan;
2
3
import org.trishinfotech.abstractfactory.AbstractAccountFactory;
4
import org.trishinfotech.abstractfactory.acct.AccountOpeningDetails;
5
import org.trishinfotech.abstractfactory.acct.AccountType;
6
7
public class LoanAccountFactory extends AbstractAccountFactory {
8
9
    @Override
10
    public LoanAccount createAccount(AccountOpeningDetails openingDetails) {
11
        if (openingDetails == null || openingDetails.isNotValid(true)) {
12
            throw new IllegalArgumentException("Account Opening Details are not valid!");
13
        }
14
        LoanAccount account = null;
15
        AccountType type = openingDetails.getAccountType();
16
        switch (type) {
17
        case PERSONAL_LOAN:
18
            account = new PersonalLoanAccount(openingDetails.getAccountNo(), openingDetails.getAccountHolderName(),
19
                    openingDetails.getTermInMonths());
20
            break;
21
        case HOME_LOAN:
22
            account = new HomeLoanAccount(openingDetails.getAccountNo(), openingDetails.getAccountHolderName(),
23
                    openingDetails.getTermInMonths());
24
            break;
25
        case VEHICLE_LOAN:
26
            account = new VehicleLoanAccount(openingDetails.getAccountNo(), openingDetails.getAccountHolderName(),
27
                    openingDetails.getTermInMonths());
28
            break;
29
        default:
30
            System.err.println("Unknown/unsupported account-type.");
31
        }
32
        return account;
33
    }
34
}


And I created DepositAccountFactory for AccountType FIXED_DEPOSIT and RECURRING_DEPOSIT and placed under the package deposit.

Java
xxxxxxxxxx
1
29
 
1
package org.trishinfotech.abstractfactory.acct.deposit;
2
3
import org.trishinfotech.abstractfactory.AbstractAccountFactory;
4
import org.trishinfotech.abstractfactory.acct.AccountOpeningDetails;
5
import org.trishinfotech.abstractfactory.acct.AccountType;
6
7
public class DepositAccountFactory extends AbstractAccountFactory {
8
9
    @Override
10
    public DepositAccount createAccount(AccountOpeningDetails openingDetails) {
11
        if (openingDetails == null || openingDetails.isNotValid(true)) {
12
            throw new IllegalArgumentException("Account Opening Details are not valid!");
13
        }
14
        DepositAccount account = null;
15
        AccountType type = openingDetails.getAccountType();
16
        switch (type) {
17
        case RECURRING_DEPOSIT:
18
            account = new RecurringDepositAccount(openingDetails.getAccountNo(), openingDetails.getAccountHolderName(), openingDetails.getTermInMonths());
19
            break;
20
        case FIXED_DEPOSIT:
21
            account = new FixedDepositAccount(openingDetails.getAccountNo(), openingDetails.getAccountHolderName(), openingDetails.getTermInMonths());
22
            break;
23
        default:
24
            System.err.println("Unknown/unsupported account-type.");
25
        }
26
        return account;
27
    }
28
}


So, we have three concrete implementation of factories as below:

  • AccountFactory - for SAVING family of Account classes.
  • LoanAccountFactory - for LOAN family of Account classes.
  • DepositAccountFactory - for DEPOSIT family of Account classes.

Now, we need a provider or producer class which will give us the factory class depending upon our category or family selection. So, here is the code of AccountFactoryProvider to do that:

Java
xxxxxxxxxx
1
29
 
1
package org.trishinfotech.abstractfactory;
2
3
import org.trishinfotech.abstractfactory.acct.deposit.DepositAccountFactory;
4
import org.trishinfotech.abstractfactory.acct.loan.LoanAccountFactory;
5
import org.trishinfotech.abstractfactory.acct.saving.AccountFactory;
6
7
public class AccountFactoryProvider {
8
9
    public static AbstractAccountFactory getAccountFactory(String accountCategory) {
10
        AbstractAccountFactory accountFactory = null;
11
        if (accountCategory != null) {
12
            switch (accountCategory) {
13
            case "SAVING":
14
                accountFactory = new AccountFactory();
15
                break;
16
            case "LOAN":
17
                accountFactory = new LoanAccountFactory();
18
                break;
19
            case "DEPOSIT":
20
                accountFactory = new DepositAccountFactory();
21
                break;
22
            default:
23
                break;
24
            }
25
        }
26
        return accountFactory;
27
    }
28
}


That's it. Its time to write a Main program to execute the code and test the output:

Java
xxxxxxxxxx
1
58
 
1
package org.trishinfotech.abstractfactory;
2
3
import org.trishinfotech.abstractfactory.acct.Account;
4
import org.trishinfotech.abstractfactory.acct.AccountOpeningDetails;
5
import org.trishinfotech.abstractfactory.acct.AccountType;
6
7
public class Main {
8
9
    public static void main(String[] args) {
10
11
        AbstractAccountFactory factory = AccountFactoryProvider.getAccountFactory("SAVING");
12
        Account acct1 = factory.createAccount(new AccountOpeningDetails(12345678l, "Vijay Kumar", AccountType.SAVING));
13
        acct1.deposit(10000.0d);
14
15
        System.out.print(acct1);
16
        System.out.printf(" has interest : %10.2f\n", acct1.getInterest(5));
17
18
        Account acct2 = factory.createAccount(new AccountOpeningDetails(12345680l, "Jay Kumar", AccountType.CURRENT));
19
        acct2.deposit(10000.0d);
20
21
        System.out.print(acct2);
22
        System.out.printf(" has interest : %10.2f\n", acct2.getInterest(5));
23
        
24
        factory = AccountFactoryProvider.getAccountFactory("DEPOSIT");
25
        Account acct3 = factory.createAccount(new AccountOpeningDetails(12345681l, "Micheal", AccountType.FIXED_DEPOSIT));
26
        acct3.deposit(10000.0d);
27
28
        System.out.print(acct3);
29
        System.out.printf(" has interest : %10.2f\n", acct3.getInterest(5));
30
31
        Account acct4 = factory.createAccount(new AccountOpeningDetails(12345682l, "Kim", AccountType.RECURRING_DEPOSIT));
32
        acct4.deposit(10000.0d);
33
34
        System.out.print(acct4);
35
        System.out.printf(" has interest : %10.2f\n", acct4.getInterest(5));
36
        
37
        factory = AccountFactoryProvider.getAccountFactory("LOAN");
38
        Account acct5 = factory.createAccount(new AccountOpeningDetails(12345683l, "Racheal", AccountType.HOME_LOAN));
39
        acct5.withdraw(10000.0d);
40
41
        System.out.print(acct5);
42
        System.out.printf(" has interest : %10.2f\n", acct5.getInterest(5));
43
44
        Account acct6 = factory.createAccount(new AccountOpeningDetails(12345685l, "Martin", AccountType.VEHICLE_LOAN));
45
        acct6.withdraw(10000.0d);
46
47
        System.out.print(acct6);
48
        System.out.printf(" has interest : %10.2f\n", acct6.getInterest(5));
49
        
50
        Account acct7 = factory.createAccount(new AccountOpeningDetails(12345686l, "Mike", AccountType.PERSONAL_LOAN));
51
        acct7.withdraw(10000.0d);
52
53
        System.out.print(acct7);
54
        System.out.printf(" has interest : %10.2f\n", acct7.getInterest(5));
55
    }
56
57
}


And here the the output of the program:

Plain Text
x
 
1
Account [accountNo=12345678, accountHolderName=Vijay Kumar, accountType=SAVING, rate=2.0, interestStrategy=Compound Interest, amount=10000.0] has interest :    1040.81
2
Account [accountNo=12345680, accountHolderName=Jay Kumar, accountType=CURRENT, rate=1.0, interestStrategy=Simple Interest, amount=10000.0] has interest :     500.00
3
Account [accountNo=12345681, accountHolderName=Micheal, accountType=FIXED_DEPOSIT, rate=3.5, interestStrategy=Simple Interest, amount=10000.0] has interest :    1750.00
4
Account [accountNo=12345682, accountHolderName=Kim, accountType=RECURRING_DEPOSIT, rate=2.5, interestStrategy=Compound Interest, amount=10000.0] has interest :    1314.08
5
Account [accountNo=12345683, accountHolderName=Racheal, accountType=HOME_LOAN, rate=3.0, interestStrategy=Compound Interest, amount=10000.0] has interest :    1592.74
6
Account [accountNo=12345685, accountHolderName=Martin, accountType=VEHICLE_LOAN, rate=4.0, interestStrategy=Compound Interest, amount=10000.0] has interest :    2166.53
7
Account [accountNo=12345686, accountHolderName=Mike, accountType=PERSONAL_LOAN, rate=5.0, interestStrategy=Compound Interest, amount=10000.0] has interest :    2762.82


I hope this tutorial helped demonstrate the business context and technical implementation of the Abstract Factory Pattern.

Source Code can be found here: Abstract-Factory-Design-Pattern-Sample-Code

Liked the article? Please don't forget to press that like button. Happy coding!

Need more articles, please visit my profile: Brijesh Saxena

Factory (object-oriented programming) Java (programming language) Abstract factory pattern

Opinions expressed by DZone contributors are their own.

Related

  • Enabling Behavior-Driven Service Discovery: A Lightweight Approach to Augment Java Factory Design Pattern
  • Builder Design Pattern in Java
  • Java 8/9 Best Practices — Part-1 (Static Factory Methods)
  • Using Python Libraries in Java

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!