Design pattern Factory Method Pattern

example

Let's start with an example


Picture reader

Design a program that can read different types of picture formats. For each picture format, a picture reader is required. For example, GIF reader is used to read GIF pictures and JPG reader is used to read JPG pictures. The flexibility and scalability of the system should be fully considered


Simple factory solutions

  • Solutions using simple factories are roughly as follows:
  • Img.java
/**
 * @Description picture
 */
public abstract class Img {
    /**
     * Get picture source
     */
    public abstract void getSource();
}
  • GifImg.java
/**
 * @Description GIF picture
 */
public class GifImg extends Img {
    @Override
    public void getSource() {
        System.out.println("obtain GIF Picture source");
    }
}
  • JpgImg.java
/**
 * @Description JPG picture
 */
public class JpgImg extends Img {
    @Override
    public void getSource() {
        System.out.println("obtain JPG Picture source");
    }
}
  • ImgFactory.java
/**
 * @Description Picture simple factory class
 */
public class ImgFactory {
    /**
     * Simple factory to get pictures of products
     * @param args
     * @return
     */
    public static Img getImg(String args) {
        Img img = null;

        if (args.equalsIgnoreCase("gif")) {
            img = new GifImg();
        } else if (args.equalsIgnoreCase("jpg")) {
            img = new JpgImg();
        }

        return img;
    }
}
  • Test.java
/**
 * @Description Picture simple factory test class
 */
public class Test {
    public static void main(String[] args) {
        Img img = ImgFactory.getImg("gif");
        if (img == null) {
            return;
        }
        img.getSource();

        img = ImgFactory.getImg("jpg");
        if (img == null) {
            return;
        }
        img.getSource();
    }
}
  • Operation results:
obtain GIF Picture source
 obtain JPG Picture source
  • Through the above code, the simple factory method mode only provides one factory class to create different products through different parameters. This factory class is in the central position of instantiating the product class. Its biggest disadvantage is that when a new product is to be added to the system, the source code of the factory class must be modified, which violates the opening and closing principle, If the above code needs to add a PNG product class, it is necessary to modify ImgFactory. How to add new products without affecting the existing code? Next, the factory method mode is introduced. In the factory method mode, a unified factory class is no longer provided to create all product objects, but different factories are provided for different products, The system provides a factory hierarchy corresponding to the product hierarchy

Factory method model

concept

  • Factory Method Pattern: define an interface for creating objects and let subclasses decide which class to instantiate. The Factory Method Pattern delays the instantiation of a class to its subclasses
  • Factory method pattern is also referred to as Factory Pattern

Factory method solutions

Img.java

/**
 * @Description Picture products
 */
public abstract class Img {
    /**
     * Get picture source
     */
    public abstract void getSource();
}
  • ImgFactory.java
/**
 * @Description Picture factory class (factory method mode)
 */
public interface ImgFactory {
    /**
     * Get picture products
     * @return
     */
    Img getImg();
}
  • GifImg.java
/**
 * @Description GIF Picture products
 */
public class GifImg extends Img {
    @Override
    public void getSource() {
        System.out.println("obtain GIF Picture source");
    }
}
  • GifImgFactory.java
/**
 * @Description Gif Picture factory class
 */
public class GifImgFactory implements ImgFactory {
    @Override
    public Img getImg() {
        return new GifImg();
    }
}
  • JpgImg.java
/**
 * @Description JPG Picture products
 */
public class JpgImg extends Img {
    @Override
    public void getSource() {
        System.out.println("obtain JPG Picture source");
    }
}
  • JpgImgFactory.java
/**
 * @Description Gif Picture factory class
 */
public class JpgImgFactory implements ImgFactory {
    @Override
    public Img getImg() {
        return new JpgImg();
    }
}
  • Test.java
/**
 * @Description Factory method pattern test class
 */
public class Test {
    public static void main(String[] args) {
        ImgFactory imgFetchFactory = new GifImgFactory();
        Img imgFetch = imgFetchFactory.getImg();
        imgFetch.getSource();

        imgFetchFactory = new JpgImgFactory();
        imgFetch = imgFetchFactory.getImg();
        imgFetch.getSource();
    }
}
  • The output is as follows:
obtain GIF Picture source
 obtain JPG Picture source
  • The class diagram is as follows:

  • The factory method pattern provides an abstract factory interface to declare the abstract factory method (ImgFactory), and its subclasses (JpgImgFactory, GifImgFactory) implement the factory method and create specific product objects. Compared with the simple factory pattern, the most important difference of the factory method pattern is the introduction of the abstract factory role

Evolution of scheme (configuration file)

  • The client test Java calling code can also be improved. Through configuration file + reflection, a new image reading method can be changed and added without modifying the client code

  • config.properties

factoryMethod.className=GifImgFactory
  • PropertiesUtil.java
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * @Description Properties Tool class
 */
public class PropertiesUtil {

    /**
     * Read value according to key
     * @Description: Relative path. The properties file needs to be in the classpath directory,
     *               For example: config Properties in package com coisini. util,
     *               / config.com/util properties
     * @param filePath
     * @param keyWord
     * @return String
     * @throws
     */
     private static String getProperties(String filePath, String keyWord){
        Properties prop = new Properties();
        String value = null;
        try {
            InputStream inputStream = PropertiesUtil.class.getResourceAsStream(filePath);
            prop.load(inputStream);
            value = prop.getProperty(keyWord);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return value;
     }

    /**
     * Extract the class name according to the configuration file and return the instance object
     * @param filePath
     * @param keyWord
     * @param packagePath
     * @return
     */
     private static Object getBean(String filePath, String keyWord, String packagePath) {
         try {
             String className = getProperties(filePath, keyWord);
             Class<?> c = Class.forName(packagePath + className);
             return c.newInstance();
         } catch (Exception e) {
             e.printStackTrace();
             return null;
         }

     }

    /**
     * Get factory method instance object
     * @return
     */
     public static Object getFactoryMethodBean() {
         return getBean("/com/coisini/design/util/config.properties",
                 "factoryMethod.className",
                 "com.coisini.design.pattern.creational.factorymethod.v3.");
     }

}
  • Test.java
/**
 * @Description Factory method pattern test class (configuration file reflection implementation method)
 */
public class Test {
    public static void main(String[] args) {
        ImgFactory imgFactory = (ImgFactory) PropertiesUtil.getFactoryMethodBean();
        Img img = imgFactory.getImg();
        img.getSource();
    }
}
  • Through the above configuration file + reflection implementation, the client code does not need to use the new keyword to create the factory object, but saves the class name of the specific factory class in the configuration file, obtains the class name string by reading the configuration file, and then uses the reflection mechanism to generate the object according to the class name character string

summary

  • advantage
1,Users only need to care about the factory corresponding to the required product, not the creation details
2,Adding new products conforms to the opening and closing principle and improves scalability
  • shortcoming
1,The number of classes is easy to be too many, which increases the complexity
2,It increases the abstraction and understanding difficulty of the system
  • Applicable scenario
1,Creating objects requires a lot of duplicate code
2,The client (application layer) does not depend on the details of how the product class instance is created and implemented
3,A class specifies which object to create through subclasses

Source code


- End - -Personal study notes-

Added by brob on Thu, 03 Mar 2022 00:57:20 +0200