Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for Factory Pattern with Polymorphism on Spring
Jonatas de Moraes Junior
Jonatas de Moraes Junior

Posted on

     

Factory Pattern with Polymorphism on Spring

The Factory Pattern is one of the most well-known design patterns described by the Gang of Four (GoF). It allows for the caller to choose wich type of class needs to be instantiated, also hiding the creation logic from the caller.

However, there is a very confusing Spring mehcanic available through this hereFactoryBean. Event though it has Factory in its name, it does not represent a Factory as designed by GoF, because it only allows for the creation of a single type.

In this article we will explore how to implement a full-compliant GoF Factory with Spring, where the created objects are also Spring controlled beans.

Let's say you have an interface Animal and two implementations Cat and Dog:

publicinterfaceAnimal{StringmakeNoise();}publicclassCatimplementsAnimal{@OverridepublicStringmakeNoise(){return"Meow!";}}publicclassDogimplementsAnimal{@OverridepublicStringmakeNoise(){return"Bark!";}}
Enter fullscreen modeExit fullscreen mode

By definition: "Factory Method is a creational design pattern that provides an interface for creating objects in a superclass, but allows subclasses to alter the type of objects that will be created".

Ok, boring. However, what you expect is an AnimalFactory class that allows you to choose wether to get an instance of either a Cat or a Dog, right?

You could create a simple AnimalFactory class which just returns a new Cat or new Dog depending on a parameter you pass. The problem is: when you callnew by yourself, the bean is not being created by Spring.

So, let's start by just doing that, let's show these classes to Spring and let it control their instances. Let's annotate them with @Component:

@ComponentpublicclassCatimplementsAnimal{@OverridepublicStringmakeNoise(){return"Meow!";}}@ComponentpublicclassDogimplementsAnimal{@OverridepublicStringmakeNoise(){return"Bark!";}}
Enter fullscreen modeExit fullscreen mode

Now Spring knows Cat and Dog, and can give us instances of them when requested. Let's now create a very simple Factory that just fulfills our requirements:

@ComponentpublicclassAnimalFactory{@AutowiredprivateCatcat;@AutowiredprivateDogdog;publicAnimalgetAnimal(StringanimalType){if("cat".equals(animalType)){returnthis.cat;}elseif("dog".equals(animalType)){returnthis.dog;}else{returnnull;}}}
Enter fullscreen modeExit fullscreen mode

Ok, it works. But what if you have a thousand different animals? Let's improve this factory a bit by putting the instances in a Map:

publicenumAnimalType{CAT,DOG,;}@ComponentpublicclassAnimalFactory{@AutowiredprivateCatcat;@AutowiredprivateDogdog;privateEnumMap<AnimalType,Animal>animalsMap;publicAnimalFactory(){this.animalsMap=newEnumMap<>(AnimalType.class);this.animalsMap.put(AnimalType.CAT,cat);this.animalsMap.put(AnimalType.DOG,dog);}publicAnimalgetAnimal(AnimalTypeanimalType){returnthis.animalsMap.get(animalType);}}
Enter fullscreen modeExit fullscreen mode

This time we have created an Enum with the types of the animals, and also a EnumMap to contain the instances of the animals. We also have changed the signature of the getAnimal method to take the enum as parameter.

This is a much better approach, but still, if we have a huge number of animal types, this class will grow accordingly with nothing but boilerplate code. And if you create a new animal type and forget to put it in the map, you may get an error somewhere else.

To apply the final touches to our Factory, we will make use of a lesser known functionality of Spring: an @Autowired constructor that takes a List of objects as parameter. Since Spring knows all Animal objects, when we inject a list of that type, Spring populates that List with all known objects of that type (Cat and Dog). Below is the full final code:

publicenumAnimalType{CAT,DOG,;}publicinterfaceAnimal{AnimalTypegetType();StringmakeNoise();}@ComponentpublicclassCatimplementsAnimal{@OverridepublicAnimalTypegetType(){returnAnimalType.CAT;}@OverridepublicStringmakeNoise(){return"Meow!";}}@ComponentpublicclassDogimplementsAnimal{@OverridepublicAnimalTypegetType(){returnAnimalType.DOG;}@OverridepublicStringmakeNoise(){return"Bark!";}}@ComponentpublicclassAnimalFactory{privateEnumMap<AnimalType,Animal>animalsMap;@AutowiredpublicAnimalFactory(List<Animal>animals){this.animalsMap=newEnumMap<>(AnimalType.class);for(Animalanimal:animals){this.animalsMap.put(animal.getType(),animal);}}publicAnimalgetAnimal(AnimalTypeanimalType){returnthis.animalsMap.get(animalType);}}
Enter fullscreen modeExit fullscreen mode

Here we have created a new method in the Animal interface: getType(), that return that animal's type. At the factory's constructor, we take the List of Animals and populate our map with them. This way the code for the AnimalFactory class does not need to be changed with every new Animal created; whenever you need a new Animal, just annotate it with @Component and the Factory can create it.

This is it, I hope it helps.

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Love to learn, love to teach. And above all, love to question.
  • Location
    São Paulo, Brasil
  • Joined

More fromJonatas de Moraes Junior

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp