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

Enterprise AI Trend Report: Gain insights on ethical AI, MLOps, generative AI, large language models, and much more.

2024 Cloud survey: Share your insights on microservices, containers, K8s, CI/CD, and DevOps (+ enter a $750 raffle!) for our Trend Reports.

PostgreSQL: Learn about the open-source RDBMS' advanced capabilities, core components, common commands and functions, and general DBA tasks.

AI Automation Essentials. Check out the latest Refcard on all things AI automation, including model training, data security, and more.

Related

  • Generics in Java and Their Implementation
  • Spring Beans With Auto-Generated Implementations: How-To
  • User-Friendly API Publishing and Testing With Retrofit
  • How to Secure Apache Ignite From Scratch

Trending

  • Continuous Improvement as a Team
  • BPMN 2.0 and Jakarta EE: A Powerful Alliance
  • Building a Performant Application Using Netty Framework in Java
  • Sprint Anti-Patterns
  1. DZone
  2. Coding
  3. Languages
  4. Java: The Factory Method Pattern

Java: The Factory Method Pattern

Explored the Factory Method pattern, walk through a Java example, and examine some other patterns that can be good alternatives to it.

By 
Justin Albano user avatar
Justin Albano
DZone Core CORE ·
Nov. 25, 17 · Tutorial
Like (32)
Save
Tweet
Share
108.0K Views

Join the DZone community and get the full member experience.

Join For Free

Patterns are one of the most useful techniques that an experienced developer can employ when writing large volumes of code or when looking for a clean solution to a difficult problem. They can also be one of the most widely overused techniques or the most fatally misunderstood techniques. In the previous articles in this series, we explored the Observer pattern and the Strategy pattern, delving into the purpose and idiosyncrasies of each. In this article, we will focus on the Factory Method pattern, introducing its textbook definition and concentrating on an intuitive understanding of the pattern. We will also explore the common mischaracterizations of this pattern and define some of the colloquial uses of this pattern.

The Textbook Definition

The Factory Method pattern has commonly been misunderstood to mean any method whose sole purpose is to construct an object and return this created object. In most cases, this translates to a static method that abstracts the construction process of an object. Even the Spring framework uses this loose definition in its configuration files, claiming that any method that creates a bean can be configured to be a Factory Method. While this colloquial definition of the Factory pattern is important, it is only a special case of the true pattern. According to the Gang of Four book that defined the technique, the intent of the Factory Method pattern is as follows:

Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.

As we can see in this textbook definition, a Factory Method involves more than just a single method. Instead, it involves creating two sets of interfaces: (1) the product interface that constitutes the object to be created and (2) the creator interface that constitutes the object that will instantiate the product. The Unified Modeling Language (UML) class diagram for this relationship is illustrated in the figure below:


Image title


Using the general pattern, we build a Creator interface, which includes one or more Factory Methods, as well as any number of other methods. This interface may also be an abstract class with defined methods or even a default implementation of the Factory Method that returns a default instance of Product . From this interface, a set of ConcreteCreator classes are created that return desired ConcreteProduct instances that implement the Product interface.

While the basic Factory Method pattern is simple, there are some variations that can add complexity to a system. For example, the operations associated with the Creator interface or abstract class may be Template Methods, where an algorithm is defined in terms of one or a series of Factory Methods that create objects utilized in the Template Method.

The Creator interface may also be a full-fledged class, containing concrete implementations for each of the operations and Factory Methods. Instead of creating a full abstraction in the form of an interface (or partial abstraction in the form of an abstract class), we allow for a hierarchy that does not necessarily include any ConcreteCreator instances. Likewise, the Product interface may actually be a class, where the fields of the class are initialized differently depending on the ConcreteCreator implementation that returned the Product.

An Example in Java

Before proceeding to gain an intuitive understanding of the Factory Method pattern, we will walk through an example of the pattern in Java. In this example, we will create an encryption mechanism that allows for the user to supply a string of text and a file name, which is then encrypted and written to disk. Since there are various encryption algorithms that may be desired in a variety of scenarios, we will use the Factory Method pattern to allow for multiple encryption implementations to be created. The UML class diagram for this example is illustrated in the figure below:


Image title


We start by developing our Product interface since it has the most dependencies (the Creator interface directly depends on it and the ConcreteProduct implementations inherit from it):

public interface EncryptionAlgorithm {
    public String encrypt(String plaintext);
}


Next, we develop the Creator interface. In our case, we will make our Creator an abstract class that contains an operation that allows a client to supply the plaintext to be encrypted, as well as the file name that the encrypted cyphertext should be saved to:

public abstract class Encryptor {

    public void writeToDisk(String plaintext, String filename) {

        EncryptionAlgorithm encryptionAlgorithm = getEncryptionAlgorithm();
        String cyphertext = encryptionAlgorithm.encrypt(plaintext);

        try (FileOutputStream outputStream = new FileOutputStream(filename)) {
            outputStream.write(cyphertext.getBytes());
        } 
        catch (IOException e) {
            e.printStackTrace();
        }

    }

    public abstract EncryptionAlgorithm getEncryptionAlgorithm();
}


With both of the abstractions in place, we move onto creating the ConcreteProduct implementations (since the ConcreteCreator instances will depend on these implementations). In our example, we will create implementations for two encryption algorithms: (1) SHA-256 and (2) SHA-512. For both implementations, we will use the Apache Commons encryption implementations (org.apache.commons.codec.digest.DigestUtils). The implementation of our SHA-256 encryption algorithm is as follows:

public class Sha256CEncryptionAlgorithm implements EncryptionAlgorithm {

    @Override
    public String encrypt(String plaintext) {
        return DigestUtils.sha256Hex(plaintext);
    }
}


Likewise, the implementation of our SHA-512 algorithm is as follows:

public class Sha512EncryptionAlgorithm implements EncryptionAlgorithm {

    @Override
    public String encrypt(String plaintext) {
        return DigestUtils.sha512Hex(plaintext);
    }
}


With our ConcreteProduct implementations complete, we can now create our two corresponding ConcreteCreator implementations: (1) Sha256Encryptor and (2) Sha512Encryptor. The implementation for the former is as follows:

public class Sha256Encryptor extends Encryptor {

    @Override
    public EncryptionAlgorithm getEncryptionAlgorithm() {
        return new Sha256CEncryptionAlgorithm();
    }
}


The implementation for the latter is as follows:

public class Sha512Encryptor extends Encryptor {

    @Override
    public EncryptionAlgorithm getEncryptionAlgorithm() {
        return new Sha512EncryptionAlgorithm();
    }
}


We can now supply our selected encryptor to a client and have the behavior of our system vary depending on the concrete type used. For example, suppose we create a new PersistedFile class that consumes the contents of a file (and maybe performs some formatting), encrypts the contents, and persists it to disk. In this case, we can vary the runtime behavior of this class by supplying a concrete implementation of Encryptor:

public class PersistedFile {

    private final String path;
    private final String contents;
    private final Encryptor encryptor;

    public PersistedFile(String path, String contents, Encryptor encryptor) {
        this.path = path;
        this.contents = contents;
        this.encryptor = encryptor;
    }

    public void persist() {
        encryptor.writeToDisk(contents, path);
    }
}

PersistedFile file = new PersistedFile("/foo/bar/text.txt", "Hello, world!", new Sha256Encryptor());


We can vary encrypted contents of the file by simply changing the Encryptor object supplied to the constructor of PersistedFile. In the case above, we used our SHA-256 implementation, which will result in SHA-256 cyphertext being persisted to disk. If instead, we selected our SHA-512 implementation, we would have had SHA-256 cyphertext written to disk. By simply varying the Creator implementation supplied to our client, we can entirely change the behavior of our system, without tieing the client to any particular implementation of our ConcreteProduct subclasses.

Idiosyncrasies

Even with the ardent simplicity of this pattern, there are still a few nuances that must be discussed. First, we will look into simplifications of the Factory Method pattern and possible techniques for collapsing simplistic hierarchies into fewer classes. Second, we will elaborate on the inherent relationship between many Factory Method pattern implementations and the Template Method pattern.

Simplified Hierarchies

In practice, Factory Methods are non-trivial and may even include a dense degree of code to support the features provided by the pattern implementation. For example, suppose we have a User Interface (UI) framework that desires to create various visual components, such as buttons, text boxes, titles, etc. In this scenario, we may have a Product interface, VisualComponent, that includes methods for altering the height and width of the various components:

public interface VisualComponent {
    public void setHeight(int pixels);
    public void setWidth(int pixels);
}


Although this interface is very simple, the implementations that accompany it will likely be very complex. For example, if a button implementation desires to keep the same aspect ratio (constrained) when resized (if the height of the button is changed, the width will also change to ensure the same ratio of height to width is maintained), a decent level of computation may be involved. On the other hand, if the components are unconstrained, the logic involved may be simpler, but will likely require some geometric computation (i.e. if the width of a button is changed, where should the text be repositioned to ensure it is centered?).

In order to allow the user to decide on the logic involved in resizing a visual component, the framework may request that a Creator is supplied that can provide the required components (note that this implementation contains more than one Factory Method):

public interface Button extends VisualComponent {
    public void setText(String text);
}

public interface Textbox extends VisualComponent {
    public void setText(String text);
    public void setCharacterLimit(int limit);
}

public interface VisualComponentFactory {
    public Button createButton();
    public Textbox createTextbox();
}


We can then implement a ConcreteCreator for constrained visual components:

public class ConstrainedButton implements Button { ... }
public class ConstrainedTextbox implements Textbox { ... }

public class ConstrainedVisualComponentFactory implements VisualComponentFactory {

    @Override
    public Button createButton() {
        return new ConstrainedButton();
    }

    @Override
    public Textbox createTextbox() {
        return new ConstrainedTextbox();
    }
}


Likewise, we can implement one for unconstrained visual components:

public class UnconstrainedButton implements Button { ... }
public class UnconstrainedTextbox implements Textbox { ... }

public class UnconstrainedVisualComponentFactory implements VisualComponentFactory {

    @Override
    public Button createButton() {
        return new UnconstrainedButton();
    }

    @Override
    public Textbox createTextbox() {
        return new UnconstrainedTextbox();
    }
}


We can then supply our desired Creator to the UI framework in the following manner:

public UiBootstrapper {

    private VisualComponentFactory factory;

    public void setVisualComponentFactory(VisualComponentFactory factory) {
        this.factory = factory;
    }

    public void drawUi() {
        // Create a button and do something with it...
        Button button = factory.createButton();

        // Create a textbox and do something with it...
        Textbox textbox = factory.createTextbox();
}

UiBootstrapper bootstrapper = new UiBootstrapper();
bootstrapper.setVisualComponentFactory(new ConstrainedVisualComponentFactory());
bootstrapper.drawUi();


In this scenario, the complexity of our ConcreteProduct implementations, along with the multiplicity of our Factory Methods, warrants the creation of separate Creator and Product hierarchies. In the case of our encryption system above, this separation is arguably unneeded. If we reexamine the Encryptor abstract class, it contains only one concrete method (writeToDisk) and one factory method (getEncryptionAlgorithm). Whatsmore, the Product interface (EncryptionAlgorithm) contains only a single operation (encrypt). Due to the simplicity of this hierarchy, we may be able to condense our system by removing the Creator from the picture. Instead, we can simply have a set of EncryptionAlgorithm implementations and refactor our code to resemble the Strategy Pattern.

To do this, we can inline the writeToDisk method to the PersistedFile class as such:

public class PersistedFile {

    private final String path;
    private final String contents;
    private final Encryptor encryptor;

    public PersistedFile(String path, String contents, Encryptor encryptor) {
        this.path = path;
        this.contents = contents;
        this.encryptor = encryptor;
    }

    public void persist() {
        EncryptionAlgorithm encryptionAlgorithm = encryptor.getEncryptionAlgorithm();
        String cyphertext = encryptionAlgorithm.encrypt(contents);

        try (FileOutputStream outputStream = new FileOutputStream(path)) {
            outputStream.write(cyphertext.getBytes());
        } 
        catch (IOException e) {
            e.printStackTrace();
        }
    }
}

PersistedFile file = new PersistedFile("/foo/bar/text.txt", "Hello, world!", new Sha256Encryptor());


This leaves us with an Encryptor abstract class that contains only one abstract method:

public abstract class Encryptor {
    public abstract EncryptionAlgorithm getEncryptionAlgorithm();
}


This abstract class simply returns a specific EncryptionAlgorithm instance. If we look into the EncryptionAlgorithm interface, we are left with a single method as well (encrypt). Since each Encryptor implementation has a one-to-one correspondence with each EncryptionAlgorithm implementation, the indirection provided by the Encryptor interface is not very useful: We could just as easily supply an EncryptionAlgorithm implementation directly when an Encryptor is expected (and we would save the overhead of the indirect call to getEncryptionAlgorithm).

Seen more concretely, if we examine the persist method of the PersistedFile class, we see that the only call that we make to the Encryptor object is getEncryptionAlgorithm. We then call the encryption algorithm to convert our plaintext to cyphertext. We can refactor these two calls into a single call as follows (this is an intermediary refactor and may not be a good idea if refactor is intended to be the final result, as discussed here):

public class PersistedFile {

    // Other fields and methods removed for clarity

    public void persist() {
        String cyphertext = encryptor.getEncryptionAlgorithm().encrypt(contents);

        try (FileOutputStream outputStream = new FileOutputStream(path)) {
            outputStream.write(cyphertext.getBytes());
        } 
        catch (IOException e) {
            e.printStackTrace();
        }
    }
}


We can then refactor the PersistedFile to skip the use of the Encryptor interface all together by dealing with an EncryptionAlgorithm object directly:

public class PersistedFile {

    private final String path;
    private final String contents;
    private final EncryptionAlgorithm cipher;

    public PersistedFile(String path, String contents, EncryptionAlgorithm cipher) {
        this.path = path;
        this.contents = contents;
        this.cipher = cipher;
    }

    public void persist() {
        String cyphertext = cipher.encrypt(contents);

        try (FileOutputStream outputStream = new FileOutputStream(path)) {
            outputStream.write(cyphertext.getBytes());
        } 
        catch (IOException e) {
            e.printStackTrace();
        }
    }
}

PersistedFile file = new PersistedFile("/foo/bar/text.txt", "Hello, world!", new Sha256EncryptionAlgorithm());


Note that we now provide an EncryptionAlgorithm object (Sha256EncryptionAlgorithm rather than Sha256Encryptor) in the constructor of PersistedFile. At the completion of this refactor, we have transformed our Factory Method pattern implementation into a Strategy Pattern implementation, where each EncryptionAlgorithm subclass represents a concrete strategy. This transformation may not be appropriate in all cases and judgment should be used when deciding if a Factory Method pattern implementation is too simple for separate Creator and Product hierarchies. In the event that the Factory Method implementation is over-simplified, the Strategy Pattern may prove to be a useful alternative.

Template Methods

As seen with the Strategy Pattern alternative, multiple patterns are often interrelated. In the case of the Factory Method pattern, the Template Method pattern is often found not far away. In the Template Method pattern, we create a method that acts as an algorithm, where other abstract methods are used as the steps. For example, we can create a template for handling different events when parsing eXtensible Markup Language (XML) snippets, such as:

<books>
    <book>
        <title>The Great Divorce</title>
        <author>C.S. Lewis</author>
    </book>
    <book>
        <title>The Devil in the White City</title>
        <author>Erik Larson</author>
    </book>
</books>


The Template Method implementation for the handler may resemble:

public class Element {

    private final String tag;

    public Element(String tag) {
        this.tag = tag;
    }

    public String getTag() {
        return tag;
    }
}

public abstract class XmlHandler {

    public void parse(String xmlSnippet) {

        // ...parse the XML file...
        // ...when XML element is found, create Element object and call...
        Element element = getNextElement();
        handleElement(element);
    }

    public abstract void handleElement(Element element);
}


We can then create an implementation of the handler:

public class XmlPrintHandler extends XmlHandler {

    @Override
    public void handleElement(Element element) {
        System.out.println("Found element with tag: " + element.getTag());
    }
}


We can then use our handler to parse some XML:

XmlHandler handler = new XmlPrintHandler();
handler.parse("<books><book><title>Ben-Hur</title><author>Lew Wallace</author></book></books>");


This would result in the following output:

Found element with tag: books
Found element with tag: book
Found element with tag: title
Found element with tag: author


The interrelationship with the Factory Method pattern is introduced when we make our step methods (such as handleElement) Factory Methods. For example, we can create a Product hierarchy to handle the found XML elements:

public interface ElementHandler {
    public void handleElement(Element element);
}

public class PrintElementHandler implements ElementHandler {

    @Override
    public void handleElement(Element element) {
        System.out.println("Found element with tag: " + element.getTag());
    }
}

public class LoggerElementHandler implements ElementHandler {

    @Override
    public void handleElement(Element element) {
        // ...log the element tag...
    }
}


We can then replace the original step method (XmlHandler::handleElement) with a Factory Method that returns the ElementHandler:

public abstract class XmlHandler {

    public void parse(String xmlSnippet) {

        // ...parse the XML file...
        // ...when XML element is found, create Element object and call...
        Element element = getNextElement();
        ElementHandler handler = getElementHandler();
        handler.handleElement(element);
    }

    public abstract ElementHandler getElementHandler();
}


We can then refactor our XmlPrintHandler to conform to this XmlHandler abstract class:

public class XmlPrintHandler extends XmlHandler {

    @Override
    public ElementHandler getElementHandler() {
        return new PrintElementHandler();
    }
}


The parser now functions exactly as it once did, but we have introduced a Factory Method into the XmlHandler abstract class. With this introduction, we have made XmlHandler the Creator, XmlPrintHandler the ConcreteCreator, ElementHandler the Product, and PrintElementHandler the ConcreteProduct. Our Creator interface may be expanded later to include handlers for open and closing elements, rather than just general elements (in our case, implicitly meaning opening elements). While the template implementation is simpler, introducing Factory Methods may provide useful functionality.

For example, suppose we do not care about the ordering of processed elements (i.e. we only wish to count how many books are in a given XML snippet by counting the number of opening <book> elements), we can parallelize this parsing process, creating a new ElementHandler each time a new element is encountered. In the template technique, multiple-threading may be difficult to achieve, since a single method on a single object is being called by multiple threads. In the Factory Method case, we create a new object for each occurrence of an element, thus removing the possibility of contention between invocations of the handleElement method (each invocation is executed on a new instance of ElementHandler rather than multiple invocations on a single XmlHandler instance).

Colloquial Definition

While we have explored the textbook definition of the Factory Method pattern from the Gang of Four Design Patterns book, this is often not how the term Factory pattern (note that the word method is often dropped) is used by developers. In many cases, the following definition is implicitly used:

A method whose sole responsibility is to abstract the creation process of an object and return that object.

Many times, this definition is implemented in the form of a static method that returns an object, but whose return type is an interface for that object. For example:

public class XmlHandlerFactory {

    public static XmlHandler createHandler() {
        return new XmlPrintHandler();
    }
}


This simplified definition of the Factory Method pattern is a special case of the general pattern, where the Creator hierarchy is reduced to a single class and the factory method is reduced to a single static method (rather than a method that is overridden for each of the possible Product implementation can be returned). In essence, the Creator returns a default Product instance and no other ConcreteCreator can override this default.

This pattern is often used as an implementation of the Facade pattern when dealing with external libraries or projects that externally compiled. The use of this static method allows for a single point of entry into a library. For example, if we packaged our XML handling logic into a project or module, we could allow for external clients to obtain the selected handler functionality in the following manner:

XmlHandler handler = XmlHandlerFactory.createHandler();


This structure assumes that the XmlHandler Factory has enough knowledge to know which handler implementation is appropriate. If the factory does not have sufficient information to make this decision, it may be a wise idea to provide various implementations and allow the client to select an appropriate implementation, possibly through dependency injection. This allows the client to select an existing implementation and use it if the implementation suffices or create a new implementation if no existing implementation suffices.

Conclusion

Patterns can be an excellent asset in solving recurring problems, but they can also introduce a great deal of confusion if not properly understood. One of the most commonly misunderstood patterns is the Factory Method pattern, which is commonly mistaken for a simplification of the original pattern. In this article, we explored the purpose of the pattern, walked through an example of the pattern in Java, and examined some other patterns that can be good alternatives to the Factory Method pattern. We also explored the simplified version of this pattern that is used in daily practice. Although this pattern is more complex than the simplified version, it is important to understand the general case in order to understand when and where the Factory Method pattern is appropriate in a design.

Factory (object-oriented programming) Factory method pattern Implementation Interface (computing) Object (computer science) Java (programming language)

Opinions expressed by DZone contributors are their own.

Related

  • Generics in Java and Their Implementation
  • Spring Beans With Auto-Generated Implementations: How-To
  • User-Friendly API Publishing and Testing With Retrofit
  • How to Secure Apache Ignite From Scratch

Partner Resources


Comments

ABOUT US

  • About DZone
  • Send 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: