define

Generator pattern: Separate the construction of a complex object from its representation so that the same construction process can create different representations.

The whole idea is to separate the “what” from the “how”. Decompose the construction process of complex objects into a client-director-Builder relationship. This will make it easier to manage and reuse the entire process.

Usage scenarios

It is natural to use this pattern when:

  • You need to create complex objects that design various parts. The algorithm for creating objects should be independent of how the parts are assembled. This is common in building composite objects.
  • The build process needs to build objects in different ways (for example, different combinations of parts or representations)

The class diagram structure

The generator has three roles, the mentor Director, the generator Builder, and the Product, as shown in the figure below

The Director holds the Builder object that forms an aggregation relationship for building the Product. The Construct method of the Director is implemented using the held Builder to execute the buildPart method. Builder is an abstract interface that declares a buildPart method. The ConcreteBuilder implementation inherits buildPart and getProduct methods. The buildPart implementation calls buildPartA, buildPartB, and buildPartC methods to build different details of the Product. Finally, use getProduct to return the built product object.

Generators versus abstract factories

Abstract factory and generator patterns have many similarities in abstract object creation. However, the two have different design concepts.

  1. Generators focus on distributed creation of complex objects, and many times objects of the same type can be created in different ways.
  2. Abstract factories focus on creating suites of simple or complex products.
  3. The generator returns the product at the end of the multi-step creation process, while the abstract factory returns the product immediately.

The differences are shown in the following table:

The generator The abstract factory
Building complex Objects Build simple or complex objects
Build objects in multiple steps Build objects in a single step
Build objects in a variety of ways Build objects in a single way
Return the product in the last step of the build process Return to product immediately
Focus on a specific product Emphasize a set of products

In short, generators focus more on process splitting and abstract factories focus more on result generation.

Code implementation

The following is an example of building a character in a game, using the generator pattern.

The requirements are as follows: we need to build two game characters, Enemy and Player. Both characters have some basic basic features, such as strength, stamina, Intelligence, Agility, and aggressiveness. Each trait affects a character’s Protection and Power. The ratio of each trait to the character’s attack or defense is as follows:

defense attack
Strength write write
Stamina write write
Intelligence write left
Agility write left
Aggressiveness (Aggressiveness) left write

According to the above requirements, the following static class diagram is designed

XQChasingGame defines createPlayer: Builder and createEnemy: Builder to create players and enemies. Each method has a different set of special factors that define the characteristics of the role. XQStandardCharacterBuilder is a subclass of XQCharacterBuilder, according to the features of different factor to create the actual role. After the completion of the building, XQStandardCharacterBuilder returns the instance of XQCharacter.

  1. Build the role object XQCharacter
@interface XQCharacter : NSObject@property (nonatomic, assign) float protection; ! [](https://user-gold-cdn.xitu.io/2020/6/24/172e213819f3556e? w=1435&h=697&f=png&s=118117)
@property (nonatomic, assign) float power;
@property (nonatomic, assign) float strength;
@property (nonatomic, assign) float stamina;
@property (nonatomic, assign) float intelligence;
@property (nonatomic, assign) float agility;
@property (nonatomic, assign) float aggressiveness;

@end

@implementation XQCharacter

- (instancetype)init{
  if (self = [super init]) {self.protection = 1.0;
      self.power = 1.0;
      self.strength = 1.0;
      self.stamina = 1.0;
      self.intelligence = 1.0;
      self.agility = 1.0;
      self.aggressiveness = 1.0;
  }
  return self;
}

@end
Copy the code

In the init method, each attribute is assigned a default value of 1.0.

  1. Build the Abstract class XQCharacterBuilder
@interface XQCharacterBuilder : NSObject

@property (nonatomic, strong, readonly) XQCharacter *character;

- (XQCharacterBuilder *)createCharacter;
- (XQCharacterBuilder *)buildStrength:(float)value;
- (XQCharacterBuilder *)buildStamina:(float)value;
- (XQCharacterBuilder *)buildIntelligence:(float)value;
- (XQCharacterBuilder *)buildAgility:(float)value;
- (XQCharacterBuilder *)buildAggressiveness:(float)value;

@end

@interface XQCharacterBuilder ()

@property (nonatomic, strong, readwrite) XQCharacter *character;

@end

@implementation XQCharacterBuilder

- (XQCharacterBuilder *)createCharacter{
    self.character = [[XQCharacter alloc] init];
    return self;
}

- (XQCharacterBuilder *)buildStrength:(float)value{
    self.character.strength = value;
    return self;
}

- (XQCharacterBuilder *)buildStamina:(float)value{
    self.character.stamina = value;
    return self;
}

- (XQCharacterBuilder *)buildIntelligence:(float)value{
    self.character.intelligence = value;
    return self;
}

- (XQCharacterBuilder *)buildAgility:(float)value{
    self.character.agility = value;
    return self;
}

- (XQCharacterBuilder *)buildAggressiveness:(float)value{
    self.character.aggressiveness = value;
    return self;
}

@end
Copy the code

XQCharacterBuilder declares the corresponding object creation and attribute assignment interface, defines the default assignment behavior, assigns the corresponding attribute value to the created object character.

Build concrete subclasses XQStandardCharacterBuilder. 3

@interface XQStandardCharacterBuilder : XQCharacterBuilder

- (XQCharacterBuilder *)buildStrength:(float)value;
- (XQCharacterBuilder *)buildStamina:(float)value;
- (XQCharacterBuilder *)buildIntelligence:(float)value;
- (XQCharacterBuilder *)buildAgility:(float)value;
- (XQCharacterBuilder *)buildAggressiveness:(float)value;

@end

@implementation XQStandardCharacterBuilder

- (XQCharacterBuilder *)buildStrength:(float)value{
    self.character.protection *= value;
    self.character.power *= value;
    return [super buildStrength:value];
}

- (XQCharacterBuilder *)buildStamina:(float)value{
    self.character.protection *= value;
    self.character.power *= value;
    return [super buildStamina:value];
}

- (XQCharacterBuilder *)buildIntelligence:(float)value{
    self.character.protection *= value;
    self.character.power /= value;
    return [super buildIntelligence:value];
}

- (XQCharacterBuilder *)buildAgility:(float)value{
    self.character.protection *= value;
    self.character.power /= value;
    return [super buildAgility:value];
}

- (XQCharacterBuilder *)buildAggressiveness:(float)value{
    self.character.protection /= value;
    self.character.power *= value;
    return [super buildAggressiveness:value];
}

@end
Copy the code

Overloading in XQStandardCharacterBuilder kind of statement, statement of the parent class method, in order to make clear the tasks and responsibilities of the class.

In XQStandardCharacterBuilder class implements the basis for each attribute of the role of defense and attack the influence of the assignment. Affects the attack and defense values of the character, as defined in a proportional relationship in the matrix. Finally, the corresponding method of the parent class is called to assign the property and return itself. (The benefits of returning self will be discussed below)

  1. Build the mentor XQChasingGame
@interface XQChasingGame : NSObject

- (XQCharacter *)createPlayer:(XQCharacterBuilder *)builder;

- (XQCharacter *)createEnemy:(XQCharacterBuilder *)builder;

@end

@implementation XQChasingGame

- (XQCharacter *)createPlayer:(XQCharacterBuilder *)builder{
    [[[[[[builder createCharacter]
         buildStrength:50.f]
        buildStamina:25.f]
       buildIntelligence:75.f]
      buildAgility:65.f]
     buildAggressiveness:35.f];
    return builder.character;
}

- (XQCharacter *)createEnemy:(XQCharacterBuilder *)builder{
    [[[[[[builder createCharacter]
         buildStrength:80.f]
        buildStamina:65.f]
       buildIntelligence:35.f]
      buildAgility:25.f]
     buildAggressiveness:95.f];
    return builder.character;
}

@end
Copy the code

XQChasingGame declares the method of creating players and enemies, and the internal implementation passes different attribute values according to the requirements of character attributes. Due to the concrete subclass XQStandardCharacterBuilder, attribute assignment methods return itself, would provide a continuous call here, a single statement. Finally, the built character is returned for external use.

  1. Client call
XQStandardCharacterBuilder *builder = [XQStandardCharacterBuilder new];
XQChasingGame *game = [XQChasingGame new];
XQCharacter *player = [game createPlayer:builder];
XQCharacter *enemy = [game createEnemy:builder];
Copy the code

Client generation XQStandardCharacterBuilder and XQChasingGame instance, call instance methods createPlayer and create new gamers createEnemy role with the enemy. You can then throw two character objects into the game.

conclusion

The generator pattern helps build objects that involve various combinations of parts and representations. Prevent the mentor Director from becoming a large class. Place the character building algorithm in a concrete XQCharacterBuilder.