Design Pattern is a set of repeatedly used, most people know, classified, summarized code Design experience, the use of Design Pattern is to reuse the code, make the code easier to understand and ensure the reliability of the code.

Due to mining rules limit 2W words, manually deleted part of the content, need to view the complete content can be viewedLearn and understand 23 design patterns

The 23 classic design patterns introduced in The book Design Patterns: The Foundation of Reusable Object-oriented Software are not the only design patterns. With the development of the software development industry, more and more new patterns are born and applied. Experienced developers who learn design patterns are more likely to understand them when they are validated by past experience.

Why learn design Patterns:

  • Design patterns are derived from the experience and wisdom of many experts. They are successful, maintainable and reusable designs derived from many excellent software systems that will allow us to avoid repetitive work

  • Design pattern provides a set of common design vocabulary and a common form to facilitate communication between developers, making the design scheme more easy to understand

  • Most design patterns take into account the reusability and expansibility of the system, which enables us to better reuse some existing design schemes, functional modules and even a complete software system, avoiding us to often do some repetitive design, write some repetitive code

  • Using design patterns properly and documenting their use will help others understand the system faster

  • Learning design patterns will help beginners gain a deeper understanding of object-oriented thinking

UML class diagram

Each pattern has a corresponding object structure diagram, and UML diagrams are sometimes used to show how it works in order to show the details of how objects interact. I will not mention all the elements of UML here. I just want to talk about the relationship between the classes in the class diagram. Once I can understand the meaning of the lines and arrows between the classes in the class diagram, IT is enough to deal with daily work and communication. At the same time, we should be able to match the meaning of the class diagram to the final code. With this knowledge, there is no problem looking at the design pattern structure diagram in the later sections.

Most of this article is UML class diagrams, but there are some simple flow diagrams. Since some of the patterns in this article are not illustrated, you can see the full UML class diagrams of the 23 design patterns I’ve collected on the Web here.

1.1 inheritance

Inheritance is represented by a direct arrow with a hollow stripe.

1.2 implementation

The implementation relationship is represented by a dashed line with a hollow arrow.

1.3 combination

Like aggregative relations, combinatorial relations also represent the semantics of the whole being made up of parts. For example, the company is composed of multiple departments, but the combination relationship is a special aggregation relationship with strong dependence. If the whole does not exist, then the part does not exist. For example, the company will no longer exist and the department will no longer exist.

1.4 the aggregation

Aggregation relationships are used to represent relationships between entity objects, representing the semantics that the whole is made up of parts, such as a department consisting of multiple employees. Unlike combinatorial relationships, whole and parts are not strongly dependent, and even if the whole does not exist, the parts still exist. For example, when departments are abolished, people don’t disappear, they still exist.

1.5 correlation

Association relation is represented by a straight line, which describes the structural relation between objects of different classes. It is a static relation, which has nothing to do with the running state and is generally determined by common sense and other factors. It is generally used to define the static and natural structure between objects. Therefore, relational relationship is a “strong relational” relationship.

For example, there is an association relationship between passengers and tickets, and between students and schools. By default, the association relationship does not emphasize the direction, indicating that objects know each other. If particular emphasis is placed on direction, as shown in the figure below, it means that A knows B, but B does not know A.

1.6 depend on

Dependencies are represented by A set of dotted lines with arrows, such as A dependence on B, which describes the relationship between one object and another object at run time.

Unlike associations, it is an AD hoc relationship, typically occurring at run time, and dependencies can change as the run time changes. Obviously, dependency has a direction, and two-way dependency is a very bad structure, so we should always keep one-way dependency and avoid two-way dependency.

Two, six principles

2.1 Open and close principle

A software entity should be open for extension and closed for modification. That is, software entities should extend as much as possible without modifying the original code.

An important problem with any software is that its requirements change over time. When a software system needs to meet new requirements, we should try to ensure that the design framework of the system is stable. If a software design conforms to the open and closed principle, it can be very convenient to extend the system, and there is no need to modify the existing code during the extension, so that the software system has good stability and continuity while having adaptability and flexibility. With the larger software scale, longer software life and higher software maintenance costs, it becomes more and more important to design software systems that meet the principle of open and close.

Abstract design is necessary to satisfy the open and close principle. Abstraction is the key of the open and close principle. In Java, C# and other programming languages, it is possible to define a relatively stable abstraction layer for the system and move the different implementation activities to the concrete implementation layer. In many object-oriented programming languages, mechanisms such as interfaces and abstract classes are provided to define the abstraction layer of the system, which can be extended by concrete classes. If you need to modify the behavior of the system, there is no need to make any changes to the abstraction layer, only need to add new concrete classes to achieve new business functions, to achieve the expansion of system functions without modifying the existing code, to meet the requirements of the open and closed principle.

Advantages: The advantage of practicing the open-close principle is that you can extend functionality without changing the original code. The expansibility of the program is increased, and the maintenance cost of the program is reduced.

2.2 Richter’s Substitution principle

All references to objects of the base class can transparently use objects of its subclasses

The Richter substitution principle tells us that if software replaces a base object with a subclass object, the program will not generate any errors or exceptions. The reverse is not true. If a software entity uses a subclass object, it may not be able to use a base object. For example: I like animals, then I must like dogs, because dogs are a subclass of animals. But I like dogs, so it doesn’t follow that I like animals, because I don’t like mice, even though they are animals.

For example, if there are two classes, one is BaseClass and the other is SubClass, and SubClass is a SubClass of BaseClass, then a method can accept a base object of type BaseClass. Method1 (base), so it must accept a subclass object sub of type BaseClass, and method1(sub) works fine. The reverse substitution does not work, for example, if a method method2 takes a subclass of BaseClass object sub: method2(sub), then you generally cannot have method2(base), unless the method is overloaded.

Richter’s substitution principle is one of the important ways to realize the open and close principle. Because we can use the base class object everywhere, we can use the subclass object, so we try to use the base class type to define the object in the program, and then determine the subclass type at run time, and replace the parent class object with the subclass object.

Advantages: it can verify the correctness of inheritance and restrict the overflow of inheritance in use.

2.3 Dependency inversion principle

Abstractions should not depend on concrete classes; concrete classes should depend on abstractions. In other words, program for the interface, not the implementation.

Rely on reverse principles dictate that we pass parameters in the program code or in the relationship, cited the high level abstraction layer class as far as possible, that is using an interface and an abstract class variable type declarations, parameter type declaration, the method return type declarations, as well as the data type conversion, etc., rather than a concrete class to do this. To ensure that this principle applies, a concrete class should only implement methods declared in the interface or abstract class, and should not give out redundant methods that would otherwise fail to call new methods added in the subclass.

After introducing the abstraction layer, the system will have good flexibility, try to use in your application programming abstraction layer, and a concrete class will be written in a configuration file, in this way, if the system behavior change, only need to extend the abstract layer, and modify the configuration file, without having to modify the source code of original system, without modifying down to extend functionality of the system, Meet the requirements of the open and close principle.

Advantages: Through abstraction to build the framework, establish classes and classes of association, to reduce the coupling between classes. Moreover, the system built by abstraction is more stable, more expansible and easier to maintain than the system built by concrete implementation.

2.4 Principle of single responsibility

A class is responsible for only one function area, or can be defined as: in the case of a class, there should be only one cause for its change.

The single responsibility principle tells us that a class can’t be too “tired”! In software systems, a class (big to the module, small to method) to assume the responsibility of, the more it is reuse the possibility of the smaller, and a class to assume the responsibility of too much, is equivalent to the duty coupled together, when one of the responsibilities change, may affect the operation of other duties, so these responsibilities, Encapsulate different responsibilities in different classes, that is, encapsulate different reasons for change in different classes, or encapsulate multiple responsibilities in the same class if they always change at the same time.

The single responsibility principle is the guideline to achieve high cohesion and low coupling. It is the simplest but most difficult principle to use, requiring designers to discover different responsibilities of classes and separate them, while discovering multiple responsibilities of classes requires designers to have strong analysis and design ability and relevant practical experience.

Advantages: If the responsibilities of classes and methods are clearly divided, it not only improves the readability of the code, but also substantially reduces the risk of errors. Because the code is clear, there is no place for bugs to hide, but also facilitates bug tracking, which reduces the maintenance cost of the program.

2.5 Demeter’s Rule (Least Know Principle)

A software entity should interact with as few other entities as possible

If a system conforms to Demeter’s law, when one module is modified, other modules will be affected as little as possible and expansion will be relatively easy. This is the limitation of communication between software entities. Demeter’s law requires that the width and depth of communication between software entities be limited. Demeter’s law can reduce the coupling degree of the system and keep loose coupling relationship between classes.

When applying Demeter’s law to system design, we should pay attention to the following points: in the division of classes, we should try to create loosely coupled classes, the lower the degree of coupling between classes, the more conducive to reuse, once a loosely coupled class is modified, it will not cause too much impact to the associated classes. In class structure design, each class should minimize the access of its member variables and member functions. In class design, whenever possible, a type should be designed as an immutable class. An object’s references to other objects should be minimized to references to other classes.

Advantages: Practicing Demeter’s law can well reduce the coupling between classes, reduce the degree of association between classes, and make the collaboration between classes more direct.

2.6 Separating Interfaces

Using multiple specialized interfaces instead of a single master interface means that a client should not rely on interfaces it does not need.

According to the interface isolation principle, when an interface is too large, we need to divide it into smaller interfaces, and the clients using the interface need only know the methods associated with it. Each interface should assume a relatively independent role, not doing what should not be done, do what should be done.

When using the interface isolation principle, you need to control the granularity of interfaces. If the interfaces are too small, the system will be flooded with interfaces, which is not conducive to maintenance. The interface should not be too large. Too large interfaces violate the interface isolation principle, resulting in poor flexibility and inconvenient use.

Advantages: Avoid the method that the same interface contains different types of responsibilities, and the interface responsibility division is more clear, in line with the idea of high cohesion and low coupling.

2.8 Six principles – learning experience

Among the six principles, the open and close principle, Richter’s substitution principle and dependence inversion principle are closely related. The latter two are important prerequisites for the realization of the open and close principle, and have good scalability and maintainability through abstract design in use.

Knowing the least reduces coupling, reduces unnecessary interactions, advocates simple interfaces and classes that encapsulate complex logic and provide easy-to-use interfaces.

The single responsibility principle allows classes and methods in a project to be subdivided by responsibility, avoiding overloading a single class. The more responsibilities there are, the less likely they are to be reused or cumbersome to use.

The interface separation principle divides interfaces with complex functions into multiple interfaces with specific functions. The interfaces only do what they need to do to reduce coupling. However, the granularity of refinement should not be too fine, which may lead to too many interfaces. The principle of single responsibility emphasizes the design of subdivision within a single class according to responsibilities, while the principle of interface separation emphasizes the coupling between classes and tries to establish the minimum dependence relationship.

Third, pattern classification

There are 23 design patterns in Design Patterns: The Foundation of Reusable Object-oriented Software, each of which offers a solution to a repeatable design problem. According to their uses, design patterns can be divided into Creational, Structural and Behavioral patterns. The Creational pattern mainly describes how to create objects, while the Structural pattern mainly describes how to achieve a combination of classes or objects. The behavioral pattern describes how classes or objects interact and assign responsibilities.

In addition, design patterns can be divided into class patterns and object patterns, depending on whether a pattern is primarily used to deal with relationships between classes or objects. We often use a combination of the two categories, such as the singleton pattern as the object creation pattern and the template method pattern as the class behavior pattern.

Type 3.1 to create

The Creational Pattern abstracts the instantiation process of a class, separating the creation and use of objects in a module. In order to make the structure more clear, the outside world only needs to know their common interface for these objects, but does not know its specific implementation details, so that the design of the whole system is more in line with the principle of single responsibility.

  1. Simple Factory Pattern
  2. Factory Method Pattern
  3. Abstract Factory Pattern
  4. Singleton Pattern
  5. Builder Pattern
  6. Prototype Pattern

3.2 structural

Structural patterns describe how to combine classes or pairs together to form larger structures. Just like building blocks, complex and more powerful structures can be formed by combining simple blocks. Structural patterns can be divided into class structural patterns and object structural patterns:

  • The class-structured pattern is concerned with the composition of classes, which can be combined into a larger system by multiple classes. Generally, there are only inheritance and implementation relationships in the class-structured pattern.

  • The object structural pattern is concerned with the composition of classes and objects by associating them so that an instance object of another class is defined in one class and its methods are called through that object. According to the principle of composite reuse, relational relationships are used instead of inheritance relationships in the system, so most structural patterns are object structural patterns.

  1. The appearance model
  2. Adapter mode
  3. The bridge model
  4. The proxy pattern
  5. Decorator pattern
  6. The flyweight pattern

3.3 behavior type

Behavioral patterns are abstractions that divide responsibilities and algorithms among different objects. Behavioral patterns focus not only on the structure of classes and objects, but also on their interactions. Behavioral patterns allow for a clearer division of the responsibilities of classes and objects, and for studying the interaction between instance objects at run time.

  1. Chain of Responsibility model
  2. Command mode
  3. Interpreter mode
  4. Iterator pattern
  5. The mediator pattern
  6. Memo mode
  7. Observer model
  8. The state pattern
  9. The strategy pattern
  10. Template method pattern
  11. Visitor pattern

4. Create – Design patterns

4.1 Simple Factory mode

Simple Factory Pattern: A class (Factory class) is specifically defined to create instances of other classes. Instances of different classes can be returned based on the arguments of the creation method, and the created instances usually have a common parent class.

For example:

The simple factory model is like a foundry, where a factory can produce many products. For example, a beverage processing plant that produces both Pepsi and Coca-Cola produces different products based on the input parameter Type.

// interface Cola: NSObject @end // interface CocaCola: Cola @end // Interface PesiCola: Cola @endCopy the code
@implementation SimpleFactory + (Cola *)createColaWithType:(NSInteger)type {
    switch (type) {
        caseZero:return [CocaCola new];
        case 1:
            return [PesiCola new];
        default:
            return nil;
            break;
    }
}
@end
Copy the code
CocaCola = [SimpleFactory createColaWithType:0]; PesiCola = [SimpleFactory createColaWithType:1];Copy the code

Advantages:

  • The user can simply pass in the correct, conventionally-agreed parameters to the factory class and get the object you need without knowing the creation details, reducing the coupling of the system to a certain extent.
  • The client does not need to know the class name of the specific product class to be created, but only needs to know the parameters corresponding to the specific product class, reducing the memory cost of developers.

Disadvantages:

  • If a new product is added to the business, the original judgment logic of the factory class needs to be modified, which actually violates the open closed principle.
  • With a large number of product types, the factory logic can become too complex. Therefore, the simple factory model is suitable for the situation where the product category is relatively small and the probability of increasing is very low.

4.2 Factory method pattern

Factory Method Pattern (Factory Method Pattern) is also called Factory Pattern. The Factory parent class is responsible for defining the common interface for creating product objects, while the Factory subclass is responsible for generating specific product objects. That is, different Factory subclasses are used to create different product objects.

For example:

There are some differences between the factory method and the simple factory. The simple factory produces different products by an OEM, while the factory method abstracts the factory, and different products are produced by specialized specific factories. The Coca-Cola factory produces Coca-Cola, and the Pepsi factory produces Pepsi.

@implementation Factory + (Cola *)createCola {return[Cola new]; } @implementation CocaColaFactory + (Cola *)createCola {return[CocaCola new]; } @end @implementation PesiColaFactory + (Cola *)createCola {return [PesiCola new];
}
@end
Copy the code
// Cola *pesiCola = [PesiColaFactory createCola]; Cola *cocaCola = [CocaColaFactory createCola];Copy the code

Advantages:

  • Users only need to care about the specific factory corresponding to the product they need, and do not need to care about the creation details of the product, nor do they need to know the class name of the specific product class.
  • When a new product is added to the system, there is no need to modify the abstract factory and the interface provided by the abstract product, nor need to modify the client and other specific factories and products, but only need to add a specific factory and its corresponding specific products, in line with the open and closed principle.

Disadvantages:

  • When a new product is added to the system, in addition to the new product class, the corresponding concrete factory class should be provided. So the number of classes in the system will increase in pairs, increasing the complexity of the system.

4.3 Abstract factory pattern

Abstract Factory Pattern: Provides an interface for creating a series of related or interdependent objects without specifying their concrete classes.

For example:

The abstract factory differs from the factory approach in that the factory that produces the product is abstract. For example, when Coca-Cola produces coke, it also needs to produce bottles and cases for coke, and the bottles and cases are also customized by Coca-Cola, and Pepsi also has this demand. At this time, our factory is not only a factory producing cola drinks, but also a factory producing bottles and boxes with the same theme at the same time. Therefore, it is an abstract theme factory specializing in producing different commodities with the same theme.

@interface Cola: NSObject @end @interface CocaCola: Cola @end @interface PesiCola: Cola @end // Bottle abstract and derived classes @interface Bottle: NSObject @end @interface CocaColaBottle: Bottle@end @interface PesiColaBottle: bottle@end and derived classes @interface Box: NSObject @end @interface CocaColaBox : Box @end @interface PesiColaBox : Box @endCopy the code
@implementation Factory + (Cola *)createCola {return [Cola new];
}
+ (Bottle *)createBottle {
    return [Bottle new];
}
+ (Box *)createBox {
    return[Box new]; } @implementation CocaColaFactory + (CocaCola *)createCola {return [CocaCola new];
}
+ (CocaColaBottle *)createBottle {
    return [CocaColaBottle new];
}
+ (CocaColaBox *)createBox {
    return[CocaColaBox new]; } @end @implementation PesiColaFactory + (PesiCola *)createCola {return [PesiCola new];
}
+ (PesiColaBottle *)createBottle {
    return [PesiColaBottle new];
}
+ (PesiColaBox *)createBox {
    return [PesiColaBox new];
}
@end
Copy the code
CocaCola = [CocaColaFactory createCola]; Bottle *cocaColaBottle = [CocaColaFactory createBottle]; Box *cocaColaBox = [CocaColaFactory createBox]; PesiCola = [PesiColaFactory createCola]; Bottle *pesiColaBottle = [PesiColaFactory createBottle]; Box *pesiColaBox = [PesiColaFactory createBox];Copy the code

Advantages:

  • Specific products are isolated in the application layer code and do not need to care about product details. When multiple objects in a product family are designed to work together, it ensures that the client always uses only objects in the same product family. This is a very useful design pattern for software systems that need to determine their behavior based on the current environment.

Disadvantages:

  • It specifies all possible product sets that can be created. It is difficult to extend new products in the product family, requiring modifications to the interface of the abstract factory.

4.4 Singleton Mode

Singleton Pattern: The Singleton Pattern ensures that there is only one instance of a class and provides a full access point to it.

For example:

In singleton mode, only one instance of a corresponding class can be generated. Just as there can only be one king in a kingdom, once the affairs of the kingdom become too much, the only king is apt to be too much.

@implementation Singleton

+ (instancetype)shareInstance {
    static Singleton *shareInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        shareInstance = [[Singleton alloc] init];
    });
    return shareInstance;
}

@end
Copy the code

Advantages:

  • Provides controlled access to a unique instance. Because a singleton class encapsulates its unique instance, it has tight control over how and when customers access it.
  • Because this class has only one object in system memory, it saves system resources.

Disadvantages:

  • Because there is no layer of abstraction in the singleton pattern, singleton classes are difficult to extend.
  • For Java and C#, languages with garbage collection systems, objects may be collected if they are not used for a long time. So if this singleton holds some data, it will not be there when it is recycled and re-instantiated.

4.5 Generator Mode

Builder Pattern: Also known as creator Pattern, it separates the construction of a complex object from its representation so that the same construction process can create different representations.

For example:

The generator pattern splits complex creation logic, such as building a car, into steps to create and install different parts. There is no need to split if the creation logic is simple.

// @interface Builder: NSObject + (void)buildEngine; + (void)buildWheel; + (void)buildBody; @endCopy the code
Builder * Builder = [Builder new]; [builder buildBody]; [builder buildWheel]; [builder buildEngine];Copy the code

Advantages:

  • The client does not have to know the details of the product’s internal composition, decoupling the product itself from the product creation process so that the same creation process can create different product objects.
  • Each concrete builder is relatively independent of other concrete builders, so it is easy to replace concrete builders or add new concrete builders, and users can use different concrete builders to get different product objects.
  • Adding a new concrete builder does not need to modify the code of the original class library, and the command class is designed for the abstract builder class, so the system is easy to expand and conforms to the “open and close principle”.
  • The product creation process can be more finely controlled. Breaking down the creation steps of complex products into different methods makes the creation process clearer and easier to control programmatically.

Disadvantages:

  • The products created by the Builder mode generally have more in common and their components are similar. If the differences between the products are large, the builder mode is not suitable for use, so its application scope is limited to a certain extent.
  • If the internal changes of the product are complex, many specific builder classes may need to be defined to implement the changes, resulting in a large system.

4.6 Prototype Mode

Prototype Pattern: Use a Prototype instance to specify the type of the object to be created, and create new objects by copying the Prototype.

For example:

Prototyping is like photocopying, making a new copy of the original object and tweaking the new object as needed.

@interface Student : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *age;
@property (nonatomic, copy) NSString *class;
@property (nonatomic, copy) NSString *school;

@end

Copy the code
// Student *lily = [Student alloc] init]; lily.name = @"lily";
lily.age = @"13";
lily.class = @"Class ONE five years";
lily.school = @"Realizing the School"; Student * Tom = [lily copy]; // Fine-tune tom.name = @ based on the original object"tom";

Copy the code

Advantages:

  • The prototype mode can be used to simplify the creation process of objects, especially for some objects with complicated creation process and many object hierarchies, the use of prototype mode can save system resources and improve the efficiency of object generation.
  • You can easily generate new objects by changing values: some objects may differ only in some values; Prototype mode allows you to quickly copy new objects and manually modify the values.

Disadvantages:

  • Object contains all objects that need to be equipped with a clone method, which makes for a lot of code and complexity in the case of a large number of object hierarchies.

Five, structure – design mode

5.1 Decoration Mode

Decorator Pattern: To dynamically add additional functionality to an object without changing the original object.

For example:

The decoration mode conforms to the open and close principle, and transforms or adds new functions to the parent class without changing the original class. For example, an abstract class named “Tea” can only provide plain water. However, new functions have been expanded through the decoration class “BlackTea”. Through the BlackTea class, people can make BlackTea with plain water and choose to add lemon.

@interface Tea : NSObject + (instancetype)createTea; @end @interface BlackTea : Tea @property (nonatomic, strong) Tea *tea; // addBlackTea - (void)addBlackTea; // Black tea can be added lemon - (void)addLemon; @endCopy the code
@implementation Tea

+ (instancetype)createTea {
    NSLog(@"add water");
    return[self new]; } @implementation BlackTea + (instanceType)createTea {return [self new];
}

- (void)addBlackTea {
    NSLog(@"add black tea");
}

- (void)addLemon {
    NSLog(@"add lemon");
}

@end
Copy the code
// Tea * Tea = [Tea createTea]; // output: add water // BlackTea * BlackTea = [BlackTea createTea]; blackTea.tea = tea [blackTea addBlackTea]; [blackTea addLemon]; // output: // add black tea // add lemonCopy the code

Advantages:

  • More flexible than inheritance: different from inheritance that works at compile time; Decorator pattern extends the functionality of an object at run time. Alternatively, different decorators can be selected at run time through configuration files to achieve different behavior. It can also achieve different effects through different combinations.
  • Accord with “open close principle” : decorator and decorator can change independently. Users can add new decoration classes as needed, and then combine them when used, without changing the original code.

Disadvantages:

  • The decorator pattern requires the creation of concrete decorator classes, which can add complexity to the system.

5.2 Appearance Mode

Facade Pattern: A Facade Pattern defines a high-level interface that provides a unified interface for a set of interfaces in a subsystem. Appearance mode, also known as facade mode, is a structural design mode.

For example:

The facade pattern provides a simple and unambiguous interface, but integrates many sub-system functions inside. Like image caching, which involves processing of other subsystems such as caching and downloading, the facade hides all of this complex logic. When you call UIImageView and UIButton, all you have to do is call a setImageWithUrl:(NSString *)url interface to decouple it.

@implementation WebImage + (UIImage *)getImageWithUrl:(NSString *)url {cacheImage = [ImageCaches getImageFromCacheWithUrl:url];if (cacheImage) {
        returncacheImage; } downloadImage = [ImageDownloader downloadImageWithUrl:url];if(downloadImage) {// cacheImage [ImageCaches cacheImage:downloadImage];return downloadImage;
    }else{
    	return nil;
    }
}
@end

@implementation UIImageView + WebImage / UIButton + WebImage

- (void)setImageWithUrl:(NSString *)url {
    UIImage webImage = [WebImage getImageWithUrl:url];
    if (webImage) {
    	[self setImage:webImage];
    }
}
@end


Copy the code
UIImageView *webImage = [UIImageView new]; [webImagesetImageWithUrl:@"https://imageUrl"];

UIButton *webButton = [UIButton new];
[webButton setImageWithUrl:@"https://imageUrl"];

Copy the code

Advantages:

  • Decoupling between the client and subsystem is realized: the client does not need to know the interface of the subsystem, which simplifies the call process of the client to call the subsystem and makes it easier to use the subsystem. At the same time convenient subsystem expansion and maintenance.
  • It follows Demeter’s rule (least know) : the subsystem only needs to expose interfaces that need to be called externally to the facade class, and its interfaces can be hidden.

Disadvantages:

  • Violation of the open close principle: Adding a new subsystem may require modifications to the facade or client code without introducing abstract facade classes.

5.3 Proxy Mode

Proxy Pattern: Provides a Proxy for an object that controls access to the original object.

For example:

The agent mode is like a real estate agency. The buyer can only buy a house through the agent. The agent has all the functions of the agent, just like the landlord has the function of selling houses, and the agent also has the function of selling houses. In addition, the agent instance can also help the agent instance to carry out some additional processing, such as the intermediary can help the landlord screen high-quality buyers, help the landlord pass some unqualified buyers. The same pattern applies to message queues.

// @interface Customer () @property (nonatomic, strong) Waiter * Waiter; @end@implementation Customer - (Waiter *)callWaiter {_waiter = [Waiter new];return_waiter; } // (void)orderingFood:(NSString *)food {[_waiter orderingFood:food]; } // (void)removeFood:(NSString *)food {[_waiter removeFood:food]; } @end // @interface Waiter () @property (nonatomic, strong) NSMutableArray *cacheList; - (void)orderingFood:(NSString *)food {[self.cacheList addObject:food]; } - (void)removeFood:(NSString *)food {[self.cacheList removeObject:food]; } / / push the first order to cook - (void) pushToChef: Chef *) Chef {[Chef cookFood: self. CacheList. FirstObject]; [self.cacheList removeObject:self.cacheList.firstObject]; } @end @implementation Chef - (void)cookFood:(NSString *)food {NSLog(@)"cook %@",food);
}

@end
Copy the code
// Chef * Chef = [Chef new]; // Customer *lily = [Customer new]; // The customer calls the waiter [Lily callWaiter]; // Customer order [lily orderingFood:@"Chicken and Mushroom stew."];
[lily orderingFood:@"Dongpo Pork"];
[lily orderingFood:@The King of Shrimp Dumplings.];
[lily orderingFood:@"Braised prawns"]; // The customer cancells a dish [Lily removeFood:@]"Dongpo Pork"]; [waiter pushToChef:chef] [waiter pushToChef:chef]Copy the code

Advantages:

  • Reduce the coupling degree of the system: the proxy mode can coordinate the caller and the called, and reduce the coupling degree of the system to a certain extent.
  • Different types of proxies can have different controls over client access to target objects:
    • Remote proxy enables clients to access objects on remote machines, which may have better computing performance and processing speed, and can quickly respond to and process client requests.
    • By using a small object to represent a large object, virtual agents can reduce the consumption of system resources, optimize the system, and improve the speed of operation.
    • A protection agent can control client access to real objects.

Disadvantages:

  • Adding a proxy object between the client and the proxied object may slow down client requests.

5.4 Share Mode

Flyweight Pattern: use sharing technology to reuse a large number of fine-grained objects, reduce the occupation of program memory, improve the performance of the program.

For example:

For example, UITableViewCell cache mechanism, to reduce the purpose of memory consumption. For example, music services are divided into free users and members based on fees. Free users can only listen to part of the free music, while members can listen to all the music and download it. There are some differences in permissions, but the music they enjoy comes from the same library, so you only need to save one copy of all the music. In addition, if the music does not exist in the music library, you need to add the music, and other services can also enjoy the new music, which is equivalent to the function of the metadata pool or cache pool.

The share mode area ensures the sharing of internal states, such as music library, while external states are customized according to different needs, such as various access permissions. Internal states cannot be changed during use to achieve the purpose of sharing.

@property (nonatomic, strong) NSArray *musicLibrary; @end@implementation MusicService - (void)listenToMusct:(NSString *)music {... } // downloadMusic - (void)downloadMusic:(NSString *)music {... } @end // @interface FreeMusicService: NSObject @property (nonatomic, strong) MusicService *musicSever; - (void)listenFreeMusic:(NSString *)music; - (void)listenToFreeMusic:(NSString *)music {if ([music isEqualToString:@"free"[self.musicSever listenMusct:music]; }else{// If it is paid music, the user will be prompted to upgrade the Vip NSLog(@"please upgrade to Vip"); } @end // VipMusicService: NSObject @property (nonatomic, strong) MusicService *musicSever; - (void)listenMusic:(NSString *)music; - (void)downloadMusic:(NSString *)music; @end@implementation VipMusicService - (void)listenToMusic:(NSString *)music {[self listenMusct:music]; } // you can downloadMusic - (void)downloadMusic:(NSString *)music {[self.musicSever downloadMusic:music]; } @endCopy the code
// Create a new base library MusicService * MusicService = [MusicService new]; // FreeMusicService *freeService = [FreeMusicService new]; // VipMusicService *vipService = [VipMusicService new]; FreeService. MusicSever = musicService; vipService.musicSever = musicService; [freeService listenFreeMusic:@"Free music"];
[vipService listenMusic:@"All music"];
Copy the code

Advantages:

  • Using the free element module can reduce the number of objects in the memory, so that only one copy of the same object or similar object can be saved in the memory, reducing the memory usage of the system and improving performance.
  • The external state of the share schema is relatively independent and does not affect its internal state, allowing share objects to be shared in different environments.

Disadvantages:

  • Using the share pattern requires the separation of internal and external state, which complicates the logic of the program.
  • Reuse of objects in buffer pools requires consideration of threads.

5.5 Bridge Mode

Bridge Pattern: Separate the abstract part from its implementation part so that they can all change independently.

For example:

Although the phones are different, they all have a card slot that can be used by different carriers. No matter how the phone or card interior changes, as long as the industry standard for the card slot does not change, it will work fine. The bridge mode is to divide the complex classes and combine them in a way of priority objects, just like pulling out the mobile card in the mobile phone and creating a new class to realize the combination of mobile phone instances holding one instance of mobile phone cards. Rather than creating a new phone subclass with multiple different phone cards by inheritance.

@protocol SIMCardProtocol <NSObject> - (void)getSIMInfo; @end // SIM card abstract class @interface SIMCard: NSObject - (void)getSIMInfo; @implementation SIMCard - (void)getSIMInfo {} @end SIMCard<SIMCardProtocol> @end @implementation UnicomSIMCard - (void)getSIMInfo { NSLog(@"Welcome Unicom User"); } @end // MobileSIMCard @interface MobileSIMCard: SIMCard<SIMCardProtocol> @end @implementation MobileSIMCard - (void)getSIMInfo { NSLog(@"Welcome Mobile User"); } @end // @interface Phone: NSObject // Hold SIM card @property (nonatomic, strong) SIMCard * SIMCard; // launchPhone - (void)launchPhone; @end @implementation Phone - (void)launchPhone {if(self.simCard) { [self.simCard getSIMInfo]; }} @end // iPhone @interface iPhone: Phone @end // Xiaomi mobile @interface miPhone: Phone @endCopy the code
// UnicomSIMCard *unicomSim = [UnicomSIMCard new]; // MobileSIMCard *mobileSim = [MobileSIMCard new]; // miPhone *mi9 = [miPhone new]; [mi9setSimCard:unicomSim]; // iPhone *iPhoneX = [iPhone new]; [iPhoneXsetSimCard:mobileSim]; // Start [mi9 launchPhone]; [iPhoneX launchPhone];Copy the code

SIMCardProtocol is an industry standard, so mobile cards follow it. Each handset manufacturer knows the protocol and can use it directly to gain access to the SIM card’s internal information.

Advantages:

  • It is extensible and conforms to the open and closed principle: it separates abstraction from implementation so that the two can change independently

Disadvantages:

  • Before design, two dimensions that vary independently need to be identified.

5.6 Adapter Mode

Adapter Pattern: The transformation of one interface into another interface desired by the customer so that classes that would otherwise not work together due to interface incompatibilities can work together. The adapter pattern, alias Wrapper pattern, is a structural design pattern.

For example:

Adapter mode as the name implies, such as mainland use like the Hong Kong version of the plug requires a adapter. Another example is the Nano card of iPhone, which is very small. The Nano card cannot fit the size of the card slot when it is carried to other phones, so we need to add a cover that matches the size of the card slot.

@protocol StandardSIMSizeProtocol <NSObject> - (void)normalSize; @end // nano card size protocol @protocol NanoSIMSizeProtocol <NSObject> - (void)nanoSize; @end // Standard card complies with standard protocol @interface StandardSIMCard: SIMCard<StandardSIMSizeProtocol> @end@implementation StandardSIMCard - (void)normalSize {} @end // The nano card complies with the Nano protocol @interface NanoSIMCard : SIMCard<NanoSIMSizeProtocol> @end @implementation NanoSIMCard - (void) implementation nanoSize {} @end // Nano  : SIMCard<StandardSIMSizeProtocol> @property (nonatomic, strong) NanoSIMCard *nanoSIMCard; @end @implementation NanoAdapter - (void)normalSize {} @end // @interface OnePhone : Phone - (void)setSimCard:(SIMCard *)simCard;

@end

@implementation OnePhone
- (void)setSimCard:(SIMCard *)simCard {
    [simCard normalSize];
}
@end
Copy the code
// StandardSIMCard *standardCard = [StandardSIMCard new]; // NanoSIMCard *nanoCard = [NanoSIMCard new]; NormalSize () OnePhone * OnePhone = [OnePhone new]; // Standard cards follow the StandardSIMSizeProtocol, which implements the normalSize() method [onePhonesetSimCard:standardCard]; // Nano follows the NanoSIMSizeProtocol and does not implement normalSize(), so an error is reported [onePhone]setSimCard:nanoCard]; // Add a NanoAdapter * NanoAdapter = [NanoAdapter new]; Nanoadapter. nanoSIMCard = nanoCard; // Put the jacket into the phone. The jacket follows StandardSIMSizeProtocol, which implements normalSize()setSimCard:nanoAdapter];
Copy the code

Advantages:

  • Comply with the open closed principle: Use adapters without changing existing classes, improving class reuse.
  • Decouple the target class from the adapter class to improve program extensibility.

Disadvantages:

  • Increased system complexity

Behavior – Design pattern

6.1 Responsibility Chain Mode

Chain of Responsibility Pattern: Avoid coupling the sender of a request with the receiver, make it possible for multiple objects to receive a request, connect these objects into a Chain, and pass the request along the Chain until an object handles it. The responsibility chain pattern is an object behavior pattern.

For example:

The responsibility chain mode is widely used in iOS, such as the event response chain. After an event is passed, it will first determine whether the event should be handled by itself, and if it is not handled by itself, it will be passed to the next responder for processing, and so on. Care should be taken to avoid a loop of response chain calls and situations where all responders are unable to handle them.

// Responder @interface Responder: NSObject @property (nonatomic, strong) Responder *nextResponder; @property (nonatomic, strong) NSString *name; @property (nonatomic, assign) NSUInteger UpperLimit; @property (nonatomic, assign) NSUInteger LowerLimit; - (void)respondWithCode:(NSUInteger)code; @end @implementation Responder - (void)respondWithCode:(NSUInteger)code {if(_LowerLimit <= code && code <= _UpperLimit) {// Within the processing range NSLog(@"code: %ld is %@", code, _name);
    }else if[_nextResponder respondWithCode:code]; [_nextResponder respondWithCode:code]; }else{// The response chain ends with NSLog(@"code: %ld no match responder", code);
    }
}

@end
Copy the code
Responder *successResponder = [Responder new]; Responder *warmingResponder = [Responder new]; Responder *httpResponder = [Responder new]; Responder *serviceResponder = [Responder new]; successResponder.nextResponder = warmingResponder; warmingResponder.nextResponder = httpResponder; httpResponder.nextResponder = serviceResponder; / / set the range successResponder. LowerLimit = 200; successResponder.UpperLimit = 299; successResponder.name = @"success";

warmingResponder.LowerLimit = 300;
warmingResponder.UpperLimit = 399;
warmingResponder.name = @"warming";

httpResponder.LowerLimit = 400;
httpResponder.UpperLimit = 499;
httpResponder.name = @"http fail";

serviceResponder.LowerLimit = 500;
serviceResponder.UpperLimit = 599;
serviceResponder.name = @"service fail"; // Use the responder chain [successResponder respondWithCode:200]; // code: 200 is success [successResponder respondWithCode:310]; // code: 310 is warming [successResponder respondWithCode:401]; // code: 401 is http fail [successResponder respondWithCode:555]; // code: 555 is service fail [successResponder respondWithCode:666]; // code: 666 no match responderCopy the code

Advantages:

  • In the chain of responsibility mode, an object does not need to know which other object processes its request, but only needs to know that the request will be processed. The receiver and sender have no clear information about each other, and the object in the chain does not need to know the structure of the chain. The client is responsible for the creation of the chain, which reduces the coupling degree of the system.
  • The request-processing object maintains only one reference to its successors, rather than all its references to candidate handlers, simplifying the interconnections of objects.
  • Chains of responsibility give us more flexibility when assigning responsibilities to objects, adding or changing responsibilities for handling a request by dynamically adding or modifying the chain at run time.
  • Adding a new concrete request handler to the system does not need to modify the original system code, but only needs to re-create the chain on the client side, which conforms to the “open closed principle”.

Disadvantages:

  • Because a request has no clear recipient, there is no guarantee that it will be processed, and the request may not be processed all the way to the end of the chain; A request may not be processed because the chain of responsibility is not configured correctly.
  • For a long chain of responsibilities, the processing of the request may involve multiple processing objects, which will affect system performance and make it inconvenient to debug the code.
  • If the chain is not built properly, it may cause circular calls, which will lead to the system falling into an infinite loop.

6.2 Command Mode

Command Pattern: Encapsulating a request as an object, allowing us to parameterize customers with different requests; Queue or log requests, and support undoable operations. Command mode is an object behavior mode, alias for Action mode or Transaction mode.

For example:

Similar to the examples in the proxy pattern, the essence of the command pattern is to encapsulate the command, separating the responsibility for issuing the command from the responsibility for executing the command. For example, the remote control is a caller, different buttons represent different commands, and the TV is a receiver.

@interface Command: nsobject@property (nonatomic, assign) SEL SEL; @property (nonatomic, strong) NSObject *target; - (void)execute; @end @implementation Command - (void)execute { [self.target performSelector:_sel withObject:nil]; } @end -> @interface Controller: NSObject - (void)invokCommand (Command *)command;
// 取消命令
- (void)cancelCommand:(Command *)command;
@end

@implementation Controller

- (void)invokCommand:(Command *)command{[command execute];
}

- (void)cancelCommand:(Command *)command{} @end // receiver -> TV @interface TV: NSObject - (void)turnOn; - (void)turnOff; @end @implementation TV - (void)turnOn { NSLog(@"trun on");
}

- (void)turnOff {
    NSLog(@"trun off");
}

@end
Copy the code
// receiver TV * TV = [TV new]; TurnOnCommand = [Command new]; turnOnCommand = [Command new]; turnOnCommand.sel = @selector(turnOn); TurnOnCommand. Target = TV; Command *turnOffCommand = [Command new]; turnOffCommand.sel = @selector(turnOff); Turn off the TV command turnOffCommand. Target = TV; // Controller * Controller = [Controller new]; / / caller call command directly, not contact the receiver [controller invokCommand: turnOnCommand]; // trun on [controller invokCommand:turnOffCommand]; // trun off // cancelCommand can also be extended on this basisCopy the code

Advantages:

  • Reduce coupling of the system. Because there is no direct reference between the requester and the receiver, the requester and the receiver achieve complete decoupling, the same requester can correspond to different receivers, similarly, the same receiver can also be used by different requester, with good independence between the two.
  • New commands can easily be added to the system. Since adding a new concrete command class does not affect other classes, it is easy to add a new concrete command class without modifying the original system source code, or even the customer class code, to meet the requirements of the “open closed principle”.
  • It is relatively easy to design a command queue or a macro command (composite command).
  • It provides a design and implementation scheme for Undo and Redo operations of requests.

Disadvantages:

  • Using command mode can cause some systems to have too many specific command classes. Because a concrete command class needs to be designed for each invocation to the request receiver, in some systems a large number of concrete command classes may need to be provided, which will affect the use of the command pattern.

6.3 Interpreter mode

Interpreter Pattern: Defines the grammar of a language and sets up an Interpreter to interpret the sentences in the language. The “language” here refers to the code that uses the prescribed format and syntax. The interpreter pattern is a behavioral pattern.

For example:

Speaking of interpreter mode, our compiler also uses this mode when it compiles code. We can just make a simple interpreter, an interpreter that gives instructions to robots.

The command parameter
Direction of movement ‘up’ ‘down’ ‘left’ ‘right’
Action Movement mode ‘move’ ‘run’
Distance moving distance an integer
Expression terminator ‘; ‘

The instructions can be quickly converted into actions by establishing a mapping, such as up Run 5; Move up 5 meters, left move 12; That means we’re moving 12 meters to the left.

Advantages:

  • Easy to change and extend grammars. Because classes are used in the interpreter schema to represent the grammar rules of the language, the grammar can be changed or extended through mechanisms such as inheritance.
  • Each rule can be represented as a class, making it easy to implement a simple language.
  • Implementing a grammar is easier. Each expression node class in the abstract syntax tree is implemented similarly, the code for these classes is not particularly complicated, and there are tools that automatically generate the node class code.
  • It is convenient to add new interpretive expressions. If a user needs to add a new interpreted expression, he only needs to add a new terminal expression or a non-terminal expression class. The code of the original expression class does not need to be modified, which conforms to the “open closed principle”.

Disadvantages:

  • Difficult to maintain for complex grammars. In the interpreter mode, each rule needs to define at least one class. Therefore, if a language contains too many grammar rules, the number of classes will increase rapidly, which makes it difficult to manage and maintain the system. In this case, you can use a parser to replace the interpreter mode.
  • Execution efficiency is low. Because of the large number of loops and recursive calls used in interpreter mode, it is slow to interpret complex sentences and cumbersome to debug the code.

6.4 Iterator mode

Iterator Pattern: Provides a way to access an aggregate object without exposing the internal representation of the object, alias Cursor. The iterator pattern is an object behavior pattern.

For example:

Iterators help requesters get the data, avoiding direct manipulation of the data aggregation class and allowing the data aggregation class to focus on storing the data. Specific applications include paging and other functions. The iterator of paging function will be specifically responsible for operating paging data, separating operation logic from data source.

@interface List: NSObject @property (nonatomic, strong) NSArray * List; @end @implementation List - (instancetype)init { self = [super init];if(self) {// Original data _list = @[@"0"The @"1"The @"2"The @"3"The @"4"The @"5"];
    }
    returnself; } @end // @interface Iterator: NSObject @property (nonatomic, assign) NSUInteger index; - (NSString *)previous; - (NSString *)next; // Next data - (BOOL)isFirst; @end@interface Iterator () @property (nonatomic, strong) List * List; @end @implementation Iterator - (instancetype)init { self = [super init];if (self) {
        _list = [List new];
    }
    return self;
}

- (NSString *)previous {
    _index = MAX(0, _index - 1);
    return [_list.list objectAtIndex:_index];
}

- (NSString *)next {
    _index = MIN(_list.list.count-1, _index + 1);
    return [_list.list objectAtIndex:_index];
}

- (BOOL)isFirst {
    return _index == 0;
}

@end
Copy the code
Iterator * Iterator = [Iterator new]; NSLog(@"% @",[iterator next]); // 1
NSLog(@"% @",[iterator next]); // 2
NSLog(@"% @",[iterator next]); // 3
NSLog(@"% @",[iterator previous]); / / 2Copy the code

Advantages:

  • It supports traversal of an aggregate object in different ways, and multiple traversals can be defined on the same aggregate object. In iterator mode, we can change the traversal method by replacing the original iterator with a different iterator. We can also define a subclass of iterators to support the new traversal method.
  • Iterators simplify aggregate classes. Due to the introduction of iterator, there is no need to provide data traversal and other methods in the original aggregation object, which can simplify the design of aggregation class.
  • In the iterator pattern, due to the introduction of an abstraction layer, it is convenient to add new aggregate classes and iterator classes without modifying the original code, so as to meet the requirements of the “open closed principle”.

Disadvantages:

  • Since the iterator pattern separates the responsibility of storing data and traversing data, adding new aggregation classes needs to correspond to adding new iterator classes, and the number of classes increases in pairs, which increases the complexity of the system to some extent.
  • Abstract Iterator is difficult to design, and the future expansion of the system needs to be fully taken into account. For example, the built-in Iterator in JDK cannot realize reverse traversal. If reverse traversal is needed, it can only be realized through its subclass ListIterator. ListIterator iterators cannot be used to manipulate aggregate objects of type Set. When customizing iterators, it is not easy to create a fully considered abstract iterator.

6.5 Intermediary model

Mediator Pattern: Encapsulates a set of object interactions with a Mediator object that does not explicitly reference each other, loosens the coupling, and can change their interactions independently. The mediator pattern, also known as the mediator pattern, is an object behavior pattern.

For example:

The mediator pattern transforms a network system structure into a star structure centered on the mediator objects, in which one-to-many relationships between the mediator objects and other objects are used to replace the many-to-many relationships between the original objects. All members interact with each other through the intermediary, facilitating the expansion of new members, such as the following example, to add a member of a chat room, just need to create a new member instance, and then register with the chat room intermediary to join the chat room.

// @mediator: NSObject + (instanceType)shareMediator; - (void)registerChatMember (ChatMember *) ChatMember; - (void)forwardMsg:(NSString *) MSG fromMember:(ChatMember *)fromMember; @end @interface ChatMediator () @property (nonatomic, strong) NSMutableArray *memberList; @end@implementation Mediator + (instanceType) shareInstance {static mediator *shareInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ shareInstance = [[ChatMediator alloc] init]; });return shareInstance;
}

- (instancetype)init
{
    self = [super init];
    if (self) {
        _memberList = [[NSMutableArray alloc] init];
    }
    return self;
}

- (void)registerChatMember:(ChatMember *)chatMember {
    [_memberList addObject:chatMember];
}

- (void)forwardMsg:(NSString *)msg fromMember:(ChatMember *)fromMember {
    [_memberList enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        ChatMember *member = obj;
        if(member ! = fromMember) [member receiveMsg:msg fromMember:member]; }]; } @end // @interface ChatMember: NSObject @property (nonatomic, strong) NSString *userName; // send a message - (void)sendMsg:(NSString *) MSG; - (void)receiveMsg:(NSString *) MSG fromMember:(ChatMember *)fromMember; @end @implementation ChatMember - (void)sendMsg:(NSString *)msg { [[ChatMediator shareMediator] forwardMsg:msg fromMember:self]; } - (void)receiveMsg:(NSString *)msg fromMember:(ChatMember *)fromMember { NSLog(@"%@ receive: %@ from %@", _userName, msg, fromMember.userName);
}

@end
Copy the code
// Mediator *mediator = [mediator]; // Create a new ChatMember *lily = [ChatMember new]; ChatMember *tom = [ChatMember new]; ChatMember *jack = [ChatMember new]; // Mediator registerChatMember:lily [mediator registerChatMember:tom]; [mediator registerChatMember:jack]; // Name lily.userName = @"lily";
tom.userName = @"tom";
jack.userName = @"jack"; // Send message [lily sendMsg:@"hello everyone!"];
[tom sendMsg:@"hello lily!"];
[jack sendMsg:@"hi tom!"]; Output: Tom receive: Hello everyone! from lily jack receive: hello everyone! from lily lily receive: hello lily! from tom jack receive: hello lily! from tom lily receive: hi tom! from jack tom receive: hi tom! from jackCopy the code

Advantages:

  • The intermediary pattern simplifies the interaction between objects. It replaces the many-to-many interaction between colleagues with the one-to-many interaction between intermediaries and colleagues. The one-to-many relationship is easier to understand, maintain and expand, and transforms the originally difficult network structure into a relatively simple star structure.
  • The mediator pattern decouples colleague objects. Intermediaries are conducive to the loose coupling between colleagues. We can independently change and reuse each colleague and intermediary. It is more convenient to add new intermediaries and new colleague classes, which better comply with the “open and closed principle”.
  • The generation of subclasses can be reduced, so that the mediator brings together behaviors that are distributed across multiple objects and changes those behaviors simply by generating new subclasses of the mediator, making the individual colleague classes reusable without extending the colleague class.

Disadvantages:

  • Containing a lot of detail about interactions between colleagues in the concrete intermediary class can lead to a very complex concrete intermediary class, making the system difficult to maintain.

6.6 Memo Mode

Memento Pattern: Capture the internal state of an object and save the state outside of the object without breaking encapsulation, so that the object can be restored to its original saved state at a later time. It is an object behavior pattern, alias Token.

For example:

Memo mode provides a back implementation mechanisms, allows the user to easily back to the history of a particular step, when a new state is invalid or there is a problem, you can use temporarily stored memo will state recovery, many current software provides undo, which use the memo mode.

Let’s use a simple game save as an example, which is also an application of the memo model.

@interface PlayerState: NSObject @property (nonatomic, strong) NSString *name; @property (nonatomic, assign) NSUInteger level; @property (nonatomic, assign) NSUInteger rank; @end@implementation PlayerState@end NSObject @property (nonatomic, strong) PlayerState *state; // Set role state - (void)setPlayerName:(NSString *)name level:(NSUInteger)level rank:(NSUInteger)rank;

@end

@implementation Player
- (void)setPlayerName:(NSString *)name level:(NSUInteger)level rank:(NSUInteger)rank {
    if(! _state) _state = [PlayerState new]; _state.name = name; _state.level = level; _state.rank = rank; } @end // @interface Memorandum: NSObject - (void)storeWithPlayer:(Player *) Player; // restore - (void)restoreWithPlayer:(Player *) Player; @end @interface Memorandum () @property (nonatomic, strong) PlayerState *state; @end@implementation - (void)storeWithPlayer:(Player *) Player {_state = player.state; } - (void)restoreWithPlayer:(Player *)player {if(_state) player.state = _state;
}

@end
Copy the code
A Player *playA = [Player new]; [playAsetPlayerName:@"King"level:99 rank:30]; // Create A Memorandum and save the status Memorandum * Memorandum = [Memorandum new]; [memorandum storeWithPlayer:playA]; Player *playB = [Player new]; [memorandum restoreWithPlayer:playB]; NSLog(@"name:%@ level:%ld rank:%ld", playB.state.name, playB.state.level, playB.state.rank); Output: Name :King level:99 rank:30Copy the code

Advantages:

  • It provides an implementation mechanism for state recovery that allows users to easily go back to a specific historical step, and when a new state is invalid or problematic, the state can be restored using a temporarily stored memo.
  • Memos encapsulate information. A memos object is a representation of the state of the original object and cannot be changed by other code. Memos store the state of the originator. Using lists, stacks and other collections to store memos can achieve multiple undo operations.

Disadvantages:

  • If too many member variables of the original developer class need to be saved, it will inevitably occupy a large amount of storage space. Every time the state of the object is saved, certain system resources will be consumed.

6.7 Observer Mode

Observer Pattern: Defines a one-to-many dependency between objects so that whenever an object’s state changes, its dependent objects are notified and automatically updated. The aliases for the observer pattern include Publish/Subscribe, Model/View, Source/Listener, or Dependents. The observer pattern is an object behavior pattern.

For example:

The observer pattern is one of the most frequently used design patterns. It is used to establish a dependency between objects. When one object changes, other objects will be notified automatically, and other objects will react accordingly.

In iOS, the observer mode is used a lot, and I used KVO to implement a simple example of observing the weather from a weather station.

@interface WeatherStation: nsobject@property (nonatomic, strong) NSString *state; - (void)registerWithObserver:(Observer *) Observer; @end @interface WeatherStation () @property (nonatomic, strong) NSMutableArray *observerList; @end @implementation WeatherStation - (instancetype)init { self = [super init];if (self) {
        [self addObserver:self forKeyPath:@"state" options:NSKeyValueObservingOptionNew context:nil];
    }
    return self;
}

- (void)dealloc {
    [self removeObserver:self forKeyPath:@"state"]; } // target status change listener event - (void) observeforkeypath :(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context { NSString *state = change[@"new"]; [_observerList enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { Observer *ob = obj; [ob observeWeather:state]; }]; } // register method - (void)registerWithObserver:(Observer *) Observer {if(! _observerList) _observerList = [NSMutableArray new]; [_observerList addObject:observer]; } @end // @interface Observer: NSObject @property (nonatomic, strong) NSString *name; - (void)observeWeather:(NSString *)weather; @end @implementation Observer - (void)observeWeather:(NSString *)weather { NSLog(@"%@ observe weather changed: %@", _name, weather);
}

@end
Copy the code
WeatherStation *station = [WeatherStation new]; Observer *farmer = [Observer new]; Observer *student = [Observer new]; farmer.name = @"farmer";
student.name = @"student"; / / listeners registered [station registerWithObserver: farmer]; [station registerWithObserver:student]; // Change the state station.state = @"rainy";
station.state = @"sunny"; Farmer observe weather changed: rainy student observe weather changed: rainy farmer observe weather changed: Rainy student observe weather changed sunny student observe weather changed: sunnyCopy the code

Advantages:

  • The observer pattern can realize the separation of presentation layer and data logic layer, define the stable message update delivery mechanism, and abstract the update interface, so that a variety of different presentation layer can act as a concrete observer.
  • The observer pattern establishes an abstract coupling between the observed object and the observer. The object of observation only needs to maintain a collection of abstract observers, without knowing its specific observers. Since the object and observer are not tightly coupled, they can belong to different levels of abstraction.
  • Observer mode supports broadcast communication, and the observation target sends notifications to all registered observer objects, simplifying the design of a one-to-many system.
  • The observer mode meets the requirements of the “open and closed principle”, and it is not necessary to modify the original system code to add a new specific observer. It is also convenient to add a new observation target when there is no correlation between the specific observer and the observation target.

Disadvantages:

  • If an observation object has many direct and indirect observers, it can take a long time to notify all of them.
  • If there is a cyclic dependency between the observer and the observed target, the observed target can trigger cyclic calls between them, potentially causing the system to crash.
  • The observer mode has no corresponding mechanism to let the observer know how the observed object has changed, but only that the observed object has changed.

6.8 Status Mode

State Pattern: Allows an object to change its behavior when its internal State changes. The object appears to have modified its class. The alias is Objects for States, and state mode is an object behavior mode.

For example:

State patterns are used to solve the problem of state transition of complex objects and encapsulation of behaviors in different states. When there are multiple states of an object in the system, these states can be converted, so the state pattern can be used when the object has different behaviors in different states. The state mode separates the state of an object from the object and encapsulates it into a special state class so that the state of the object can be flexibly changed.

We can take a simple example. I designed a bank account system, which automatically set the account status according to the balance of deposit. Bank accounts can deposit, withdraw and borrow money in different states. In different states, these actions get different responses, such as not being able to withdraw money when there is no balance, but having to borrow money.

// account status abstract class @interface State: NSObject // saveMoney - (BOOL)saveMoney:(float)money; // drawMoney - (BOOL)drawMoney:(BOOL)drawMoneyfloat)money; // borrowMoney - (BOOL)borrowMoney:float)money;

@end

@implementation State

- (BOOL)saveMoney:(float)money {
    return NO;
}

- (BOOL)drawMoney:(float)money {
   return NO;
}

- (BOOL)borrowMoney:(float)money {
   returnNO; } @end @interface RichState: State @end @implementation RichState - (BOOL)saveMoney:(BOOLfloat)money {
    NSLog(@"Welcome to deposit %.2f", money);
    returnYES; } // drawMoney - (BOOL)drawMoney:(BOOL)drawMoneyfloat)money {
    NSLog(@"Welcome to withdraw money %.2f", money);
    returnYES; } // borrowMoney - (BOOL)borrowMoney:(float)money {
    NSLog(@"You have a balance, please spend the balance first.");
    returnNO; } @end @interface ZeroState: State @end @implementation ZeroState - (BOOL)saveMoney:(float)money {
    NSLog(@"Welcome to deposit %.2f", money);
    returnYES; } // drawMoney - (BOOL)drawMoney:(BOOL)drawMoneyfloat)money {
    NSLog(@"You have no current balance.");
    returnNO; } // borrowMoney - (BOOL)borrowMoney:(float)money {
    NSLog(@"Welcome to borrow money %.2f", money);
    returnYES; } @end @interface DebtState: State @end @implementation DebtState - (BOOL)saveMoney:(float)money {
    NSLog(@"Money back is welcome.", money);
    returnYES; } // drawMoney - (BOOL)drawMoney:(BOOL)drawMoneyfloat)money {
    NSLog(@"You have no current balance.");
    returnNO; } // borrowMoney - (BOOL)borrowMoney:(float)money {
    NSLog(@"I haven't paid off my last debt, so I can't borrow money now.");
    returnNO; } @end // @interface Account: NSObject - (void)saveMoney:(float)money; // drawMoney - (void)drawMoney:(void)drawMoneyfloat)money; // borrowMoney - (void)borrowMoney:float)money;

@end

@interface Account ()

@property (nonatomic, assign) floatmoney; // balance @property (nonatomic, strong) State * State; - (instanceType)init {self = [super init];if (self) {
        _money = 0;
        _state = [ZeroState new];
    }
    returnself; } // saveMoney - (void)saveMoney:()float)money {
    if ([_state saveMoney:money]) {
        _money += money;
        [self updateState];
    }
    NSLog(@"Balance: %.2f", _money); } // drawMoney - (void)drawMoney:((void)drawMoneyfloat)money {
    if ([_state drawMoney:money]) {
        _money -= money;
        [self updateState];
    }
    NSLog(@"Balance: %.2f", _money); } // borrowMoney - (void)borrowMoney:float)money {
    if ([_state borrowMoney:money]) {
        _money -= money;
        [self updateState];
    }
    NSLog(@"Balance: %.2f", _money); } // Update account status - (void)updateState {if (_money > 0) {
        _state = [RichState new];
    }else if (_money == 0) {
        _state = [ZeroState new];
    }else{
        _state = [DebtState new];
    }
}

@end
Copy the code
BankAccount *bankAccount = [bankNew]; Drawbankaccount drawMoney:50 [bankAccount drawMoney:50]; BankAccount saveMoney:100 [bankAccount saveMoney:100]; [bankAccount borrowMoney:100]; BankAccount drawMoney:100; bankAccount drawMoney:100; bankAccount drawMoney:100; bankAccount drawMoney:100; [bankAccount borrowMoney:100]; [bankAccount borrowMoney:50] [bankAccount borrowMoney:50]; // Balance: -100 The last account has not been paid off, so I cannot borrow money for the time beingCopy the code

Advantages:

  • The state transformation rules can be encapsulated. In the state mode, the state transformation code can be encapsulated in the environment class or the specific state class, and the state transformation code can be centralized management, rather than scattered in one business method.
  • By putting all the behavior associated with a particular state into one class, you can make the environment object have different behavior by injecting a different state object.
  • By allowing state transition logic to be integrated with state objects, rather than providing a giant block of conditional statements, state patterns allow us to avoid using giant conditional statements to interweave business methods and state transition code.
  • You can reduce the number of objects in the system by having multiple environment objects share a state object.

Disadvantages:

  • The use of state mode will inevitably increase the number of classes and objects in the system, resulting in higher system operating overhead.
  • The structure and implementation of state mode are complicated, if used improperly, it will lead to the confusion of program structure and code, and increase the difficulty of system design.
  • The state mode does not support the “on/off principle” very well. Adding a new state class requires modifying the source code responsible for the state transition, otherwise it cannot be converted to the new state. And modifying the behavior of a status class also requires modifying the source code of the corresponding class.

6.9 Policy Mode

Strategy Pattern: Define a series of algorithm classes, encapsulate each algorithm, and make them interchangeable. The Strategy Pattern makes the algorithm change independently of the customers who use it, also called Policy Pattern. Policy pattern is an object behavior pattern.

For example:

With the policy pattern, we can define several policy classes, each of which encapsulates a specific algorithm. Here, each class that encapsulates the algorithm can be called a policy, and the environment class can execute the algorithm in the different policy class according to the different policy class.

There are many similar examples in life, such as the mall membership card mechanism. When we go shopping in the mall, we can get discounts by holding membership cards. When we buy the same product, we can get discounts with different levels of membership cards. In the following examples, I have listed three KINDS of Vip membership cards: bronze, silver and gold. The final payment amount of different membership cards will be different.

@interface Vip: NSObjectfloat)getDiscount; // Postage for VIP - (float)getPostage; // Calculate price based on discount and postage - (float)calcPrice:(float)price;

@end

@implementation Vip

- (float)getDiscount {
    return 1;
}

- (float)getPostage {
    return 20;
}

- (float)calcPrice:(float)price {
    float payPrice = price*[self getDiscount] + [self getPostage];
    returnpayPrice; } @interface BronzeVip: VIP @implementation BronzeVip - (float)getDiscount {
    return 0.9;
}

- (float)getPostage {
    return20; } @end @implementation SilverVip - (float)getDiscount {
    return 0.7;
}

- (float)getPostage {
    return10; } @end @interface GoldVip: VIP @implementation GoldVip - (float)getDiscount {
    return 0.5;
}

- (float)getPostage {
    return0; } @end // online mall - environment class @interface OnlineShop: NSObject // Buy goods passed hold VIP card - (void)buyProductWithVip:(VIP *) VIP; @end @implementation OnlineShop - (void)buyProductWithVip:(Vip *)vip {float productPrice = 100;
    float payPrice = [vip calcPrice:productPrice];
    NSLog(@"pay: %f", payPrice);
}

@end
Copy the code
// Initialize the shop class OnlineShop *shop = [OnlineShop new]; GoldVip * GoldVip = [GoldVip new]; SilverVip *silverVip = [SilverVip new]; BronzeVip *bronzeVip = [BronzeVip new]; / / use bronze VIP buy $100 worth of goods [shop buyProductWithVip: bronzeVip]; / / discount + 20 freight pay: 110.00 / VIP/use of silver to buy $100 worth of goods [shop buyProductWithVip: silverVip]; / / 7 fold + 10 freight pay: 80.00 / / use gold VIP buy $100 worth of goods [shop buyProductWithVip: goldVip]; // 5 discount + 0 freight pay: 50.00Copy the code

Advantages:

  • The policy pattern provides perfect support for the “open closed principle”, allowing users to choose algorithms or behaviors without modifying the existing system, or to flexibly add new ones.
  • The policy pattern provides a way to manage related algorithm families. The hierarchical structure of a policy class defines an algorithm or behavior family, and inheritance can be used appropriately to move common code into an abstract policy class, thereby avoiding duplicate code.
  • The policy pattern provides an alternative to inheritance. If the policy pattern is not used, an environment class that uses algorithms may have subclasses, each of which provides a different algorithm. However, in this way, the use of the algorithm is mixed with the algorithm itself, which does not conform to the “single responsibility principle”, and the logic of deciding which algorithm to use is mixed with the algorithm itself, so that it is no longer possible to evolve independently; And you can’t use inheritance to dynamically switch algorithms or behaviors while the program is running.
  • Multiple conditional selection statements can be avoided using the policy pattern. Multi-conditional selection statements are not easy to maintain, because they mix the logic of which algorithm or behavior to take with the logic of the implementation of the algorithm or behavior itself, and Hard Coding them all in a large multi-conditional selection statement is more primitive and backward than directly inheriting environment classes.
  • The policy pattern provides a reuse mechanism for algorithms. Because algorithms are isolated and encapsulated in policy classes, different environment classes can easily reuse these policy classes.

Disadvantages:

  • The client must know all the policy classes and decide which one to use. This means that the client must understand the differences between these algorithms in order to choose the right one at the right time. In other words, the policy pattern only works if the client knows all the algorithms or behaviors.
  • The policy pattern will cause the system to produce many concrete policy classes, and any small change will cause the system to add a new concrete policy class.
  • Multiple policy classes cannot be used on the client at the same time. That is, the client can use only one policy class at a time when using the policy mode. You cannot use one policy class to complete some functions and then use another policy class to complete other functions.

6.10 Template Method Mode

Template method pattern: A framework that defines an algorithm in an operation, deferring some steps to subclasses. The template method pattern allows subclasses to redefine specific steps of an algorithm without changing the structure of that algorithm.

For example:

The template method pattern is also used a lot in iOS, such as UIViewController lifecycle functions defined in the parent class, which can be overridden by subclasses.

The specific application of template method mode can be divided into three categories:

  • Abstract method: An abstract method is declared by an abstract class and implemented by its concrete subclasses.

  • Concrete method: A concrete method is declared and implemented by an abstract or concrete class whose subclasses can override or inherit directly.

  • Hook methods: A hook method is declared and implemented by an abstract or concrete class, and its subclasses may extend it. An empty implementation is usually given in a parent class as the default implementation of a method, although hook methods can also provide a non-empty default implementation. The hook method implemented in the subclass restrains the execution of the parent class method to realize the reverse control of the subclass to the parent class behavior.

Here is an example of a cooking tutorial given a fixed template that can be dynamically adapted to meet different cooking needs.

(void)cook; (void)cook; (void)cook; // prepareIngredients - (void)prepare; // Add cooking oil - (void)addFat; // addIngredients - (void)addIngredients; // Forester - (void)addFlavouring; // Healthy food - Hook method - (BOOL)isHealthyFood; @end @implementation CookTutorial - (void)cook { [self prepareIngredients]; // If it is healthy food, do not add cooking oilif(! [self isHealthyFood]) { [self addFat]; } [self addIngredients]; [self addFlavouring]; } // Void addFat {NSLog(@);} // Void addFat {NSLog(@)"2. Add mixed oil"); } // Legacy - (void) addflavflavouring {NSLog(@)} // Legacy - (void)addFlavouring {NSLog(@)"4. Add salt"); } // Hook method - (BOOL)isHealthyFood {returnNO; } @end // cook 🐟 - template method subclass @interface CookFish: CookTutorial @end @implementation CookFish // prepareIngredients - (void) prepareprepare_nslog (@)"1. Prepare raw cod"); } // addIngredients - (void)addIngredients {NSLog(@)"3. Add raw cod to the pot."); } // Forester - (void)addFlavouring {[super addFlavouring]; NSLog(@"Step 4 Add black pepper"); } // whether it isHealthyFood (without oil) - (BOOL)isHealthyFood {return YES;
}

@end
Copy the code
CookFish * cookFish = [CookFish new]; [cookFish cook]; Output: 1. Prepare raw cod 3. Add raw cod to pan 4. Add black pepperCopy the code
  • First stepprepareIngredients, there is no concrete implementation as abstract method in the parent class, directly overwrite in the child class.
  • The second step is to add cooking oiladdFatHook methodisHealthyFoodI skipped it.
  • The third stepaddIngredientsIn the parent class is also abstract method, subclass overrides directly.
  • The fourth stepaddFlavouringThere is a concrete implementation in the parent class, and the subclass inherits the “salt” operation from the parent class and adds a new “black pepper” operation.

Advantages:

  • An algorithm is formally defined in the parent class, and its subclasses implement the detailed processing without changing the order of the steps in the algorithm when subclasses implement the detailed processing algorithm.
  • The template method pattern is a code reuse technique, which is particularly important in the design of the library. It extracts the common behavior in the library, puts the common behavior in the parent class, and implements different behavior through its subclasses. It encourages us to use inheritance appropriately to implement code reuse.
  • A reverse control structure can be implemented to determine whether a particular step needs to be performed by subclasses overriding the hook methods of their parent classes.
  • In the template method pattern, the basic method of the parent class can be overridden by subclasses. Different subclasses can provide different implementations of the basic method. It is easy to replace and add new subclasses, in accordance with the single responsibility principle and the open and closed principle.

Disadvantages:

  • It is necessary to provide a subclass for different implementations of each basic method. If there are too many variable basic methods in the parent class, the number of classes will increase, the system will be larger, and the design will be more abstract. In this case, the bridge mode can be combined to design.

6.11 Visitor Mode

Visitor Pattern: Provides a representation of operations on elements in an object structure that allows us to define new operations on those elements without changing the classes of the elements. The visitor pattern is an object behavior pattern.

For example:

The visitor pattern is a complex behavioral design pattern, which consists of two main parts: the visitor and the visited elements. The visited elements usually have different types, and different visitors can perform different operations on them. The visitor pattern allows users to extend the functionality of the system without modifying the existing system, adding new actions to these different types of elements.

When using the visitor pattern, the elements being accessed do not usually exist in isolation, but are stored in a collection called an “object structure,” through which the visitor performs each operation on the stored elements. Look at the visitor pattern in a simple example. The visitor has a finance department, FADepartment, and an HR department, and accesses Employee to see what the Employee is doing.

@interface Department: NSObject - (void)visitEmployee:(Employee *) Employee; @implementation Department - (void)visitEmployee:(Employee *) Employee {} @end - (void)visitEmployee:(Employee *) Employee {if (employee.workTime > 40) {
        NSLog(@"%@ working time over 40 hours", employee.name);
    }else{
        NSLog(@"%@ work less than 40 hours, warning!", employee.name); }} @end // HR department - @interface HRDepartment: - (void)visitEmployee:(Employee *) Employee {NSUInteger weekSalary = employee.workTime * employee.salary; NSLog(@"%@ get paid this week: %ld",employee.name , weekSalary); } @interface Employee: NSObject @property (nonatomic, strong) NSString *name; @property (nonatomic, assign) NSUInteger workTime; // @property (nonatomic, assign) NSUInteger salary; - (void)accept:(Department *) Department; @end @implementation Employee - (void)accept:(Department *) Department {} @end FulltimeEmployee : Employee @end@implementation FulltimeEmployee - (void)accept:(Department *) Department {[Department visitEmployee:self]; } @endCopy the code
FADepartment *fa = [FADepartment new]; HRDepartment *hr = [HRDepartment new]; FulltimeEmployee * Tim = [FulltimeEmployee new]; tim.name = @"tim";
tim.workTime = 55;
tim.salary = 100;

FulltimeEmployee *bill = [FulltimeEmployee new];
bill.name = @"bill"; bill.workTime = 38; bill.salary = 150; NSArray *employeeList = @[Tim, bill]; NSArray *employeeList = @[Tim, bill]; [employeeList enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {Employee * Employee = obj; Employee accept: FA; Employee accept: HR;}]; Output: Tim works more than 40 hours Tim gets salary this week: 5500 Bill works less than 40 hours, please warn! Bill gets his salary this week: 5700Copy the code

Advantages:

  • Adding new access operations is convenient. With the visitor pattern, adding a new access operation means adding a new concrete visitor class, which is easy to implement without modifying the source code and conforms to the “open closed principle”.
  • Centralize the access behavior of the element object into a visitor object rather than scattering it across element classes. The responsibility of the class is clearer, which facilitates the reuse of element objects in the object structure. The same object structure can be accessed by multiple different visitors.
  • Enables users to define operations that operate on an existing element class hierarchy without modifying that hierarchy.

Disadvantages:

  • Adding new element classes is difficult. In the visitor pattern, adding a new element class means adding a new abstract operation in the abstract visitor role and a corresponding concrete operation in each concrete visitor class, which violates the “open closed principle.”
  • Break encapsulation. The visitor pattern requires the visitor object to access and invoke the operations of each element object, which means that the element object must sometimes expose some of its own internal operations and internal state, otherwise it cannot be accessed by the visitor.

conclusion

After studying design patterns systematically, you can see from your past development experience that design patterns are ubiquitous. Before learning the design pattern, we often rely on past experience and wisdom to improve the design of the system, and many of these experiences happen to coincide with the idea of a design pattern.

There are still some places that are not fully understood, and I hope to point out the mistakes in the article.

reference

  • Juejin. Cn/user / 356207…
  • The design – patterns. Readthedocs. IO/zh_CN/lates…
  • Blog.csdn.net/lovelion/ar…
  • Github.com/skyming/Tri…