Current location - Quotes Website - Signature design - How Cocoa applies design patterns
How Cocoa applies design patterns

Cocoa often bases its distinctive working mechanisms on patterns, and its design is influenced by factors such as language capabilities or existing architecture. This section contains an introduction to most of the design patterns cataloged in the book Design Patterns: Elements of Reusable Object-Oriented Software. Each design pattern has a summary description and a discussion of the Cocoa implementation of the pattern. Listed in this article are the patterns implemented by Cocoa, and the discussion of each pattern occurs in a specific Cocoa environment. We recommend that you become familiar with these patterns, you will find them very useful in Cocoa software development. The implementation of design patterns in Cocoa comes in different forms. Some of the designs described in the following sections—such as protocols and categories—are features of the Objective-C language; in other cases, an "instance of a pattern" is implemented as a class or a group of related classes (such as class clusters and singletons). class); in other cases, the pattern appears as a large framework structure, such as the responder chain pattern. Some pattern-based mechanics are virtually "free" for you to use; others require some work on your part. Even for patterns that Cocoa does not implement, we encourage you to implement them yourself if conditions permit. For example, when extending the behavior of a class, object composition (decoration mode) technology is usually better than generating subclasses. There are two design patterns not discussed below, namely the Model-View-Controller (MVC) pattern and object modeling. MVC is a composite or aggregation pattern, which means it is based on several different types of patterns. Object modeling has no counterpart in the Quad's taxonomy and originates from the world of relational databases. However, MVC and object modeling are probably the most important and ubiquitous design patterns or terms in Cocoa, and they are largely related. They play a key role in the design of several technologies, including binding, undo management, script control, and document architecture. To learn more about these patterns, see the "Model-View-Controller Design Patterns" and "Object Modeling" sections. This part contains the following main contents: Abstract Factory Pattern Adapter Pattern Chain of Responsibility Pattern Command Pattern Composite Pattern Decoration Pattern Appearance Pattern Reducer Pattern Arbitrator Pattern Memo Pattern Observer Pattern Agent Pattern Singleton Pattern Template Method Pattern Abstract Factory Pattern provides an interface. , used to create a family of classes that are related to or dependent on certain objects without specifying their concrete classes. This pattern decouples the client code from the concrete object details from the factory. Class cluster A class cluster is an architecture that groups together some private concrete subclasses under a public abstract superclass. The abstract superclass is responsible for declaring methods to create private subclass instances, and assigns appropriate concrete subclasses based on the method being called. Each returned object may belong to a different private subclass. Cocoa limits class clustering to object generation where data storage may vary depending on the environment. The Foundation framework defines class families for NSString, NSData, NSDictionary, NSSet, and NSArray objects. Public superclasses include the above-mentioned immutable classes and their complementary mutable classes NSMutableString, NSMutableData, NSMutableDictionary, NSMutableSet, and NSMutableArray. Usage and Limitations When you want to create mutable or immutable objects of the type represented by a class cluster, you can do so by using one of the public classes in the class cluster. Using class clusters is a compromise between simplicity and extensibility. Class clusters simplify a class interface, making it easier to learn and use, but make it more difficult to create custom subclasses of the clustered abstract class. Further reading: The "Class Clusters" section provides more information about Cocoa class clusters. The Adapter pattern converts a class interface into another interface required by client code. Adapters enable classes that cannot work together due to compatibility to work together, eliminating the coupling between client code and the target object's classes. Protocols A protocol is a programming language level (Objective-C) feature that makes it possible to define instances of the Adapter pattern ("interface" and "protocol" are synonymous in Java). If you want one client object to communicate with another object, but have difficulty because their interfaces are incompatible, you can define a protocol, which is essentially a series of method declarations that are not associated with a class. In this way, other object classes can formally adopt the protocol and "follow" the protocol by implementing all methods in the protocol. As a result, client objects can send messages to other objects through the protocol interface. A protocol is a set of method declarations that are independent of the class hierarchy, making it possible to group objects according to the protocols they follow, just like class inheritance. You can confirm the protocol relationship of an object through NSObject's conformsToProtocol: method. In addition to formal agreements, Cocoa also has the concept of informal agreements. This type of protocol is a category within the NSObject class, making all objects potential implementers of the category methods (see the "Categories" section).

The informal agreement approach can be optionally implemented. An informal protocol is part of the implementation of the delegation mechanism (see the "Delegates" section. Note that the design of the protocol does not exactly match the Adapter pattern. But it is a means of allowing classes with incompatible interfaces to work together. Usage and Restrictions of the Protocol It is mainly used to declare interfaces that hierarchically unrelated classes need to follow in order to communicate with each other. However, you can also use protocols to declare the interface of an object and hide the corresponding class. The Cocoa framework includes a number of formal protocols. Custom subclasses can communicate with the framework for specific purposes. For example, the Foundation framework includes the NSObject, NSCopying, and NSCoding protocols, and the protocols in the Application Kit include NSDraggingInfo, NSTextInput, and NSChangeSpelling. Formal protocol compliance. Implement all methods declared by a protocol. They are also fragmented, and once you define a protocol and provide it to other classes, future modifications to the protocol will break those classes. Further reading: See Objective for more information on formal protocols. - Discussion in the "Extended Classes" section of the C Programming Language Documentation The Chain of Responsibility pattern avoids coupling the sender and receiver of a request by chaining the receiving objects together and routing the request along. It is passed along the receiver chain until an object either handles the request or passes it to the next object in the chain. The Application Kit framework includes a structure called the responder chain. . The chain consists of a series of responder objects (that is, objects inherited from NSResponder). Events (such as mouse clicks) or action messages are passed along the chain and (usually) are eventually processed by the given responder. The object does not process a specific message, but passes the message to the next responder in the chain. The order of responders in the chain is usually determined by the hierarchy of the view, from lower-level responder objects to higher-level objects. Passing, the vertex is the window object that manages the view hierarchy, the window object's delegate object, or the global application object. The exact path that event and action messages take in the responder chain varies from one application to another. A responder chain may have as many windows (or even view objects in the local hierarchy) as it owns, but only one responder chain is active at a time—the one associated with the currently active window. There is a chain similar to the responder chain for application error handling. The view hierarchy is designed using the composition pattern (see the "Composite Pattern" section), which is closely related to the responder chain - derived from action messages. Messages for control objects—based on the target-action mechanism and are an instance of the Command pattern (see the "Command Pattern" section). Uses and limitations Available "for free" when you construct a user interface for a program through Interface Builder or programmatically. Get one or more responder chains. The responder chain goes together with the view hierarchy, which is automatically generated when you make a view object a subview of the window's content view. If you add a custom view to a view hierarchy, it becomes part of the responder chain; if you implement the appropriate NSResponder methods, you can receive and handle event and action messages. The custom object is a delegate object of the window object or the global application object NSApp and can also receive and process those messages. You can also programmatically inject custom responder objects into the responder chain, and programmatically manipulate the order of responders in the chain. Further reading: The responder chain for handling event and action messages and program errors is described in the Cocoa Event Handling Guide and Cocoa Error Handling Programming Guide documents. This document provides a summary of view hierarchies in the "Composite Patterns" section and a more complete description in the "Core Application Architecture" section. Command pattern This pattern encapsulates requests as objects, allowing you to parameterize client code with different requests; queues and logs requests, and supports undoable operations. The request object binds one or more actions to a specific recipient. The command pattern distinguishes between the object that makes the request and the object that receives and executes the request. Instances of the calling object NSInvocation class are used to encapsulate Objective-C messages. A call object contains a target object, a method selector, and method parameters. You can dynamically change the target of the message and its parameters in the calling object, and once the message is executed, you can get the return value from the object. Messages with different targets or parameters can be called multiple times through a calling object. Creating an NSInvocation object requires the use of an NSMethodSignature object, which is responsible for encapsulating information related to method parameters and return values. The creation of NSMethodSignature objects requires the use of a method selector. The implementation of NSInvocation also uses some functions of the Objective-C runtime environment.

Usage and Restrictions NSInvocation objects are part of the distributed, undo management, messaging, and timer object programming interfaces. You can also use it in similar situations where you need to remove the coupling relationship between the message sending object and the receiving object. Distributed objects are an inter-process communication technology, see the "Agent Pattern" section for more information on this topic. Further reading: Please read the NSInvocation class reference documentation for details on the invocation object. You can also get information about related technologies from: Distributed Objects, Undo Architecture, Timers, and Beyond in the Objective-C Programming Language Documentation. Target-Action The target-action mechanism enables a control object—that is, an object like a button or text input box—to send a message to another object that can interpret the message and process it into application-specific instructions. The receiving object, or target, is usually a custom controller object. Messages—also called action messages—are identified by a selector, which is a unique runtime identifier for a method. Typically, the unit object owned by a control encapsulates a target and action to send a message when the user clicks or activates the control (menu items also encapsulate a target and action to send an action message when the user selects it). The reason why the target-action mechanism can be based on selectors (rather than method signatures) is because Cocoa stipulates that the signature of the action method and the name of the selector are always the same. Usage and Limitations When you use Interface Builder to build your program's user interface, you can set the controls' actions and targets. You can therefore customize the behavior of the control without having to write any code for the control itself. Action selectors and target connections are archived in the nib file and are resurrected when the nib file is unarchived. You can also dynamically change targets and actions by sending setTarget: and setAction: messages to the control or its unit object. The target-action mechanism is often used to notify custom controller objects to pass data from the user interface to model objects, or to display data from model objects. Cocoa binding technology can avoid this usage. For more information about this technology, please see the Cocoa Binding Programming Topic Documentation. Controls and units in Application Kit do not maintain their target. Instead, they maintain a weak reference to the target. See the "Ownership of Delegates, Observers, and Targets" section for further information. Further reading: See the "Target-Action Mechanics" section for more information. Composite mode This mode combines interrelated objects into a tree structure to represent part-to-full hierarchies. It enables client code to uniformly handle individual objects and the composite results of multiple objects. Composite objects are part of the Model-View-Controller aggregation pattern. View Hierarchy The view objects (NSView objects) contained in a window internally form a view hierarchy. The root of the hierarchy is the window object (NSWindow object) and its content views. The content view is a transparent view filled in the content box of the window. The views added to the content view are all its subviews, and these subviews will become the parent views of the next-level views. A view has one (and only one) parent view and can have zero or more child views. Visually, you can understand this structure as a containment relationship: a parent view contains its child views. Figure 4-2 shows the structure of the view hierarchy and its visual relationships. Figure 4-2 The structure of the view hierarchy and its visual relationship The view hierarchy is a structural architecture that plays a certain role in graphics rendering and event processing. A view has two bounding boxes that affect the position of graphics operations, namely frame and bound. The border is the outer boundary and represents the position of the view in the coordinate system of its parent view. It is responsible for defining the size of the view and cropping the graphics according to the view boundary. The bounds is the internal bounding box that defines the internal coordinate system of the view object's own drawing surface. When the window system asks a window to be ready for display, the parent view is asked to render before its child views. When you send a message to a view—such as a message to redraw the view—the message is propagated to subviews. Therefore, you can treat a branch in the view hierarchy as a unified view. The responder chain also uses the view hierarchy to handle event and action messages. See the Chain of Responsibility Pattern section ("Chain of Responsibility Pattern") for a summary description of the responder chain. Usage and Limitations Whether programmatically or through Interface Builder, you create or modify a view hierarchy when you join one view to another. The Application Kit framework automatically handles all relationships associated with view hierarchies. Further reading: If you need more information about view hierarchies, read the "View Hierarchies" section of this document. View hierarchy is also discussed in the Cocoa Drawing Guidelines document. Decoration Pattern This pattern dynamically attaches additional responsibilities to an object. Decoration is a flexible alternative to subclassing when extending functionality. Like subclassing, adopting the decoration pattern allows you to add new behavior without modifying existing code.

Decoration wraps an object of a class that needs to be extended, implements the same interface as the object, and adds its own behavior before or after passing the task to the wrapped object. The Decoration pattern expresses the design principle that classes should accept extensions but avoid modifications. General Description Decoration is a pattern for object composition. Composition of objects should be encouraged in your own code (see the "When subclassing is necessary" section). However, Cocoa itself provides some classes and mechanisms based on this pattern (discussed in the following section). In these implementations, the extension object does not completely copy the interface of the object it wraps, although different techniques can be used in specific implementations for interface sharing. Cocoa uses the decoration mode when implementing certain classes, including NSAttributedString, NSScrollView, and NSTableView. The latter two classes are examples of composite views, which combine objects from other view classes and then coordinate the interactions between them. Delegation Delegation embeds a weak reference (an unmaintained socket variable) pointing to another object (that is, the delegate object) in the host object, and sends messages to the delegate object from time to time to enable it to input related tasks. mechanism. The host object is generally a "resurrected" framework object (such as an NSWindow or NSXMLParser object) that seeks to complete a certain job, but can only do it in a general way. A delegate is almost always an instance of a custom class that is responsible for working with the host object to provide program-specific behavior at specific points in the task (see Figure 4-3). In this way, the delegation mechanism allows us to modify or extend the behavior of another object without the need to generate a subclass. Figure 4-3 A framework object sends a message to its delegate object. In short, delegation is when one object delegates a task to another object. It is a common technique in object-oriented programming. However, Cocoa implements the delegation mechanism in a unique way. The host class uses informal protocols—categories in NSObject—to define the interfaces that the delegate object may choose to implement. A delegate object does not have to implement all protocol methods as if it were adopting a formal protocol. Before sending a message to the delegate object, the host object can first determine whether the corresponding method is implemented (via the respondsToSelector: message) to avoid runtime exceptions. For more information about formal and informal agreements, see the "Agreements" section. Some classes in the Cocoa framework also send messages to their data sources. A data source is like a delegate in every respect, except that its purpose is to provide data to the host object for delivery to the browser, table view, or similar user interface view. Unlike delegates, data sources must also implement certain protocol methods. Delegates are not a strict implementation of the decorator pattern. The host (delegate) object does not wrap an instance of the class it wishes to extend. Instead, delegation is a specialization of the behavior of a delegated framework class. In addition to the delegate methods declared by the framework class, they also have no public interface. Delegates in Cocoa are also part of the Template Method pattern ("Template Method Pattern"). Using and limiting delegates is a common design in the Cocoa framework. Many classes in the Application Kit framework send messages to their delegates, including NSApplication, NSWindow, and several subclasses of NSView. Some classes in the Foundation framework, such as NSXMLParser and NSStream, also maintain their own delegates. You should always use a class's delegation mechanism rather than subclassing a class, unless the delegation method does not accomplish your goals. Although you can dynamically change delegates, only one object can be a delegate at a time. Therefore, if you want multiple objects to be notified simultaneously when a specific program event occurs, you cannot use delegates. In this case you can use the notification mechanism. If the delegate object implements one or more notification methods declared by the framework class, it will automatically receive notifications from its delegate framework object. See the discussion of notifications in the Observer Pattern ("Observer Pattern"). Objects in the Application Kit framework that delegate tasks do not retain their delegate or data source, but maintain a weak reference. For more information, see the "Ownership of Delegates, Observers, and Targets" section. Further reading: See the "Delegates and Data Sources" section for further information about delegates. Categories Categories are a feature of the Objective-C language that are used to add methods (interfaces and implementations) to a class without having to generate subclasses. There is no difference at runtime between methods originally declared by a class and methods added through categories—within the scope of your program. Methods in the category become part of the class type and are inherited by all subclasses. Like delegates, categories do not strictly fit decorator patterns. It achieves the purpose of the pattern, but in a different way. The behavior of category joining is generated at compile time, not obtained dynamically. Furthermore, the category does not encapsulate instances of the extended class. Usage and Limitations There are many categories defined in the Cocoa framework, most of which are informal agreements (summarized in the "Protocols" section). They often use categories to group related methods.

You can also implement categories in your code to extend a class without subclassing, or to group related methods. But you need to note the following two points: You cannot add instance variables to a class. If you overload an existing method, your application may behave unexpectedly. Further reading: For more information about categories, see the "Extensions to Classes" section of the Objective-C Programming Language article. Apparent pattern This pattern provides a unified interface to a set of interfaces in a subsystem. The apparent pattern defines a higher-level interface that makes subsystems easier to use by reducing complexity and hiding communication and dependencies between subsystems. NSImage The NSImage class provides a unified interface for loading and using images based on bitmaps (such as JPEG, PNG, or TIFF formats) or vectors (EPS or PDF formats). NSImage can maintain multiple representations of the same image, and different representations correspond to different types of NSImageRep objects. NSImage can automatically select a representation appropriate for a particular data type and display device. At the same time, it hides the details of image manipulation and selection, allowing client code to use many different representations interchangeably. Usage and Limitations Because NSImage supports several different image representations, some properties may not apply. For example, you might not be able to get the color of a pixel in an image if the underlying image representation is vector-based and device-independent. Please note: See the Cocoa Drawing Guide for a discussion of NSImage and image representation. Iterator Pattern This pattern provides a way to sequentially access the elements of an aggregate object (that is, a collection) without exposing the underlying representation. The Iterator pattern transfers the responsibility of accessing and traversing collection elements from the collection object to the iterator object. Iterators define an interface for accessing collection elements and keep track of the current element. Different iterators can implement different traversal strategies. The NSEnumerator class in the NSEnumeratorFoundation framework implements the iterator pattern. Private concrete subclasses of the NSEnumerator abstract class return enumerator objects that can sequentially iterate over collections of different types—arrays, sets, dictionaries (values ??and keys)—and return the objects in the collection to client code. NSDirectoryEnumerator is a loosely related class whose instances can recursively enumerate the contents of directories in a file system. Usage and Limitations Collection classes such as NSArray, NSSet, and NSDictionary contain methods that return enumerators appropriate to the type of collection. All enumerators work the same way. You can send a nextObject message to the enumerator in a loop and exit the loop if the message returns nil instead of the next object in the collection. The Arbiter Pattern defines an object that encapsulates the interaction mechanism of a group of objects. The Arbiter pattern avoids explicit mutual references between objects, loosens the coupling between objects, and allows you to independently change the way they interact. These objects can therefore be more reusable. Arbitrator objects centralize complex communication and control logic between objects in the system. These objects tell the arbiter object when the state changes and, in turn, respond to the arbiter object's requests. The Controller Class Model-View-Controller design pattern assigns different roles to objects in an object-oriented system (such as an application). They can be model objects, which contain the application's data and operate on that data; they can be view objects, which are responsible for representing data and responding to user actions; or they can be controller objects, which are responsible for coordinating model and view objects. Controller objects fit into the Arbiter pattern. In Cocoa, there are generally two types of controller objects: arbitration controllers or coordination controllers. The arbitration controller is responsible for mediating the flow of data between view objects and model objects in the application. The arbitration controller is usually an NSController object. The coordination controller is responsible for implementing the application's centralized communication and control logic, serving as the delegate for framework objects and the target of action messages. They are usually instances of NSWindowController objects or custom NSObject subclasses. Since the coordination controller is highly specialized for a specific program, reuse is not considered. The NSController abstract class and its concrete subclasses in the Application Kit framework are part of Cocoa binding technology, which can automatically synchronize the data contained in the model object and the data displayed and edited in the view object. For example, if the user edits a string in a text box, the binding technology will propagate the changes in the text box (through the arbitration controller) to the appropriate properties in the bound model object. What programmers need to do is to design their own model objects correctly and establish binding relationships between the program's views, controllers, and model objects through Interface Builder. Instances of specific public controller classes are available on the Control Palette of Interface Builder, making them highly reusable. They provide services such as selection and management of placeholder values.

These objects perform the following specific functions: NSObjectController manages a single model object. NSArrayController manages an array of model objects and maintains a selection; it can also add or delete model objects from the array. NSTreeController enables you to add, delete, and manage model objects in a hierarchical tree structure. NSUserDefaultsController provides a convenient interface to the provisioning (user defaults) system. Usage and Limitations You can typically use NSController objects as arbitration controllers because these objects are designed to pass data between your application's view objects and model objects. When using a arbitration controller, you usually drag the object from the Interface Builder palette, specify the property keys of the model object, and establish the binding relationship between the view and the model object through the Bindings panel in the Interface Builder Info window. You can also subclass NSController or its subclasses for more specific behavior. A binding relationship can be established between almost any pair of objects as long as they follow the two informal protocols NSKeyValueCoding and NSKeyValueObserving. However, we recommend that you establish bindings through a quorum controller to take advantage of the benefits that NSController and its subclasses provide you. Coordinating controllers centralize an application's communication and control logic by maintaining socket variables that point to model and view objects (socket variables are connections or references to other objects that are held as instance variables). Respond to user operations on the view object through the goal-action mechanism (see the "Goal-Action" section). As a delegate object, handles messages sent from the framework object (see the "Delegates" section). You typically make the connections described above—outlet variables, target-actions, and delegates—in Interface Builder, which archives these connections in your application's nib file. Further Reading: See the "Model-View-Controller Design Pattern" section for a discussion of arbitrating controllers, coordinating controllers, and controller-related design decisions. Memento Pattern This pattern captures and externalizes the internal state of an object so that the object can later be restored to that state without breaking encapsulation. The memo pattern externalizes the important state of key objects while maintaining the cohesion of the objects. Archives Archives store objects in a program and their attributes (including attributes and relationships) into archives so that they can be saved to the file system or transferred between different processors and networks. An archive saves a program's object graph as an architecture-independent byte stream, and object identities and relationships between objects are preserved. Since the object's type is stored along with its data, the object decoded from the archived byte stream is instantiated normally, using the same class as the originally encoded class. Usage and Limitations Typically, you want to archive objects in your program that require saved state. Model objects almost always fall into this category. You write objects to the archive by encoding, and you read objects from the archive by decoding. Encoding and decoding operations can be performed through the NSCoder object. It is best to use keyed archiving technology during the encoding and decoding process (you need to call the methods of the NSKeyedArchiver and NSKeyedUnarchiver classes). The object being encoded and decoded must comply with the NSCoding protocol; the methods of this protocol will be called during the archiving process. Further reading: Further information about archiving. Serialization of Property Lists A property list is a simple, structured sequence of object graphs that uses only objects of the following classes: NSDictionary, NSArray, NSString, NSData, NSDate, and NSNumber. These objects are also often called property list objects. There are several framework classes in Cocoa that provide methods for serializing property list objects and defining special data stream formats for recording object contents and their hierarchical relationships. The NSPropertyListSerialization class provides class methods for serializing property list objects into XML or other optimized binary formats. Usage and Limitations If your object graph contains simple objects, property list serialization is a flexible, portable, and appropriate tool for capturing and externalizing objects and their state. However, this form of serialization has its limitations. It does not retain the entire class identity of the object, but only some general types (arrays, dictionaries, strings, etc.). In this way, the object restored from the attribute list may be different from the original class, which can cause problems especially when the mutability of the object may change. Property list serialization also does not track objects that are referenced multiple times in the same object, which may result in multiple instances when deserializing, but only one instance in the original object graph. Further reading: More information about property list serialization. Core DataCore Data is a framework and architecture for managing object graphs and persisting them.

It is this second ability—the ability to persist objects—that makes Core Data an adaptation of the memo pattern. In the Core Data architecture, the central object is called the managed object context, which is responsible for managing the model objects in the application object graph. Below the managed object context is the persistence stack of the object graph, which is a collection of framework objects responsible for coordinating model objects with external data stores, such as XML files or relational databases.

The persistent stack object is responsible for establishing the mapping relationship between the data in the storage and the objects in the managed object context. When there are multiple data stores, the persistent stack object represents these stores as an aggregate storage in the managed object context < /p>