Design Mode - Creator Mode Factory Mode

The 23 design modes we often refer to can be roughly divided into three categories:

  • Builder
  • Structural pattern
  • Behavioral patterns

The factory design mode belongs to the creator mode.

Builder

As the name implies, the focus is on how to create objects, and the feature is to separate the creation and use of objects.Reduce system coupling so that users don't care about details.

Plant Design Mode

In JAVA, everything is an object, and these objects need to be created. If a new object is created directly in the business code, it will cause the object to be heavily coupled.For instance:
There are now two mobile phone classes, Huawei and Apple, and a store class, Store, which provides a way to sell different phones based on the parameters passed in.If we don't use factory design patterns, this is what the initial code looks like.

  • Define Phone Class
/**
 * Mobile Abstract class, describing the main functions of the product
 */
public abstract class Phone {
    // Mobile phone name
    String name ;
    // Get mobile information
    public abstract String getName() ;
    // Public method
    public void call() {
        System.out.println("I am Phone,I can call");
    }
}
  • Define the Phone Huawei class, inherit Phone
/**
 * Huawei Mobile Class
 */
public class PhoneHuawei extends Phone{

    public PhoneHuawei() {
        this.name = "Huawei Mobile Phone" ;
    }

    @Override
    public String getName() {
        return this.name ;
    }
}
  • Define the Apple Mobile (PhoneApple) class, inherit Phone
/**
 * IPhone
 */
public class PhoneApple extends Phone{

    public PhoneApple() {
        this.name = "IPhone" ;
    }
    @Override
    public String getName() {
        return this.name ;
    }
}

  • Define Store Class
/**
 * Shop
 */
public class Store {

    public static Phone getPhone(String type) {
        if ("apple".equals(type)) {
            return new PhoneApple() ;
        } else if ("huawei".equals(type)) {
            return new PhoneHuawei() ;
        } else {
            throw new RuntimeException("There are no mobile phones of different types locally~~~~") ;
        }
    }
}
  • Test Class
/**
 * Test Class
 */
public class Client {
    public static void main(String[] args) {
        Phone phone = Store.getPhone("apple");
        System.out.println("Buy a mobile phone:" + phone.getName());
        phone.call();
    }
}

Output results:

Buy a mobile phone: Apple phone
 I am Phone,I can call

As you can see from the code above, the operation of creating objects is done in the Store class.Now when the business changes, we need to create another millet phone when it is passed into xiaomi. According to the above case, we can only add another if judgment in Store.If there are multiple Store classes, we need to add a lot of unnecessary code, which violates the open and close principles of software design.

Open and Close Principle: Open to extension and close to modification.This means to extend, not modify, the original code when upgrading your business.

For the coupling of a large number of new objects in the business, we can extract the creation of objects and create a class specifically for the production of objects, called a factory class.From the above example, we can create a PhoneFactory for mobile phone factory to specialize in mobile phone production:

/**
 * Mobile phone factory class
 */
public class PhoneFactory {

    public static Phone createPhone(String type) {
        if ("apple".equals(type)) {
            return new PhoneApple() ;
        } else if ("huawei".equals(type)) {
            return new PhoneHuawei() ;
        } else {
            throw new RuntimeException("There are no mobile phones of different types locally~~~~") ;
        }
    }
}

Modify the store code to get the created objects from the factory:

/**
 * Shop
 */
public class Store {

    public static Phone getPhone(String type) {
        // Call factory method to get mobile phone
        return PhoneFactory.createPhone(type) ;
    }
}

In this way, business methods can be separated from object creation and code coupling can be reduced, even if the mobile phone type is added later, we only need to change the factory code.
Advantage:

  • Encapsulates the process of creating an object, and C-side can get the object directly through parameters.Separating object creation from business logic reduces the possibility of C-side code modification and makes it easier to extend.

Disadvantages:

  • When adding a new phone, it still needs to modify the factory code, which still violates the switch-on principle

To summarize the above case, each additional model requires an additional if judgment, which always modifies the original code.Is there a way to avoid modifying the code, or is there a way to generate objects without judging by if?
As C-end consumers, we can clearly know what we want.So we need to convey a message to Store that allows Store to go back to what we want without making any judgment.So the message we're conveying can't be a string anymore, it needs to be related to the object, which is what we'll mention next: the mobile phone manufacturer (the mobile phone factory), so there's the factory method mode.

Factory Method Mode

Previously, we defined a Phonefactory class for a mobile phone factory, in which we could produce different objects using if.The factory method model also requires a PhoneFactory factory, but this factory is defined as abstract and also provides a way to produce mobile phones, but specific brands of mobile phones need to be produced by specific sub-factories.Code first:

  • Abstract Mobile Factory Interface:
public interface PhoneFactory {
    /**
     * Produce mobile phone, but what brand is produced, implementation method written by subfactory
     * It could be Huawei, it could be Apple, so the return value here should be the parent of all mobile phones
     * @return
     */
    Phone createPhone() ;
}
  • Specific Huawei Mobile Phone Factory Class
/**
 * Huawei Mobile Phone Factory
 */
public class PhoneHuaweiFactory implements PhoneFactory {
    // Huawei Factory, which produces Huawei's mobile phones
    @Override
    public Phone createPhone() {
        return new PhoneHuawei() ;
    }
}
  • Specific Apple Mobile Factory Class
/**
 * Apple Mobile Factory
 */
public class PhoneAppleFactory implements PhoneFactory{
    // Apple factory, making Apple mobile phones
    @Override
    public Phone createPhone() {
        return new PhoneApple();
    }
}

Stores, on the other hand, have their own manufacturers. Stores produce everything they do, so there should be a producer in the Store, also known as a mobile phone factory.

  • Store code:
public class Store {

    // Define the store's producer base, possibly Huawei, possibly Apple, so use their parent class
    private PhoneFactory factory ;

    // The customer is God, and the factory produces what the customer throws in
    public void setFactory(PhoneFactory factory) {
        this.factory = factory ;
    }

    public Phone getPhone() {
        return factory == null ? null : factory.createPhone() ;
    }
}

Next, it is clear that when the C-terminal clearly knows what it wants, it can simply throw specific producer objects into the store.

  • Consumer side:
public class Client {
    public static void main(String[] args) {
        // 1. Get Store Objects
        Store store = new Store() ;
        // 2. The manufacturer who sets the type of mobile phone for the store, here is the one who wants to get the Apple phone
        store.setFactory(new PhoneAppleFactory());
        // 3. Get your mobile phone
        Phone phone = store.getPhone();
        // 4. Print mobile phone information
        System.out.println("Get a new phone:" + phone.getName());
        phone.call();
    }
}

View the results:

Get a new phone: the Apple phone
 I am Phone,I can call

If the later business is combined with other brands of mobile phones, just one more entity class and the corresponding factory class are needed.Avoid modifying the original code, conform to the open and close principle.
Advantage:

  • C-end only needs to know the specific factory name to create the corresponding product, without knowing the specific process of product creation.
  • It conforms to the open and close principle and does not need to make task modifications to the original factory.

Disadvantages:

  • For each additional product, an entity class and a corresponding factory class are added, which increases the complexity of the system.

Abstract Factory Mode

With the upgrade of the mobile phone store business, not only mobile phones are sold, but also the TT type of mobile phone sleeves are sold in the store to protect the phone and aesthetics.
Phone sleeve will certainly be divided into phone sleeve TTHuawei and Apple phone sleeve TTApple. We need to define a TT Abstract class, describe the characteristics of TT, and then create two entity classes to inherit this TT Abstract class.Say nothing but code.

  • TT abstract class
/**
 * Mobile phone sleeve
 */
public abstract class TT {
    // Get the name of the mobile phone case
    public abstract void getName() ;

    // Common Functions
    public void function() {
        System.out.println("Protect your mobile phone~~");
    }
}
  • Huawei TT Class
public class TTHuawei extends TT{
    @Override
    public void getName() {
        System.out.println("Huawei Mobile Set");
    }
}
  • Apple TT s
public class TTApple extends TT{
    @Override
    public void getName() {
        System.out.println("Apple phone sleeve");
    }
}

According to the factory method mode, we also need to define an abstract TT production factory interface TTFactory, create specific HuaweiTTFactory implements TTFactory and specific Apple TTFactory class AppleTTFactory implements TTFactory respectively, then go to the store class store to declare a TT factory object and provide the setter method.When invoking on the C-side, we need an additional setTTFactory in addition to setPhoneFactory for the store object, with some key code as follows:

  • Shop Code
public class Store {

    // Define the manufacturer object of the mobile phone, possibly the Huawei factory, possibly the Apple factory, so use their abstraction
    private PhoneFactory phoneFactory ;
    // Define the object of mobile phone sleeve production
    private TTFactory ttFactory ;
    
    // The customer is God, and the factory produces what the customer throws in
    public void setPhoneFactory (PhoneFactory factory) {
        this.phoneFactory = factory ;
    }

    public void setTTfactory (TTFactory factory) {
        this.ttFactory = factory ;
    }

    public Phone getPhone() {
        return phoneFactory == null ? null : phoneFactory.createPhone() ;
    }

    public TT getTT() {
        return ttFactory == null ? null : ttFactory.getTT() ;
    }
}
  • Consumer Code:
public class Client {
    public static void main(String[] args) {
        // 1. Get Store Objects
        Store store = new Store() ;
        // 2. The manufacturer who sets the type of mobile phone for the store, here is the one who wants to get the Apple phone
        store.setPhoneFactory(new PhoneAppleFactory());
        // 3. Get your mobile phone
        Phone phone = store.getPhone();
        
        // 4. Set up mobile phone sleeve manufacturers for stores to obtain sleeves
        store.setTTfactory(new HuiweTTFactory());
        // 5. Get the phone case
        TT tt = store.getTT() ;
    }
}

The new business, although this can also fulfill the demand, has one obvious problem: I bought an Apple phone, but I accidentally bought a Huawei phone case.Although subjectively possible, this is objectively not allowed.
So when designing programs, we need to find ways to change that.First, you need to understand two concepts: product class and product family.

  • Product grade: A category of products with the same characteristics.Huawei mobile phone and Apple mobile phone are the same kind of product, we call this product with the same characteristics product grade.
  • Product Family: A product family that has multiple product levels and has some relationship with each other is called a product family.For example, Huawei Mobile Phone and Huawei Mobile Phone Cover belong to Huawei in both product grades, so Huawei can be called product family.

Sounds abstract, let's take a few more examples:

  • Business coats and leisure coats belong to a product class, business trousers and leisure trousers belong to a product class, business style belongs to a product family, and leisure style belongs to a product family.
  • Cats and dogs belong to one product level, and chickens and ducks belong to one product level.So animals belong to a family of products, and poultry belongs to a family of products.

Personally, the product family is more abstract, drawn from the actual business.A product family can contain many product classes with some association.
In the example above, Huawei mobile phone and Apple mobile phone belong to a product level, Huawei mobile phone case and Apple mobile phone case belong to a product level, Huawei belongs to a product family, and Apple belongs to the same product family.

So according to our actual business, when we go to a store and only want to say one product family, we want to wait until we get all the product classes under the current product family.For example, go to Store and say Huawei, you will get Huawei mobile phone and Huawei mobile phone sleeve.
When designing code for such businesses, we typically use the description of the product family as the bottom-level abstract interface that is implemented by each specific product family factory class.This means that both Huawei and Apple can make mobile phones and mobile phone sleeves, so we take out the functions of making mobile phones and mobile phone sleeves to design an abstract factory.Then create the Huawei and Apple factories to implement this interface.

  • Abstract factory
/**
 * Abstract production of mobile phones and sleeves
 */
public interface PhoneAndTTFactory {
    /**
     * Manufacturing mobile phones
     * @return
     */
    Phone createPhone() ;
    /**
     * Production sleeve
     * @return
     */
    TT createTT() ;
}
  • Huawei Factory
/**
 * Huawei Factory
 * Production of Huawei Mobile Phone and Huawei Mobile Phone Cover
 */
public class HuaweiFactory implements PhoneAndTTFactory{

    @Override
    public Phone createPhone() {
        return new PhoneHuawei() ;
    }

    @Override
    public TT createTT() {
        return new TTHuawei() ;
    }
}
  • Apple Factory
/**
 * Apple Factory
 * Making Apple Mobile Phones and Apple Sleeves
 */
public class AppleFactory implements PhoneAndTTFactory{

    @Override
    public Phone createPhone() {
        return new PhoneApple() ;
    }

    @Override
    public TT createTT() {
        return new TTApple() ;
    }
}
  • Shop Class
/**
 * Shop
 */
public class Store {

    private PhoneAndTTFactory factory ;

    /**
     * Because both the Huawei and Apple factories implement the PhoneAndTTFactory interface
     * So any factory can be accepted here
     * So the object returned, that is, the object produced by each factory
     * @param factory
     */
    public void setFactory(PhoneAndTTFactory factory) {
        this.factory = factory ;
    }

    public Phone getPhone() {
        return factory.createPhone() ;
    }

    public TT getTT() {
        return factory.createTT() ;
    }
}
  • Consumer
public class Client {
    public static void main(String[] args) {
        Store store = new Store() ;

        // Deliver product family factories
        store.setFactory(new AppleFactory());

        Phone phone = store.getPhone();
        TT tt = store.getTT();

        System.out.println("Mobile phone:" + phone.getName());
        tt.getName();
    }
}

Result:

Mobile phone: Apple mobile phone
 Apple phone sleeve

Advantage
When multiple objects in a product family are designed to work together, it ensures that clients always use only objects in the same product family.
shortcoming
The factory class needs to be modified when a product needs to be added to the product family.For example, adding Huawei computers and Apple computers requires that the codes for both the Huawei and Apple factories be modified.
Use scenarios
When the object you need to create is a series of related products or a family of interdependent products, such as input method (change input method, background map, color, font and so on all change), the game skin (change the skin, lines, special effects and so on will change).

Factory Mode Extension (Important)

Simple Factory+Profile Contact Coupling
The coupling of plant objects and products can be accessed through factory mode + configuration files.Load the full name of the class in the configuration file in the factory class and create an object to store it. Clients can get it directly if they use it.

Step 1: Define a configuration file
Under the resources of the maven project, create the bean.properties file

huawei: cn.lzc.demo5.entity.PhoneApple
apple: cn.lzc.demo5.entity.PhoneApple

Step 2: Improve the factory class

public class CustomFactory {

    // Storage object
    private static Map<String, Phone> phoneMap = new HashMap<String, Phone>(0) ;

    static {
        // Get profile input stream
        Properties properties = new Properties() ;
        InputStream inputStream = CustomFactory.class.getClassLoader().getResourceAsStream("bean.properties");
        try {
            properties.load(inputStream);
            // Gets the set of defined key s
            Set<Object> keys = properties.keySet();
            // Traverse keys to get objects
            for (Object key : keys) {
                // Get the full class name
                String className = properties.getProperty((String) key) ;
                Class clazz = Class.forName(className) ;
                Phone phone = (Phone) clazz.newInstance() ;
                phoneMap.put((String) key, phone) ;
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Get Object
     * @param name
     * @return
     */
    public static Phone createPhone (String name) {
        return phoneMap.get(name) ;
    }
}

Step 3: Consumer Code

public class Client {
    public static void main(String[] args) {
        // Get the object based on the key in the configuration file
        Phone phone = CustomFactory.createPhone("apple") ;
        System.out.println(phone.getName());
        phone.call();
    }
}

This method also conforms to the on/off principle, and if you need to add a coffee, you only need to add one action to the configuration file.

Keywords: Java Design Pattern

Added by bulrush on Fri, 03 Sep 2021 22:00:17 +0300