I’ve always wanted to write a series of articles on design patterns, but I’ve always felt I didn’t understand enough to get started. Of course, writing now does not mean that I have a thorough understanding, but I just want to record some key points of the book and my own understanding, so that I can repeat them, and also hope to discuss with other developers. This series of articles is based on The Tao of Objective-C Programming for those who want to read it more closely. The book is well written, but it is quite old, using MRC, and some of the writing is quite old, but the design pattern part is quite easy to understand.

I. What are design patterns?

A design pattern is a customized solution to a problem in a particular scenario. Design pattern is an abstraction of the main aspects of a specific object-oriented design problem, which embodies the important idea of object-oriented design. When design patterns are used in programming, they are easier to reuse and extend in the future, and easier to change.

Architectural patterns are often confused with design patterns. MVC, for example, is an architectural pattern. In MVC, objects in an application are divided into three groups that act as models, views, and controllers. It determines which of the three groups an object or class should belong to. It contains several basic design patterns. Composition (view hierarchy), command (target-action mechanism), mediator (controller), policy (controller), observer (notification).

2. Problems affecting design

There are six principles that influence design patterns, and these principles are rules of thumb for building reusable, maintainable object-oriented applications. I am not going to go into details here. There are many principles (or ideas) that we have been silently observing, such as the principle of single responsibility and the principle of open and closed. If you want to know more about them, you can read the six principles of design pattern in this article.

Here I mainly discuss two issues (or principles) :

  • ① Program for interfaces rather than implementations
  • ② Use object composition in preference to class inheritance

① Program for interfaces rather than implementations

There are two ways to program for interfaces in OC: 1) Abstract base classes. An abstract base class can generate default behavior that other subclasses can share. An abstract base class is like a normal class, except that it reserves some behavior that can or should be overridden by subclasses. 2) Agreement. Protocols are simply “interfaces” that define abstract behavior, and classes that implement protocols define implementations of these methods.

Implement protocols or inherit from abstract classes so that objects share the same interface. All objects of a subtype can respond to requests from an interface of a protocol or abstract class. This has two advantages: 1) Clients don’t need to care about the exact type of object they use as long as the object conforms to the interface required by the client. 2) The client only knows the protocol or abstract class that defines the interface, so the client knows nothing about the class of the object.

By encapsulating and isolating changing parts of a program by defining interfaces, those parts change independently of the rest of the program because they don’t depend on any details. These mutable parts can then be changed or extended without affecting the rest of the program. The program will therefore be able to change more flexibly and reliably, as we eliminate dependencies between parts and reduce coupling. Therefore, when considering the design pattern, it is necessary to make clear the change point of the design pattern, rather than just considering the scene, implementation mode and coupling relationship between classes and objects of the design pattern, so as not to lead to the abuse of the design pattern, so that the change does not change, and should not change often.

Of course, the choice of protocol or abstract base class should be based on the actual situation. In the case of multiple inheritance (OC does not support multiple inheritance), we can define an abstract base class for a class that does not need to subclass other classes. We can then define a namesake protocol that other classes, including this abstract base class, can implement. (Example: The NSObject base class also complies with the NSObject protocol).

② Use object composition in preference to class inheritance

When extending functionality, we have two options: object composition and class inheritance. Class inheritance (subclassing) allows us to use other classes to define the implementation of a class. Subclassing is often referred to as white-box reuse because the internal description and details of the parent class are usually visible to subclasses. Object composition can replace class inheritance. Object composition requires that the objects being composed have well-defined interfaces that are defined dynamically at run time through references from other objects. So you can combine objects onto other objects to build more complex functionality. Because the internal details of an object are not visible to other objects, this type of reuse is called black box reuse.

Advantages of class inheritance: Class inheritance is simple and straightforward, because relationships are defined statically at compile time and reused implementations are easy to modify. Disadvantages: Too much coupling with subclasses, and any changes implemented by the parent class will force the subclass to make changes. Subclasses face the details of their parent’s implementation directly, thus breaking encapsulation.

Object composition advantage: Encapsulation is not broken because objects are accessed only through interfaces. Implementation dependencies are greatly reduced because the implementation of an object is defined through an interface. It helps to keep the encapsulation of classes to focus on a single task, and classes and their hierarchies clean and tidy, not bloated. Disadvantages: Design involves more objects, and the behavior of the system is related to different objects, rather than defined in a single class.

Therefore, class inheritance should not be abused. For discussion of class inheritance, see casA’s series of articles, which jump out of object-oriented thinking (A) inheritance. The preference for object composition over class inheritance is not to say that class inheritance cannot be used; you need to make clear judgments about how to reuse classes and objects on a case-by-case basis. If the system is well designed, class inheritance and object composition can work together. When designing a class, prioritize object combinations, then look for redundant behavior and refine the design (if finding redundant behavior means using class inheritance here).

Ii. Introduction of design patterns

In The Tao of Objective-C Programming, design patterns are divided into eight types. We need to choose design patterns based on specific scenarios and requirements. For example, when you need to create objects, instead of just randomly creating New objects, consider coupling, extensibility, usage scenarios, and so on to choose the appropriate design pattern.



IOS Design Pattern — Object creation



IOS Design Pattern — Interface adaptation

#### iOS design Pattern to be updated ─ object decoupling iOS design pattern ─ Interface adaptation iOS design pattern ─ Abstract Collection iOS design pattern ─ Behavior extension iOS design pattern ─ algorithm encapsulation iOS design pattern ─ performance and object access iOS design pattern ─ Object status