DZone
DevOps Zone
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
  • Refcardz
  • Trend Reports
  • Webinars
  • Zones
  • |
    • Agile
    • AI
    • Big Data
    • Cloud
    • Database
    • DevOps
    • Integration
    • IoT
    • Java
    • Microservices
    • Open Source
    • Performance
    • Security
    • Web Dev
DZone > DevOps Zone > CDI, Polymorphism and The Factory Pattern

CDI, Polymorphism and The Factory Pattern

Gabriel Francisco user avatar by
Gabriel Francisco
·
Mar. 08, 15 · DevOps Zone · Interview
Like (2)
Save
Tweet
13.20K Views

Join the DZone community and get the full member experience.

Join For Free
One of the most useful things about object oriented programming is polymorphism. In fact, it makes our software to have different behaviors without the need to explicit it in the code.
For instance, you are writing a code to make a credit card transaction. If it is a VISA transaction, you should execute one specific action, if it is a MASTERCARD transaction, you should execute another specific action, so, the first thing you think to do is:
view source
print?
01.public void pay(Transaction transaction) throws UnsupportedCardNetworkException {
02.        if("visa".equals(transaction.getType())){
03.            //pay with visa
04.            return;
05.        }
06.        
07.        if("mastercard".equals(transaction.getType())){
08.            //pay with mastercard
09.            return;
10.        }
11.        
12.        throw new UnsupportedCardNetworkException(); 
} 
That's not a good design, is it? Probably we need to write a lot of code for each card network, besides, if I want to add support to another card network, I will need another if. Someday this code is going to be little demon.
So, what should I do? Well, in oriented programming, there is something what people call "polymorphism", and what they say that the same method could have several behaviors. Well, that's just what we need for this situation. So, how can we begin?
Well, we are going to have an interface called Payment, having a method, of course, called "pay":
Payment:
view source
print?
1.public interface Payment {
2.    void pay(Transaction transaction);
3.}
That's pretty simple, actually. So, as we have two credit card networks to implement, we are going to have two more classes, one implementing visa, another implementing mastercard:
VisaPayment:
view source
print?
1.public class VisaPayment implements Payment {
2.    @Override
3.    public void pay(Transaction transaction) {
4.        //paying with visa
5.    }
6.}
MasterCardPayment:
view source
print?
1.public class MasterCardPayment implements Payment {
2.    @Override
3.    public void pay(Transaction transaction) {
4.        //paying with mastercard
5.    }
6.}
So, when I need to pay using a visa card, I just do:
view source
print?
1.Payment payment = new VisaPayment();
2.payment.pay(transaction);
And, when I need to pay using mastercard:
view source
print?
1.Payment payment = new MasterCardPayment();
2.payment.pay(transaction);
That solves our first "pay" method, which was going to be very big (very!!)
But, if I want to do something dynamically? For instance, I don't want to use ifs in my code to choose which instance of a Payment I am going to use, I just want to tell the code to give me an instance based on some variable, like:
view source
print?
1.Payment payment = payments.get("visa");
2.payment.pay(transaction);
Well, I said something dynamic, so:
view source
print?
1.Payment payment = payments.get(transaction.getType());
2.payment.pay(transaction);
That's pretty good, actually! But how can we do that? Well, the anwser is short and simple:
PaymentFactory:
view source
print?
01.public class PaymentFactory {
02.    private static final Map<String, Payment> payments;
03.    
04.    private PaymentFactory(){}
05.    static {
06.        payments = new HashMap<String, Payment>();
07.        payments.put("visa", new VisaPayment());
08.        payments.put("mastercard", new MasterCardPayment());
09.    }
10.    public static Payment get(String type){
11.        return payments.get(type);
12.    } 
13.}
So, when I want to get a Payment instance:
view source
print?
1.Payment payment = PaymentFactory.get(transaction.getType());
Quite simple, isn't it? Well, you should be wondering "but, why is there a CDI in the title of this article?", well, now you are going to know why and see how this stuff gets interesting in here.
CDI allows us to create several beans of a super type and choose a instance by a qualifier, so, here is what we are going to do first:
Create a qualifier called Network
view source
print?
1.@Retention(RetentionPolicy.RUNTIME)
2.@Target({TYPE, METHOD, FIELD, PARAMETER, CONSTRUCTOR})
3.@Qualifier
4.public @interface Network {
5.    String value();
6.}
And, annotate our classes MasterCardPayment and VisaPayment with this qualifier, setting the value in the qualifier in each one:
MasterCardPayment:
view source
print?
1.@Network("mastercard")
2.public class MasterCardPayment implements Payment {
3.    @Override
4.    public void pay(Transaction transaction) {
5.        //paying with mastercard
6.    }
7.}
VisaPayment:
view source
print?
1.@Network("visa")
2.public class VisaPayment implements Payment {
3.    @Override
4.    public void pay(Transaction transaction) {
5.        //paying with visa
6.    }
7.}
We can't select an instance just by the string value, so, we need to create a class whichs extends from AnnotationLiteral and implements our Network annotation.
NetworkAnnotationLiteral:
view source
print?
01.public class NetworkAnnotationLiteral extends AnnotationLiteral<Network> implements Network{
02.    private String value;
03.    private NetworkAnnotationLiteral(String value) {
04.        this.value = value;
05.    }
06.    @Override
07.    public String value() {
08.        return value;
09.    }
10.    public static NetworkAnnotationLiteral network(String value){
11.        return new NetworkAnnotationLiteral(value);
12.    }
13.}
If we want a instance of NetworkAnnotationLiteral, we just call: 
view source
print?
1.NetworkAnnotationLiteral.network("type");
But, how can we select our instance based on a transaction type now? Well:
view source
print?
01.@Inject @Any
02.private Instance<Payment> payments;
03. 
04.@Test
05.public void payWithVisa(){
06.    Transaction transaction = new Transaction();
07.    transaction.setType("visa");
08.    Payment payment = payments.select(NetworkAnnotationLiteral.network(transaction.getType())).get();
09.    payment.pay(transaction);
10.}
11. 
12.@Test
13.public void payWithMaster(){
14.    Transaction transaction = new Transaction();
15.    transaction.setType("mastercard");
16.    Payment payment = payments.select(NetworkAnnotationLiteral.network(transaction.getType())).get();
17.    payment.pay(transaction);
18.}
When we inject the object javax.enterprise.inject.Instance<Payment> with the qualifier @Any, every instance of Payment will be available for you to choose and use. The nicest thing is: now CDI will control the creation of your objects, you don't need to write your own factory, you can also inject resources into your Payment subtypes and choose any CDI scope you want.
That's not a new thing in CDI, but, most people still don't know how powerful CDI is.
CDI Factory (object-oriented programming) Polymorphism (computer science)

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Why Your Database Needs a Machine Learning Brain
  • Anatomy of a Webhook HTTP Request
  • Remote Debugging and Developer Observability
  • How to Build Microservices With Node.js

Comments

DevOps Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

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

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends:

DZone.com is powered by 

AnswerHub logo