[Design pattern] Factory pattern (simple factory pattern, factory method pattern, abstract factory pattern) in detail

Note: This article is for learning reference only, please correct me if there are any mistakes or omissions!

Reference/article address:

  • https://zh.wikipedia.org/wiki/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%EF%BC%9A%E5%8F%AF%E5%A4%8D%E7%94%A8%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E8%BD%AF%E4%BB%B6%E7%9A%84%E5%9F%BA%E7%A1%80

  • https://design-patterns.readthedocs.io/zh_CN/latest/creational_patterns/simple_factory.html

  • https://refactoringguru.cn/design-patterns/abstract-factory

1. About GoF

  • "Design Patterns: Elements of Reusable Object-Oriented Software" (Design Patterns: Elements of Reusable Object-Oriented Software) is a book about design patterns in the field of software engineering, which proposes and summarizes standard solutions to some common software design problems schemes, known as software design patterns. The authors of the book are Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides, later known as the "Gang of Four" (Gang of Four, GoF), and the design patterns in the book are also known as "Gang of Four Design". Pattern" (Gang of Four design patterns).

  • The book describes 23 design patterns. The design patterns we usually refer to refer to these 23 design patterns.

  • However, in addition to the GoF23 design patterns, there are other design patterns, such as: JavaEE design patterns (DAO pattern, MVC pattern, etc.).

23 design patterns and their classification

Create an example

The creation example is all about how to create an instance. This set of examples can be divided into two groups: class creation examples and object creation examples. The class creation instance effectively uses the inheritance relationship between classes during the instantiation process, and the object creation paradigm uses proxies to complete its tasks.

structure example

This set of examples is all about class and object composition relationships.

Behavioral example

This set of examples is all about how objects communicate with each other.

2. Simple factory pattern (special form of factory method)

2.1 Schema Definition

Simple Factory Pattern: Also known as the Static Factory Method pattern, it belongs to the class creation pattern. In the simple factory pattern, instances of different classes can be returned according to different parameters. The simple factory pattern specifically defines a class to be responsible for creating instances of other classes, and the created instances usually have a common parent class.

2.2 Schema structure

  • Factory: factory role

    The factory role is responsible for implementing the internal logic of creating all instances

  • Product: abstract product role

    The abstract product role is the parent class of all objects created and is responsible for describing the public interface shared by all instances

  • ConcreteProduct: concrete product role

    The concrete product role is the creation target, and all created objects act as instances of some concrete class of this role.

2.3 Class Diagram

../_images/SimpleFactory.jpg

2.4 Implementation

The directory structure is as follows:

image-20230625163639652

Abstract product role:

package *com.hzzlovezq.abstractProduct*;

public abstract class Fruit {
    
    
    public abstract void sell();
}

Specific product roles:

package com.hzzlovezq.specificProduct;

import com.hzzlovezq.abstractProduct.Fruit;

public class Banana extends Fruit {
    
    
    @Override
    public void sell() {
    
    
        System.out.println("出售一个香蕉~");
    }
}
package com.hzzlovezq.specificProduct;

import com.hzzlovezq.abstractProduct.Fruit;

public class Orange extends Fruit {
    
    
    @Override
    public void sell() {
    
    
        System.out.println("出售一个橙子~");
    }
}

Factory role:

package com.hzzlovezq.factory;

import com.hzzlovezq.abstractProduct.Fruit;
import com.hzzlovezq.specificProduct.Banana;
import com.hzzlovezq.specificProduct.Orange;

public class FruitFactory {
    
    
    public static Fruit getFruit(String fruitType){
    
    
        if ("ORANGE".equals(fruitType)) {
    
    
            return new Orange();
        } else if ("BANANA".equals(fruitType)){
    
    
            return new Banana();
        } else {
    
    
            throw new RuntimeException("本店不出售该水果!");
        }
//        return null;
    }
}

Test code:

import com.hzzlovezq.abstractProduct.Fruit;
import com.hzzlovezq.factory.FruitFactory;
import org.junit.Test;

public class SimpleFactoryTest {
    
    
    @Test
    public void testSell(){
    
    
        Fruit orange = FruitFactory.getFruit("ORANGE");
        orange.sell();
        Fruit banana = FruitFactory.getFruit("BANANA");
        banana.sell();
    }
}

Test Results:

image-20230625164026077

2.5 Advantages and disadvantages of the simple factory model

advantage:

The client program does not need to care about the details of object creation. When it needs an object, it only needs to ask for it from the factory, which preliminarily realizes the separation of responsibilities. The client is only responsible for "consumption", and the factory is responsible for "production". Separation of production and consumption.

shortcoming:

  • The factory class concentrates all logical creations to form an omniscient and omnipotent class, some people call it the God class. Obviously the factory class is very critical, once something goes wrong, the whole system will be paralyzed.
  • The simple factory pattern violates the open-closed principle (OCP principle), and the factory class needs to be modified when the system is extended.

3. Factory method pattern

3.1 Schema Definition

The Factory Method Pattern (Factory Method Pattern) is also known as the factory pattern, also known as the Virtual Constructor (Virtual Constructor) pattern or the Polymorphic Factory (Polymorphic Factory) pattern, which belongs to the class creation pattern. In the factory method pattern, the factory parent class is responsible for defining the public interface for creating product objects, while the factory subclass is responsible for generating specific product objects. The purpose of this is to delay the instantiation of the product class to the factory subclass. That is, the factory subclass is used to determine which specific product class should be instantiated.

3.2 Schema structure

  • The Product will declare the interface. These interfaces are common to all objects constructed by the creator and its subclasses.

  • Concrete Products are different implementations of the product interface.

  • The Creator class declares a factory method that returns Product objects. The method's return object type must match the product interface.

    You can declare a factory method as an abstract method, forcing each subclass to implement the method differently. Alternatively, you can also return the default product type in the base factory method.

    Note that despite its name as creator, its primary responsibility is not to create products. Generally speaking, the creator class contains some core business logic related to the product. The factory method separates these logic processing from the concrete product class. For example, a large software development company has a programmer training department. However, the main job of these companies is to write code, not to produce programmers.

  • Concrete Creators will override the base factory method to return a different type of product.

    Note that not every call to the factory method will necessarily create a new instance. Factory methods can also return existing objects from caches, object pools, or other sources.

3.3 Class Diagram

Factory Method Pattern Structure

3.4 Implementation

The directory structure is as follows:

image-20230625171106704

Product (abstract product):

package *com.hzzlovezq.abstractProduct*;

public abstract class Fruit {
    
    
    public abstract void sell();
}

Specific products:

package com.hzzlovezq.specificProduct;

import com.hzzlovezq.abstractProduct.Fruit;

public class Banana extends Fruit {
    
    
    @Override
    public void sell() {
    
    
        System.out.println("Sold a banana~");
    }
}	
package com.hzzlovezq.specificProduct;

import com.hzzlovezq.abstractProduct.Fruit;

public class Orange extends Fruit {
    
    
    @Override
    public void sell() {
    
    
        System.out.println("Sold an orange~");
    }
}
package com.hzzlovezq.specificProduct;

import com.hzzlovezq.abstractProduct.Fruit;

public class Apple extends Fruit {
    
    
    @Override
    public void sell() {
    
    
        System.out.println("Sold an apple~");
    }
}

Creator (abstract factory):

package com.hzzlovezq.abstractFactory;

import com.hzzlovezq.abstractProduct.Fruit;

public interface FruitFactory {
    
    
    Fruit getFruit();
}

Concrete creator (concrete factory):

package com.hzzlovezq.specificFactory;

import com.hzzlovezq.abstractFactory.FruitFactory;
import com.hzzlovezq.abstractProduct.Fruit;
import com.hzzlovezq.specificProduct.Banana;

public class BananaFactory implements FruitFactory {
    
    

    @Override
    public Fruit getFruit() {
    
    
        return new Banana();
    }
}
package com.hzzlovezq.specificFactory;

import com.hzzlovezq.abstractFactory.FruitFactory;
import com.hzzlovezq.abstractProduct.Fruit;
import com.hzzlovezq.specificProduct.Orange;

public class OrangeFactory implements FruitFactory {
    
    
    @Override
    public Fruit getFruit() {
    
    
        return new Orange();
    }
}
package com.hzzlovezq.specificFactory;

import com.hzzlovezq.abstractFactory.FruitFactory;
import com.hzzlovezq.abstractProduct.Fruit;
import com.hzzlovezq.specificProduct.Apple;

public class AppleFactory implements FruitFactory {
    
    
    @Override
    public Fruit getFruit() {
    
    
        return new Apple();
    }
}

Test code:

import com.hzzlovezq.abstractFactory.FruitFactory;
import com.hzzlovezq.abstractProduct.Fruit;
import com.hzzlovezq.specificFactory.AppleFactory;
import com.hzzlovezq.specificFactory.BananaFactory;
import com.hzzlovezq.specificFactory.OrangeFactory;
import org.junit.Test;

public class MethodFactoryTest {
    
    
    @Test
    public void methodTest(){
    
    
        FruitFactory bananaFactory = new BananaFactory();
        Fruit banana = bananaFactory.getFruit();
        banana.sell();
        FruitFactory orangeFactory = new OrangeFactory();
        Fruit orange = orangeFactory.getFruit();
        orange.sell();
        FruitFactory appleFactory = new AppleFactory();
        Fruit apple = appleFactory.getFruit();
        apple.sell();
    }
}

Test Results:

image-20230625171503377

3.5 Advantages and disadvantages of the factory method pattern

advantage:

  • You can avoid tight coupling between the creator and the concrete product.
  • Single Responsibility Principle . You can put the product creation code in a single place in the program, making the code easier to maintain.
  • The principle of opening and closing . You can introduce new product types into your program without changing existing client code.

shortcoming:

Applying the factory method pattern requires the introduction of many new subclasses, and the code may become more complex as a result. The best case scenario is to introduce the pattern into the existing hierarchy of creator classes.

4. Abstract factory pattern

4.1 Schema Definition

Abstract Factory Pattern: Provides an interface for creating a series of related or interdependent objects without specifying their specific classes. The abstract factory pattern, also known as the Kit pattern, belongs to the object creation pattern.

4.2 Schema structure

  • Abstract Product (Abstract Product) declares an interface for a set of distinct but related products that make up a family of products.
  • Concrete Products are implementations of many different types of abstract products. All variants (Victorian/Modern) must implement a corresponding abstract product (chair/sofa).
  • The Abstract Factory interface declares a set of methods for creating various abstract products.
  • The specific factory (Concrete Factory) realizes the construction method of the abstract factory. Each concrete plant corresponds to a specific product variant and only this product variant is created.

4.3 Class Diagram

../_images/AbatractFactory.jpg

4.4 Implementation

The directory structure is as follows:
insert image description here

Abstract factory:

package *com.hzzlovezq.abstractFactory*;

import *com.hzzlovezq.abstractProduct.*Fruit;
import *com.hzzlovezq.abstractProduct.*Weapon;

public abstract class AbstractFactory {
    
    
    public abstract Fruit getFruit(*String* *fruitType*);
    public abstract Weapon getWeapon(*String* *weaponType*);
}

Specific factory:

package com.hzzlovezq.concreteFactory;

import com.hzzlovezq.abstractFactory.AbstractFactory;
import com.hzzlovezq.abstractProduct.Fruit;
import com.hzzlovezq.abstractProduct.Weapon;
import com.hzzlovezq.concreteProduct.Apple;
import com.hzzlovezq.concreteProduct.Orange;

public class FruitFactory extends AbstractFactory {
    
    
    @Override
    public Fruit getFruit(String fruitType) {
    
    
        if ("ORANGE".equals(fruitType)) {
    
    
            return new Orange();
        } else if ("APPLE".equals(fruitType)) {
    
    
            return new Apple();
        } else {
    
    
            throw new RuntimeException("不出售该类水果!");
        }
    }

    @Override
    public Weapon getWeapon(String weaponType) {
    
    
        return null;
    }
}

package com.hzzlovezq.concreteFactory;

import com.hzzlovezq.abstractFactory.AbstractFactory;
import com.hzzlovezq.abstractProduct.Fruit;
import com.hzzlovezq.abstractProduct.Weapon;
import com.hzzlovezq.concreteProduct.Apple;
import com.hzzlovezq.concreteProduct.Dagger;
import com.hzzlovezq.concreteProduct.Gun;
import com.hzzlovezq.concreteProduct.Orange;

public class WeaponFactory extends AbstractFactory {
    
    
    @Override
    public Fruit getFruit(String fruitType) {
    
    
        return null;
    }

    @Override
    public Weapon getWeapon(String weaponType) {
    
    
        if ("GUN".equals(weaponType)) {
    
    
            return new Gun();
        } else if ("DAGGER".equals(weaponType)) {
    
    
            return new Dagger();
        } else {
    
    
            throw new RuntimeException("不出售该类水果!");
        }
    }
}

Abstract product:

package com.hzzlovezq.abstractProduct;

public abstract class Fruit {
    
    
    public abstract void sell();
}

package com.hzzlovezq.abstractProduct;

public abstract class Weapon {
    
    
    public abstract void attack();
}

Specific products:

package com.hzzlovezq.concreteProduct;

import com.hzzlovezq.abstractProduct.Weapon;

public class Gun extends Weapon {
    
    

    @Override
    public void attack() {
    
    
        System.out.println("给你一梭子~");
    }
}

package com.hzzlovezq.concreteProduct;

import com.hzzlovezq.abstractProduct.Weapon;

public class Dagger extends Weapon {
    
    
    @Override
    public void attack() {
    
    
        System.out.println("戳死你~");
    }
}

package com.hzzlovezq.concreteProduct;

import com.hzzlovezq.abstractProduct.Fruit;

public class Apple extends Fruit {
    
    
    @Override
    public void sell() {
    
    
        System.out.println("卖了一个苹果~");
    }
}
package com.hzzlovezq.concreteProduct;

import com.hzzlovezq.abstractProduct.Fruit;

public class Orange extends Fruit {
    
    
    @Override
    public void sell() {
    
    
        System.out.println("卖了一个橙子~");
    }
}

Test code:

import com.hzzlovezq.abstractFactory.AbstractFactory;
import com.hzzlovezq.abstractProduct.Fruit;
import com.hzzlovezq.abstractProduct.Weapon;
import com.hzzlovezq.concreteFactory.FruitFactory;
import com.hzzlovezq.concreteFactory.WeaponFactory;
import org.junit.Test;

public class AbstractFactoryTest {
    
    
    @Test
    public void abstractTest(){
    
    
        // 客户端调用方法时只面向AbstractFactory调用方法。
        AbstractFactory factory = new WeaponFactory(); // 注意:这里的new WeaponFactory()可以采用 简单工厂模式 进行隐藏。
        Weapon gun = factory.getWeapon("GUN");
        Weapon dagger = factory.getWeapon("DAGGER");
        gun.attack();
        dagger.attack();

        AbstractFactory factory1 = new FruitFactory(); // 注意:这里的new FruitFactory()可以采用 简单工厂模式 进行隐藏。
        Fruit orange = factory1.getFruit("ORANGE");
        Fruit apple = factory1.getFruit("APPLE");
        orange.sell();
        apple.sell();
    }
}

operation result:

image-20230625175006770

4.5 Advantages and disadvantages of the abstract factory pattern

advantage:

When multiple objects in a product family are designed to work together, it guarantees that clients always use only objects from the same product family.

shortcoming:

It is very difficult to expand the product family. To add a certain product of a series, it is necessary to add code in the AbstractFactory and add code in the concrete.

Guess you like

Origin blog.csdn.net/m0_47015897/article/details/131387369