This is the second day of my participation in the August More text Challenge. For details, see:August is more challenging

preface

The Factory Design Pattern is also a common creation Pattern. It provides the best way to create objects without exposing the creation logic to the client, and instead uses a common interface to specify newly created objects.

The factory pattern can be broken down into three types: simple factory, factory methods, which are more common in real projects, and abstract factory methods, which are less common in real projects.

Simple Factory

What is the simple factory pattern, let’s use an example to explain.

1. Create an Animal interface

/ * * *@Author:
 * @Description: Animal interface */
public interface Animal {
    void eat(a);
}
Copy the code

2. Dog implements animal interface

/ * * *@Author:
 * @DescriptionDog: * /
public class Dog implements Animal {
    @Override
    public void eat(a) {
        System.out.println("Dogs eat bones."); }}Copy the code

3. Cat to achieve animal interface

/ * * *@Author: 
 * @Description: Cats */
public class Cat implements Animal {
    @Override
    public void eat(a) {
        System.out.println("Cats eat dried fish."); }}Copy the code

4. AnimalFactory

/ * * *@Author:
 * @Description: Animal factory class (first implementation) */
public class AnimalFactory {
    public Animal createAnimal(String animalType) {
        Animal animal = null;
        if ("dog".equals(animalType)) {
            animal =  new Dog();
        } else if ("cat".equals(animalType)) {
            animal =  new Cat();
        }
        returnanimal; }}Copy the code

5. Test

/ * * *@Author: 
 * @Description: * / test
public class Test {
    public static void main(String[] args) {
        AnimalFactory animalFactory = new AnimalFactory();
        Animal dog = animalFactory.createAnimal("dog");
        Animal cat = animalFactory.createAnimal("cat"); dog.eat(); cat.eat(); }}Copy the code

Here are the results:

Dogs eat bones and cats eat dried fish


In the above code implementation, we create a new animal every time we call the AnimalFactory’s createAnimal() method. In fact, to save memory and object creation time, if animal can be reused, the Parser can be pre-created and cached. When the createAnimal() method is called, we take the Animal object from the cache and use it directly.

This is similar to the combination of the singleton pattern and the simple factory pattern. The code implementation is as follows:

/ * * *@Author:
 * @Description: Animal factory class (second implementation) */
public class AnimalFactory {

    private static final Map<String, Animal> cachedAnimals = new HashMap<>();
    static {
        cachedAnimals.put("dog".new Dog());
        cachedAnimals.put("cat".new Cat());
    }
    public Animal createAnimal(String animalType) {
        returncachedAnimals.get(animalType); }}Copy the code

If we want to add a new animal, we must change the AnimalFactory code. Does this violate the open closed principle? In fact, if you don’t add animals frequently, and only occasionally modify the AnimalFactory code, a slight deviation from the open closed principle is acceptable.

In the example of the first implementation, if in the AnimalFactory class has only two branches and can be written as such, but if there are many if branch logic, consider using polymorphic or design patterns instead of if branch logic.

Factory Method

If we had to remove the if branching logic from the AnimalFactory class, the classic approach would be to use polymorphism. In accordance with the implementation of polymorphic ideas, the above code for refactoring.

/ * * *@Author:
 * @Description: Animal factory interface */
public interface IAnimalFactory {
    Animal createAnimal(a);
}

/ * * *@Author:
 * @Description: Dog factory */
public class DogFactory implements IAnimalFactory {
    @Override
    public Animal createAnimal(a) {
        return newDog(); }}/ * * *@Author:
 * @Description: Cat factory */
public class CatFactory implements IAnimalFactory {
    @Override
    public Animal createAnimal(a) {
        return newCat(); }}Copy the code

In fact, this is a typical code implementation of the factory method pattern. When we add an animal, we only need to add a Factory class that implements the IAnimalFactory interface. Therefore, the factory method pattern is more consistent with the open closed principle than the simple factory pattern.

From the above implementation, everything looks perfect, but in fact there is still a problem with the use of these factory classes.

/ * * *@Author:
 * @Description: * /
public class Test {
    public static void main(String[] args) {
        Animal animal = null;
        if ("dog".equals(getAnimalType())) {
            DogFactory dogFactory = new DogFactory();
            animal = dogFactory.createAnimal();
        } else if ("cat".equals(getAnimalType())) {
            CatFactory catFactory = new CatFactory();
            animal = catFactory.createAnimal();
        }
        animal.eat();
    }

    public static String getAnimalType(a) {
        return "dog"; }}Copy the code

Looking at the code above, the design seems to have become more complicated when using the factory class. To solve this problem, we can create another simple factory for the factory class, the factory’s factory, to create factory objects.

The AnimalFactoryMap class is the factory class that created the factory object, and the getAnimalFactory() method returns the cached singleton factory object.

/ * * *@Author:
 * @Description: factory factory, you don't need to create a new factory object every time */
public class AnimalFactoryMap {
    private static final Map<String, IAnimalFactory> cachedFactories = new HashMap<>();

    static {
        cachedFactories.put("dog".new DogFactory());
        cachedFactories.put("cat".new CatFactory());
    }

    public static IAnimalFactory getAnimalFactory(String animalType) {
        returncachedFactories.get(animalType.toLowerCase()); }}Copy the code

Using a factory class:

/ * * *@Author:
 * @Description: * /
public class Test {
    public static void main(String[] args) {
        IAnimalFactory animalFactory = AnimalFactoryMap.getAnimalFactory(getAnimalType());
        Animal animal = animalFactory.createAnimal();
        animal.eat();
    }

    public static String getAnimalType(a) {
        return "cat"; }}Copy the code

This way, when we need to add new animals such as birds, we just create new Bird and BirdFactory classes and add the new BirdFactory object to the cachedFactories in the AnimalFactoryMap class. Code changes are not much, basically in line with the open closed principle.

In fact, if each Factory class does a simple new operation, the functionality is very thin and there is no need to design it as a separate class, so the simple Factory pattern is more appropriate than the Factory method pattern in this application scenario. The factory method pattern is recommended when the object creation logic is complex.

The abstract factory

Abstract work mode has a special application scenario and is not commonly used in the first two types. The above example is no longer appropriate and may need to be rewritten as an abstract factory pattern.

Pay attention to

The factory pattern, as a creation pattern, can be used anywhere complex objects need to be generated. One thing to note is that the factory pattern is good for complex objects, while simple objects, especially those that can be created by simply using new, do not need the factory pattern. If you use the factory pattern, you need to introduce a factory class, which increases the complexity of the system.