We all know the visitor pattern from the book GoF (gang of four) or from other famous books. The examples in there are usually associated with trees or composite structures. I was always searching for real life examples, where this pattern could be used, making software reusable and more readable. During a refactoring task, a collegue of mine had a really good idea, that i want to share with you.
Here is the code fragment from the method we want to refactore. The method has a lot of if-statements and instanceOf in there. It maps contacts to data transfer objects (DTO)
public static void mapKontakte(ContactType contactType, List<ErweiterteKontakt<? extends StringDomainEnum>> kontakte) { ch.abraxas.tax.register.registerimport.parser.ech0046.v10.ObjectFactory ech0046Factory = new ch.abraxas.tax.register.registerimport.parser.ech0046.v10.ObjectFactory(); for (ErweiterteKontakt<? extends StringDomainEnum> kontakt : kontakte) { if (kontakt instanceof Telefon) { PhoneType phoneType = ech0046Factory.createPhoneType(); phoneType.setPhoneCategory(new BigInteger( ((Telefon) kontakt).getKategorie().key())); phoneType.setPhoneNumber(kontakt.getDetail()); contactType.getPhone().add(phoneType); } else if (kontakt instanceof Email) { EmailType emailType = ech0046Factory.createEmailType(); emailType.setEmailCategory(new BigInteger( ((Email) kontakt).getKategorie().key())); emailType.setEmailAddress(kontakt.getDetail()); contactType.getEmail().add(emailType); } else if (kontakt instanceof Internet) { InternetType internetType = ech0046Factory.createInternetType(); internetType.setInternetCategory(new BigInteger( ((Internet) kontakt).getKategorie().key())); internetType.setInternetAddress(kontakt.getDetail()); contactType.getInternet().add(internetType); } // Should we use an else branch to verify that we are mapping all Kontakte? } }
here is the same method after refactoring:
public static void mapKontakte(ContactType contactType, List<ErweiterteKontakt<? extends StringDomainEnum>> kontakte) { ErweiterterKontaktVisitor visitor = new BindingObjectFromKontaktVisitor(contactType); for (ErweiterteKontakt<? extends StringDomainEnum> kontakt : kontakte) { // calls the Kontakt to use the visitor kontakt.accept(visitor); } }
Well here is how to do it. In the abstract class ErweiterteKontakt we define following method:
public abstract void accept(ErweiterterKontaktVisitor visitor);
Then we implement this abstract method in the classes Email, Telefon and Internet. They all extends ErweiterkeKontakt.
@Override public void accept(ErweiterterKontaktVisitor visitor) { visitor.visit(this); }Defining the visitor interface
public interface ErweiterterKontaktVisitor {
/**
* Visit the telefon
*
* @param telefon
*/
public void visit(Telefon telefon);
/**
* visit the email
*
* @param email
*/
public void visit(Email email);
/**
* visit the internet
*
* @param internet
*/
public void visit(Internet internet);
}
Then Implementing the Visitor itself:
public class BindingObjectFromKontaktVisitor implements ErweiterterKontaktVisitor { private ch.abraxas.tax.register.registerimport.parser.ech0046.v10.ObjectFactory ech0046Factory = new ch.abraxas.tax.register.registerimport.parser.ech0046.v10.ObjectFactory(); private ContactType contactType; /** * Konstruktor */ public BindingObjectFromKontaktVisitor(ContactType contactType) { this.contactType = contactType; } /** * {@inheritDoc} */ @Override public void visit(Telefon telefon) { PhoneType phoneType = ech0046Factory.createPhoneType(); phoneType.setPhoneCategory(new BigInteger(telefon.getKategorie().key())); phoneType.setPhoneNumber(telefon.getDetail()); contactType.getPhone().add(phoneType); } /** * {@inheritDoc} */ @Override public void visit(Email email) { EmailType emailType = ech0046Factory.createEmailType(); emailType.setEmailCategory(new BigInteger(email.getKategorie().key())); emailType.setEmailAddress(email.getDetail()); contactType.getEmail().add(emailType); } /** * {@inheritDoc} */ @Override public void visit(Internet internet) { InternetType internetType = ech0046Factory.createInternetType(); internetType.setInternetCategory(new BigInteger((internet).getKategorie().key())); internetType.setInternetAddress(internet.getDetail()); contactType.getInternet().add(internetType); } }
Done!
No more If-Statements, no more InstanceOf and the code is now extandable and more flexible.
😱👇 PROMOTIONAL DISCOUNT: BOOKS AND IPODS PRO 😱👇
Be sure to read, it will change your life!
Show your work by Austin Kleon: https://amzn.to/34NVmwx
This book is a must read - it will put you in another level! (Expert)
Agile Software Development, Principles, Patterns, and Practices: https://amzn.to/30WQSm2
Write cleaner code and stand out!
Clean Code - A Handbook of Agile Software Craftsmanship: https://amzn.to/33RvaSv
This book is very practical, straightforward and to the point! Worth every penny!
Kotlin for Android App Development (Developer's Library): https://amzn.to/33VZ6gp
Needless to say, these are top right?
Apple AirPods Pro: https://amzn.to/2GOICxy
😱👆 PROMOTIONAL DISCOUNT: BOOKS AND IPODS PRO 😱👆