This is the 20th day of my participation in the Genwen Challenge

The factory pattern

Simple Factory model

For example, we have a restaurant, the chef of the restaurant has the following steps: 1, choose ingredients, 2, cut vegetables, 3, stir fry vegetables, 4, plate 5, serve food

Now 2, 3, 4, 5 is the same no matter what dish it is. Let’s write it this way, right

Cook

package com.wangscaler.factory;

/ * * *@author wangscaler
 * @date2021.06.22 16:17 * /
public abstract class Cook {
    protected String name;

    public abstract void prepare(a);

    public void cut(a) {
        System.out.println(name + "The material has been cut.");
    }

    public void fry(a) {
        System.out.println(name + "Fired.");
    }

    public void dish(a) {
        System.out.println(name + "It's on a plate.");
    }

    public void serve(a) {
        System.out.println(name + "Served on the table.");
    }

    public void setName(String name) {
        this.name = name; }}Copy the code

FishFlavoredPork

package com.wangscaler.factory;
/ * * *@author wangscaler
 * @date2021.06.22 16:17 * /
public class FishFlavoredPork extends Cook {
    @Override
    public void prepare(a) {
        System.out.println("Carrots, shredded meat, sweet sauce and so on."); }}Copy the code

KungPaoChicken

package com.wangscaler.factory;
/ * * *@author wangscaler
 * @date2021.06.22 16:17 * /
public class KungPaoChicken extends Cook {
    @Override
    public void prepare(a) {
        System.out.println("Diced chicken, cucumber and peanuts are ready."); }}Copy the code

Order

package com.wangscaler.factory;

import java.io.BufferedReader;
import java.io.InputStreamReader;

/ * * *@author wangscaler
 * @date2021.06.22 16:17 * /
public class Order {
    public Order(a) {
        Cook cook = null;
        String type;
        do {
            type = geType();
            if (type.equals(Menu.FISHFLAVOREDPORK.getName())) {
                cook = new FishFlavoredPork();
                cook.setName("Shredded pork with fish flavor");
            } else if (type.equals(Menu.KUNGPAOCHICKEN.getName())) {
                cook = new KungPaoChicken();
                cook.setName("Kung Pao Chicken");
            } else if (type.equals(Menu.EOF.getName())) {
                break;
            } else {
                break;
            }
            cook.prepare();
            cook.cut();
            cook.fry();
            cook.dish();
            cook.serve();
        } while (true);
    }

    public enum Menu {
        /** ** * Fish fragrant pork */
        FISHFLAVOREDPORK("fishflavoredpork"),
        /** * Kung pao chicken */
        KUNGPAOCHICKEN("kungpaochicken"),
        /** * End identifier */
        EOF("EOF");
        private String name;

        Menu(String name) {
            this.name = name;
        }

        public String getName(a) {
            returnname; }}private String geType(a) {
        try {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("Please enter your order, ending with EOF.");
            String str = bufferedReader.readLine();
            return str;
        } catch (Exception e) {
            e.printStackTrace();
            return ""; }}}Copy the code

main

package com.wangscaler.factory;
/ * * *@author wangscaler
 * @date2021.06.22 16:17 * /
public class SimpleFactory {
    public static void main(String[] args) {
     Order order =newOrder(); }}Copy the code

The result of this run is that everything is fine

Please enter your order, ending with EOF kungpaochicken, cucumber, We've got the peanuts and all the other ingredients we've got the kung pao chicken we've got the chopped kung pao chicken we've got the stir-fried kung pao chicken we've got the kung pao chicken on the plate we've got the kung pao chicken on the table please type in your order, ending with EOF Sweet bean sauce and other materials ready Braised pork material is cut Braised pork is ok Braised shredded meat into the dish Braised pork on the table Please enter your main points to EOF to end the EOFCopy the code

However, when we add new recipes to the menu, we not only need to add new classes, but also need to modify the Order class. If there are many restaurants, we may need to write many orders, which is not only troublesome to change, but also violates the open and closed principle of our design principle, which is open for expansion and closed for modification.

Modify the Order

package com.wangscaler.factory;

import java.io.BufferedReader;
import java.io.InputStreamReader;

/ * * *@author wangscaler
 * @date2021.06.22 16:17 * /
public class Order {
    // public Order() {
// Cook cook = null;
// String type;
// do {
// type = geType();
// if (type.equals(Menu.FISHFLAVOREDPORK.getName())) {
// cook = new FishFlavoredPork();
// cook.setName(" fish meat fillet ");
// } else if (type.equals(Menu.KUNGPAOCHICKEN.getName())) {
// cook = new KungPaoChicken();
// cook.setName(" kung pao chicken ");
// } else if (type.equals(Menu.EOF.getName())) {
// break;
// } else {
// break;
/ /}
// cook.prepare();
// cook.cut();
// cook.fry();
// cook.dish();
// cook.serve();
// } while (true);
/ /}
    SimpleFactory simpleFactory;
    Cook cook = null;

    public Order(SimpleFactory simpleFactory) {
        setFactory(simpleFactory);
    }

    public void setFactory(SimpleFactory simpleFactory) {
        String type = "";
        this.simpleFactory = simpleFactory;
        do {
            type = geType();
            cook = this.simpleFactory.createCook(type);
            if(cook ! =null) {
                cook.prepare();
                cook.cut();
                cook.fry();
                cook.dish();
                cook.serve();
            } else {
                break; }}while (true);
    }

    private String geType(a) {
        try {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("Please enter your order, ending with EOF.");
            String str = bufferedReader.readLine();
            return str;
        } catch (Exception e) {
            e.printStackTrace();
            return ""; }}}Copy the code

Modify SimpleFactory

package com.wangscaler.factory;

/ * * *@author wangscaler
 * @date2021.06.22 16:17 * /
public class SimpleFactory {
    public Cook createCook(String type) {
        Cook cook = null;
        if (type.equals(SimpleFactory.Menu.FISHFLAVOREDPORK.getName())) {
            cook = new FishFlavoredPork();
            cook.setName("Shredded pork with fish flavor");
        } else if (type.equals(SimpleFactory.Menu.KUNGPAOCHICKEN.getName())) {
            cook = new KungPaoChicken();
            cook.setName("Kung Pao Chicken");
        }
        return cook;
    }
  public enum Menu {
        /** ** * Fish fragrant pork */
        FISHFLAVOREDPORK("fishflavoredpork"),
        /** * Kung pao chicken */
        KUNGPAOCHICKEN("kungpaochicken"),
        /** * End identifier */
        EOF("EOF");
        private String name;

        Menu(String name) {
            this.name = name;
        }

        public String getName(a) {
            returnname; }}public static void main(String[] args) {
        new Order(newSimpleFactory()); }}Copy the code

When we add a new menu, we only need to add a new menu class and modify the factory without affecting the Order. Simple factory mode is also called static factory mode because our code can be modified to

The SimpleFactory createCook method adds static and then changes Order

package com.wangscaler.factory;

import java.io.BufferedReader;
import java.io.InputStreamReader;

/ * * *@author wangscaler
 * @date2021.06.22 16:17 * /
public class Order1 {
    Cook cook = null;
    String type = "";

    public Order1(a) {
        do {
            type = geType();
            cook = SimpleFactory.createCook(type);
            if(cook ! =null) {
                cook.prepare();
                cook.cut();
                cook.fry();
                cook.dish();
                cook.serve();
            } else {
                break; }}while (true);
    }

    private String geType(a) {
        try {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("Please enter your order, ending with EOF.");
            String str = bufferedReader.readLine();
            return str;
        } catch (Exception e) {
            e.printStackTrace();
            return ""; }}}Copy the code

Since SimpleFactory produces all the dishes, if the factory has a problem, so does everything it produces. In the following pattern, object instantiation is deferred to subclasses.

Factory method pattern

For example, now we not only have dishes, but also add flavors, such as spicy and non-spicy dishes (if you can understand the meaning, don’t care about whether you can eat it or not, ha ha), namely spicy fish flavor shredded pork and non-spicy fish flavor shredded pork.

The above Cook is not modified, and the fish-flavoredpork is changed into SpicyFishFlavoredPork and NotSpicyFishFlavoredPork, the content is the same as before. Then we modified the Order and added two factories: SpicyFactory and NotSpicyFactory. The following code

Order

package com.wangscaler.factory.factorymethod;


import java.io.BufferedReader;
import java.io.InputStreamReader;

/ * * *@author wangscaler
 * @date2021.06.22 16:17 * /
public abstract class Order {
    Cook cook = null;
    String type = "";

    abstract Cook createCook(String type);

    public Order(a) {
        do {
            type = geType();
            cook = createCook(type);
            if(cook ! =null) {
                cook.prepare();
                cook.cut();
                cook.fry();
                cook.dish();
                cook.serve();
            } else {
                break; }}while (true);
    }

    private String geType(a) {
        try {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("Please enter your order, ending with EOF.");
            String str = bufferedReader.readLine();
            return str;
        } catch (Exception e) {
            e.printStackTrace();
            return ""; }}}Copy the code

SpicyFactory

package com.wangscaler.factory.factorymethod;

import com.wangscaler.factory.simplefactory.SimpleFactory;

/ * * *@author wangscaler
 * @date2021.06.23 10:10 * /
public class SpicyFactory extends Order {
    @Override
    Cook createCook(String type) {
        Cook cook = null;
        if (type.equals(SimpleFactory.Menu.FISHFLAVOREDPORK.getName())) {
            cook = new SpicyFishFlavoredPork();
            cook.setName("Spicy fish fillet");
        } else if (type.equals(SimpleFactory.Menu.KUNGPAOCHICKEN.getName())) {
            cook = new SpicyKungPaoChicken();
            cook.setName("Spicy Kung Pao Chicken");
        }
        return cook;
    }

    public enum Menu {
        /** ** * Fish fragrant pork */
        FISHFLAVOREDPORK("fishflavoredpork"),
        /** * Kung pao chicken */
        KUNGPAOCHICKEN("kungpaochicken"),
        /** * End identifier */
        EOF("EOF");
        private String name;

        Menu(String name) {
            this.name = name;
        }

        public String getName(a) {
            returnname; }}}Copy the code

NotSpicyFactory

package com.wangscaler.factory.factorymethod;
import com.wangscaler.factory.simplefactory.SimpleFactory;

/ * * *@author wangscaler
 * @date2021.06.23 10:11 * /
public class NotSpicyFactory extends Order {
    @Override
    Cook createCook(String type) {
        Cook cook = null;
        if (type.equals(SimpleFactory.Menu.FISHFLAVOREDPORK.getName())) {
            cook = new NotSpicyFishFlavoredPork();
            cook.setName("Shredded pork with fish flavor");
        } else if (type.equals(SimpleFactory.Menu.KUNGPAOCHICKEN.getName())) {
            cook = new NotSpicyKungPaoChicken();
            cook.setName("Kung Pao Chicken");
        }
        return cook;
    }

    public enum Menu {
        /** ** * Fish fragrant pork */
        FISHFLAVOREDPORK("fishflavoredpork"),
        /** * Kung pao chicken */
        KUNGPAOCHICKEN("kungpaochicken"),
        /** * End identifier */
        EOF("EOF");
        private String name;

        Menu(String name) {
            this.name = name;
        }

        public String getName(a) {
            returnname; }}}Copy the code

We can see that we have added the abstract method createCook to Order, passing its implementation to subclasses NotSpicyFactory and SpicyFactory.

Abstract Factory pattern

Abstract factory pattern is the combination of simple factory pattern and factory method pattern. In popular words, abstract factories are factories that produce different grades of products, that is, they can produce spicy factories and non-spicy factories.

Leave Cook, NotSpicyFishFlavoredPork, SpicyFishFlavoredPork unchanged in the factory method mode and add the abstract factory AbsFactory

package com.wangscaler.factory.abstractfactory;

/ * * *@author wangscaler
 * @date2021.06.23 11 * /
public interface AbsFactory {
    public Cook createCook(String type);
}

Copy the code

Provide interface for spicy factories and non-spicy factories

SpicyFactory

package com.wangscaler.factory.abstractfactory;



/ * * *@author wangscaler
 * @date2021.06.23 10:10 * /
public class SpicyFactory implements AbsFactory {

    @Override
    public Cook createCook(String type) {
        Cook cook = null;
        if (type.equals(SpicyFactory.Menu.FISHFLAVOREDPORK.getName())) {
            cook = new SpicyFishFlavoredPork();
            cook.setName("Spicy fish fillet");
        } else if (type.equals(SpicyFactory.Menu.KUNGPAOCHICKEN.getName())) {
            cook = new SpicyKungPaoChicken();
            cook.setName("Spicy Kung Pao Chicken");
        }
        return cook;
    }


    public enum Menu {
        /** ** * Fish fragrant pork */
        FISHFLAVOREDPORK("fishflavoredpork"),
        /** * Kung pao chicken */
        KUNGPAOCHICKEN("kungpaochicken"),
        /** * End identifier */
        EOF("EOF");
        private String name;

        Menu(String name) {
            this.name = name;
        }

        public String getName(a) {
            returnname; }}}Copy the code

NotSpicyFactory

package com.wangscaler.factory.abstractfactory;


/ * * *@author wangscaler
 * @date2021.06.23 10:10 * /
public class NotSpicyFactory implements AbsFactory {

    @Override
    public Cook createCook(String type) {
        Cook cook = null;
        if (type.equals(NotSpicyFactory.Menu.FISHFLAVOREDPORK.getName())) {
            cook = new NotSpicyFishFlavoredPork();
            cook.setName("Shredded pork with fish flavor");
        } else if (type.equals(NotSpicyFactory.Menu.KUNGPAOCHICKEN.getName())) {
            cook = new NotSpicyKungPaoChicken();
            cook.setName("Kung Pao Chicken");
        }
        return cook;
    }


    public enum Menu {
        /** ** * Fish fragrant pork */
        FISHFLAVOREDPORK("fishflavoredpork"),
        /** * Kung pao chicken */
        KUNGPAOCHICKEN("kungpaochicken"),
        /** * End identifier */
        EOF("EOF");
        private String name;

        Menu(String name) {
            this.name = name;
        }

        public String getName(a) {
            returnname; }}}Copy the code

At this point, our Order only needs to use the abstract factory

package com.wangscaler.factory.abstractfactory;


import java.io.BufferedReader;
import java.io.InputStreamReader;

/ * * *@author wangscaler
 * @date2021.06.22 16:17 * /
public class Order {

    AbsFactory factory;

    private void setFactory(AbsFactory factory) {
        Cook cook = null;
        String type = "";
        this.factory = factory;
        do {
            type = geType();
            cook = factory.createCook(type);
            if(cook ! =null) {
                cook.prepare();
                cook.cut();
                cook.fry();
                cook.dish();
                cook.serve();
            } else {
                break; }}while (true);
    }

    public Order(AbsFactory factory) {
        setFactory(factory);

    }

    private String geType(a) {
        try {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("Please enter your order, ending with EOF.");
            String str = bufferedReader.readLine();
            return str;
        } catch (Exception e) {
            e.printStackTrace();
            return ""; }}}Copy the code

The main function

package com.wangscaler.factory.abstractfactory;

/ * * *@author wangscaler
 * @date2021.06.23 and * /
public class AbstractFactory {
    public static void main(String[] args) {
        new Order(newSpicyFactory()); }}Copy the code

This method has good expansibility. For example, we only need to add spicy factory to achieve the createCook method of abstract factory and add spicy dishes.

Factory pattern in source code

The JDK in the Calendar

Calendar calendar=Calendar.getInstance(); We open the getInstance method of Calendar and find

 /**
     * Gets a calendar using the default time zone and locale. The
     * <code>Calendar</code> returned is based on the current time
     * in the default time zone with the default
     * {@link Locale.Category#FORMAT FORMAT} locale.
     *
     * @return a Calendar.
     */
    public static Calendar getInstance(a)
    {
        return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
    }

    /** * Gets a calendar using the specified time zone and default locale. * The <code>Calendar</code> returned is based on  the current time * in the given time zone with the default * {@link Locale.Category#FORMAT FORMAT} locale.
     *
     * @param zone the time zone to use
     * @return a Calendar.
     */
    public static Calendar getInstance(TimeZone zone)
    {
        return createCalendar(zone, Locale.getDefault(Locale.Category.FORMAT));
    }

    /** * Gets a calendar using the default time zone and specified locale. * The <code>Calendar</code> returned is based on  the current time * in the default time zone with the given locale. * *@param aLocale the locale for the week data
     * @return a Calendar.
     */
    public static Calendar getInstance(Locale aLocale)
    {
        return createCalendar(TimeZone.getDefault(), aLocale);
    }

    /**
     * Gets a calendar with the specified time zone and locale.
     * The <code>Calendar</code> returned is based on the current time
     * in the given time zone with the given locale.
     *
     * @param zone the time zone to use
     * @param aLocale the locale for the week data
     * @return a Calendar.
     */
    public static Calendar getInstance(TimeZone zone, Locale aLocale)
    {
        return createCalendar(zone, aLocale);
    }

    private static Calendar createCalendar(TimeZone zone, Locale aLocale)
    {
        CalendarProvider provider =
            LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
                                 .getCalendarProvider();
        if(provider ! =null) {
            try {
                return provider.getInstance(zone, aLocale);
            } catch (IllegalArgumentException iae) {
                // fall back to the default instantiation
            }
        }

        Calendar cal = null;

        if (aLocale.hasExtensions()) {
            String caltype = aLocale.getUnicodeLocaleType("ca");
            if(caltype ! =null) {
                switch (caltype) {
                case "buddhist":
                cal = new BuddhistCalendar(zone, aLocale);
                    break;
                case "japanese":
                    cal = new JapaneseImperialCalendar(zone, aLocale);
                    break;
                case "gregory":
                    cal = new GregorianCalendar(zone, aLocale);
                    break; }}}if (cal == null) {
            // If no known calendar type is explicitly specified,
            // perform the traditional way to create a Calendar:
            // create a BuddhistCalendar for th_TH locale,
            // a JapaneseImperialCalendar for ja_JP_JP locale, or
            // a GregorianCalendar for any other locales.
            // NOTE: The language, country and variant strings are interned.
            if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
                cal = new BuddhistCalendar(zone, aLocale);
            } else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
                       && aLocale.getCountry() == "JP") {
                cal = new JapaneseImperialCalendar(zone, aLocale);
            } else {
                cal = newGregorianCalendar(zone, aLocale); }}return cal;
    }

Copy the code

He actually called createCalendar, and in createCalendar we found out

Calendar cal = null;

    if (aLocale.hasExtensions()) {
        String caltype = aLocale.getUnicodeLocaleType("ca");
        if(caltype ! =null) {
            switch (caltype) {
            case "buddhist":
            cal = new BuddhistCalendar(zone, aLocale);
                break;
            case "japanese":
                cal = new JapaneseImperialCalendar(zone, aLocale);
                break;
            case "gregory":
                cal = new GregorianCalendar(zone, aLocale);
                break; }}}if (cal == null) {
        // If no known calendar type is explicitly specified,
        // perform the traditional way to create a Calendar:
        // create a BuddhistCalendar for th_TH locale,
        // a JapaneseImperialCalendar for ja_JP_JP locale, or
        // a GregorianCalendar for any other locales.
        // NOTE: The language, country and variant strings are interned.
        if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
            cal = new BuddhistCalendar(zone, aLocale);
        } else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
                   && aLocale.getCountry() == "JP") {
            cal = new JapaneseImperialCalendar(zone, aLocale);
        } else {
            cal = newGregorianCalendar(zone, aLocale); }}return cal;
Copy the code

It’s the simple factory model that we talked about

conclusion

Design patterns must adhere to design principles: rely on abstraction, not on concrete, open for extension, closed for modification…

Four concepts of the factory model:

  • Abstract product (Cook): Is the parent of all concrete products.
  • Concrete product (KungPaoChicken) : Inherits Cook’s parent class and is also an object instance produced by the factory.
  • Image Factory (AbsFactory) : Provides an interface to create objects for subclass implementation.
  • SpicyFactory: An interface that implements an abstract factory and produces object instances.

The above three design patterns can be used flexibly according to the business

  • Simple Factory model
    • There are fewer objects to create
    • The client does not need to focus on the object creation process
  • Factory method pattern
    • Does not know the class of the object it needs
    • Subclasses specify which object to create
    • Delegate the creation of an object to one of multiple factory subclasses
  • Abstract Factory pattern
    • One or more groups of objects are required to perform a function
    • Objects are not added frequently
    • You do not know the class of the object you want

The resources

  • JAVA Design Pattern
  • Deep understanding of the factory model