Preface:

  • The factory approach is one of the creation patterns in design patterns
  • Creation pattern: Handles the problem of creating objects without specifying the object’s specific type

Factory method mode:

Definition:

  • Define an interface for creating an object, but let the class implementing the interface decide which class to instantiate. Factory methods defer class instantiation to subclasses

Background:

  • Creating an object is often a complex process, so inclusion in a reusable object is not appropriate. Creating an object may result in a lot of duplicated code, may require information that the reusable object does not have access to, may not provide a sufficient level of abstraction, and may not be part of the concept of reusable objects. The factory method pattern solves these problems by defining a separate method for creating objects. Subclasses implement this method to create objects of specific types

UML:

The roles in the picture are as follows:

  • Abstract Factory role: A factory is usually an object used to create other objects. Factories are abstractions of constructors used to implement different allocation schemes.
  • Concrete factory role: a concrete object that implements a product and is a subclass of an abstract factory
  • Product roles: Instances implemented by concrete objects

Example:

There is a Button class for buttons, and its two subclasses, WinButton and MacButton, represent Windows and Mac-style buttons, respectively. These classes and the factory class used to create them can be implemented in iOS as follows:

// Struct WinButton:Button {} struct MacButton:Button {} struct MacButton:Button { ButtonFactory { static func createButton()->Button; } struct WinButtonFactory: ButtonFactory { static func createButton() -> Button { return WinButton.init() } } struct MacButtonFactory:ButtonFactory {static func createButton() -> Button {return macbutton.init ()}} // Client call let winBtn = WinButtonFactory.createButton(); . / / winBtn create let macBtn = MacButtonFactory createButton (); / / macBtn createdCopy the code
Button.h @interface Button: NSObject @end Button.m @implementation Button @end WinButton.h @interface WinButton : Button @end WinButton.m @implementation WinButton @end MacButton.h @interface MacButton : button@end macbutton.m @implementation macbutton.h @interface ButtonFactory: NSObject + (Button *)createButton; @end ButtonFactory.m @implementation ButtonFactory + (Button *)createButton{ return [Button new]; } @end WinButtonFactory.h @interface WinButtonFactory : ButtonFactory @end WinButtonFactory.m @implementation WinButtonFactory + (Button *)createButton{ return [WinButton new];  } @end MacButtonFactory.h @interface MacButtonFactory : ButtonFactory @end MacButtonFactory.m @implementation MacButtonFactory + (Button *)createButton{ return [WinButton new];  } @end Button *winBtn = [WinButtonFactory createButton]; Button *macBtn = [MacButtonFactory createButton]; / / macBtn createdCopy the code

Variant:

While the motivation behind the factory method pattern is to allow subclasses to choose the specific types of objects that are created, there are other benefits to using the factory method pattern, many of which do not depend on subclasses. Therefore, factory methods that create objects without polymorphism are sometimes created to get the other benefits of using factory methods.

Factory “methods” instead of factory “classes “:

Aside from the scope of design patterns, the term “factory method” can also be used to refer to a method that acts as a “factory” whose primary purpose is to create objects, and that method does not necessarily reside in a separate factory class. These methods are usually defined as static methods in the class that the method instantiates.

Example:

//swift struct Button { var product:String static func createWinButton()->Button{ return Button(productName: "WinButton") }; static func createMacButton()->Button{ return Button(productName: "MacButton") }; Private init(productName:String) {product = productName}} private init(productName:String) {product = productName}} let winButton = button.createWinButton ( Let macBtn = button.createmacButton ()// create macBtnCopy the code
//objective-c Button.h @interface Button : NSObject @property (nonatomic,copy)NSString *product; + (Button *)creatWinButton; + (Button *)creatMacButton; @end Button.m @implementation Button + (Button *)creatWinButton{ return [[Button alloc] initWithProductName:@"WinButton"]; } + (Button *)creatMacButton{ return [[Button alloc] initWithProductName:@"MacButton"]; } -(instancetype)initWithProductName:(NSString *)product{ self = [super init]; if (self) { self.product = product; } return self; } @end // Button *winBtn = [Button creatWinButton]; Button *macBtn = [Button creatMacButton]; / / macBtn creationCopy the code

Apple has a similar design:

//apple NSnumber // the client calls NSnumber *aChar =[NSnumber initWithBool:YES]; //__NSCFBoolean NSNumber *anInt = [NSNumber numberWithInt:1]; //__NSCFNumber NSNumber * convertible = [NSNumber numberWithFloat:1.0]; NSNumber *aDouble = [NSNumber numberWithDouble:1.0];Copy the code

The returned result is a class cluster, an application of the abstract factory pattern

Simple factory:

The common factory method pattern is usually accompanied by a one-to-one correspondence between the specific type of the object and the specific type of the factory, and the client code chooses the appropriate specific type of factory to use as needed. However, this choice can involve complex logic. At this point, you can create a single factory class that contains this selection logic, implementing different concrete objects based on the choice of parameters. This factory class does not require each concrete product to implement its own concrete factory class, so factory methods can be set to static methods.

Example:

//swift protocol Button {} struct WinButton:Button {} struct MacButton:Button {} enum ButtonType { case Win case Mac } struct ButtonFactory { static func createButton(type:ButtonType)->Button{ let button:Button switch type { case ButtonType.Win: button = WinButton() case ButtonType.Mac: button = MacButton() } return button } } let winBtn = ButtonFactory.createButton(type: ButtonType. Win) / / winBtn create let macBtn = ButtonFactory. CreateButton (Mac) type: ButtonType. / / macBtn creationCopy the code
Button.h @interface Button: NSObject @end Button.m @implementation Button @end WinButton.h @interface WinButton : Button @end WinButton.m @implementation WinButton @end MacButton.h @interface MacButton : button@end macbutton.m @implementation macbutton@end buttonfactory.h typedef enum {WinButtonType, MacButtonType }ButtonType; @interface ButtonFactory : NSObject + (Button *)createButtonWithType:(ButtonType)type; @end ButtonFactory.m @implementation ButtonFactory + (Button *)createButtonWithType:(ButtonType)type{ Button *newBtn = nil; switch (type) { case WinButtonType: newBtn = [WinButton new]; break; case MacButtonType: newBtn = [MacButton new]; default: newBtn = [Button new]; break; } return newBtn; } / / @ end client calls: Button * winBtn3 = [ButtonFactory createButtonWithType: WinButtonType]; / / winBtn create Button * macBtn3 = [ButtonFactory createButtonWithType: MacButtonType]; / / macBtn creationCopy the code

Applicability:

The factory approach applies to programming to interface and implements the dependency reversal principle. Consider using the factory method pattern in the following cases:

  • Creating objects requires a lot of repetitive code. You can write this code in a factory base class.
  • Creating an object requires access to information that should not be contained in a composite class.
  • The life cycle of the created objects must be centrally managed to ensure consistent behavior throughout the program. When an object is created, there are a number of parameters that determine how the object is created.
  • The code author of the business object wants to hide the real type of the object, the constructor must have a real class name, and so on

Limitations:

  • Refactoring an existing class breaks the client code
  • Because classes instantiated by factory methods have private constructors, these classes cannot be extended. Any subclass must call the constructor of the superclass, but the private constructor of the superclass cannot be called by a subclass
  • If you do extend the class that the factory method instantiates (for example, it is risky but feasible to make the constructor protected), the subclass must have a set of implementations of all the factory methods. Otherwise an instance of the parent class is returned, not the desired instance of the subclass.

Reference:

Wikipedia – The factory method