223 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			223 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| ---
 | ||
| layout: pattern
 | ||
| title: Abstract Factory
 | ||
| folder: abstract-factory
 | ||
| permalink: /patterns/abstract-factory/
 | ||
| categories: Creational
 | ||
| tags:
 | ||
|  - Gang of Four
 | ||
| ---
 | ||
| 
 | ||
| ## Also known as
 | ||
| 
 | ||
| Kit
 | ||
| 
 | ||
| ## Intent
 | ||
| 
 | ||
| Provide an interface for creating families of related or dependent
 | ||
| objects without specifying their concrete classes.
 | ||
| 
 | ||
| ## Explanation
 | ||
| 
 | ||
| Real world example
 | ||
| 
 | ||
| > To create a kingdom we need objects with a common theme. Elven kingdom needs an Elven king, Elven castle and Elven army whereas Orcish kingdom needs an Orcish king, Orcish castle and Orcish army. There is a dependency between the objects in the kingdom.
 | ||
| 
 | ||
| In plain words
 | ||
| 
 | ||
| > A factory of factories; a factory that groups the individual but related/dependent factories together without specifying their concrete classes.
 | ||
| 
 | ||
| Wikipedia says
 | ||
| 
 | ||
| > The abstract factory pattern provides a way to encapsulate a group of individual factories that have a common theme without specifying their concrete classes
 | ||
| 
 | ||
| **Programmatic Example**
 | ||
| 
 | ||
| Translating the kingdom example above. First of all we have some interfaces and implementation for the objects in the 
 | ||
| kingdom.
 | ||
| 
 | ||
| ```java
 | ||
| public interface Castle {
 | ||
|   String getDescription();
 | ||
| }
 | ||
| 
 | ||
| public interface King {
 | ||
|   String getDescription();
 | ||
| }
 | ||
| 
 | ||
| public interface Army {
 | ||
|   String getDescription();
 | ||
| }
 | ||
| 
 | ||
| // Elven implementations ->
 | ||
| public class ElfCastle implements Castle {
 | ||
|   static final String DESCRIPTION = "This is the Elven castle!";
 | ||
|   @Override
 | ||
|   public String getDescription() {
 | ||
|     return DESCRIPTION;
 | ||
|   }
 | ||
| }
 | ||
| public class ElfKing implements King {
 | ||
|   static final String DESCRIPTION = "This is the Elven king!";
 | ||
|   @Override
 | ||
|   public String getDescription() {
 | ||
|     return DESCRIPTION;
 | ||
|   }
 | ||
| }
 | ||
| public class ElfArmy implements Army {
 | ||
|   static final String DESCRIPTION = "This is the Elven Army!";
 | ||
|   @Override
 | ||
|   public String getDescription() {
 | ||
|     return DESCRIPTION;
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| // Orcish implementations similarly -> ...
 | ||
| 
 | ||
| ```
 | ||
| 
 | ||
| Then we have the abstraction and implementations for the kingdom factory
 | ||
| 
 | ||
| ```java
 | ||
| public interface KingdomFactory {
 | ||
|   Castle createCastle();
 | ||
|   King createKing();
 | ||
|   Army createArmy();
 | ||
| }
 | ||
| 
 | ||
| public class ElfKingdomFactory implements KingdomFactory {
 | ||
|   public Castle createCastle() {
 | ||
|     return new ElfCastle();
 | ||
|   }
 | ||
|   public King createKing() {
 | ||
|     return new ElfKing();
 | ||
|   }
 | ||
|   public Army createArmy() {
 | ||
|     return new ElfArmy();
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| public class OrcKingdomFactory implements KingdomFactory {
 | ||
|   public Castle createCastle() {
 | ||
|     return new OrcCastle();
 | ||
|   }
 | ||
|   public King createKing() {
 | ||
|     return new OrcKing();
 | ||
|   }
 | ||
|   public Army createArmy() {
 | ||
|     return new OrcArmy();
 | ||
|   }
 | ||
| }
 | ||
| ```
 | ||
| 
 | ||
| Now we have our abstract factory that lets us make family of related objects i.e. Elven kingdom factory creates Elven castle, king and army etc.
 | ||
| 
 | ||
| ```java
 | ||
| var factory = new ElfKingdomFactory();
 | ||
| var castle = factory.createCastle();
 | ||
| var king = factory.createKing();
 | ||
| var army = factory.createArmy();
 | ||
| 
 | ||
| castle.getDescription();
 | ||
| king.getDescription();
 | ||
| army.getDescription();
 | ||
| ```
 | ||
| 
 | ||
| Program output:
 | ||
| 
 | ||
| ```java
 | ||
| This is the Elven castle!
 | ||
| This is the Elven king!
 | ||
| This is the Elven Army!
 | ||
| ```
 | ||
| 
 | ||
| Now, we can design a factory for our different kingdom factories. In this example, we created FactoryMaker, responsible for returning an instance of either ElfKingdomFactory or OrcKingdomFactory.  
 | ||
| The client can use FactoryMaker to create the desired concrete factory which, in turn, will produce different concrete objects (Army, King, Castle).  
 | ||
| In this example, we also used an enum to parameterize which type of kingdom factory the client will ask for.
 | ||
| 
 | ||
| ```java
 | ||
| public static class FactoryMaker {
 | ||
| 
 | ||
|   public enum KingdomType {
 | ||
|     ELF, ORC
 | ||
|   }
 | ||
| 
 | ||
|   public static KingdomFactory makeFactory(KingdomType type) {
 | ||
|     switch (type) {
 | ||
|       case ELF:
 | ||
|         return new ElfKingdomFactory();
 | ||
|       case ORC:
 | ||
|         return new OrcKingdomFactory();
 | ||
|       default:
 | ||
|         throw new IllegalArgumentException("KingdomType not supported.");
 | ||
|     }
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| public static void main(String[] args) {
 | ||
|   var app = new App();
 | ||
| 
 | ||
|   LOGGER.info("Elf Kingdom");
 | ||
|   app.createKingdom(FactoryMaker.makeFactory(KingdomType.ELF));
 | ||
|   LOGGER.info(app.getArmy().getDescription());
 | ||
|   LOGGER.info(app.getCastle().getDescription());
 | ||
|   LOGGER.info(app.getKing().getDescription());
 | ||
| 
 | ||
|   LOGGER.info("Orc Kingdom");
 | ||
|   app.createKingdom(FactoryMaker.makeFactory(KingdomType.ORC));
 | ||
|   -- similar use of the orc factory
 | ||
| }
 | ||
| ```
 | ||
| 
 | ||
| ## Class diagram
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| ## Applicability
 | ||
| 
 | ||
| Use the Abstract Factory pattern when
 | ||
| 
 | ||
| * The system should be independent of how its products are created, composed and represented
 | ||
| * The system should be configured with one of multiple families of products
 | ||
| * The family of related product objects is designed to be used together, and you need to enforce this constraint
 | ||
| * You want to provide a class library of products, and you want to reveal just their interfaces, not their implementations
 | ||
| * The lifetime of the dependency is conceptually shorter than the lifetime of the consumer.
 | ||
| * You need a run-time value to construct a particular dependency
 | ||
| * You want to decide which product to call from a family at runtime.
 | ||
| * You need to supply one or more parameters only known at run-time before you can resolve a dependency.
 | ||
| * When you need consistency among products
 | ||
| * You don’t want to change existing code when adding new products or families of products to the program.
 | ||
| 
 | ||
| Example use cases	
 | ||
| 
 | ||
| * Selecting to call to the appropriate implementation of FileSystemAcmeService or DatabaseAcmeService or NetworkAcmeService at runtime.
 | ||
| * Unit test case writing becomes much easier
 | ||
| * UI tools for different OS
 | ||
| 
 | ||
| ## Consequences:
 | ||
| 
 | ||
| * Dependency injection in java hides the service class dependencies that can lead to runtime errors that would have been caught at compile time.
 | ||
| * While the pattern is great when creating predefined objects, adding the new ones might be challenging.
 | ||
| * The code becomes more complicated than it should be, since a lot of new interfaces and classes are introduced along with the pattern.
 | ||
| 
 | ||
| ## Tutorial
 | ||
| 
 | ||
| * [Abstract Factory Pattern Tutorial](https://www.journaldev.com/1418/abstract-factory-design-pattern-in-java) 
 | ||
| 
 | ||
| ## Known uses
 | ||
| 
 | ||
| * [javax.xml.parsers.DocumentBuilderFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/parsers/DocumentBuilderFactory.html)
 | ||
| * [javax.xml.transform.TransformerFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/transform/TransformerFactory.html#newInstance--)
 | ||
| * [javax.xml.xpath.XPathFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/xpath/XPathFactory.html#newInstance--)
 | ||
| 
 | ||
| ## Related patterns
 | ||
| 
 | ||
| [Factory Method](https://java-design-patterns.com/patterns/factory-method/)
 | ||
| [Factory Kit](https://java-design-patterns.com/patterns/factory-kit/)
 | ||
| 
 | ||
| ## Credits
 | ||
| 
 | ||
| * [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
 | ||
| * [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
 |