Design Patterns: Behavioral and Cocoa-Specific
Work through Objective-C examples of the behavioral design patterns — Strategy, Observer, Command, State, Template Method, and Chain of Responsibility — and the idiomatic Cocoa patterns woven throughout the language's own practice — Coordinator, Delegate, and Target-Action — and finish with a clear historical-versus-modern verdict on each.
The previous lesson covered the creational and structural patterns — Factory, Abstract Factory, Builder, Singleton, Adapter, Decorator, Facade, and Proxy — patterns primarily concerned with how objects get built and how they're shaped and wrapped. This lesson turns to a different question: not how objects come to exist, but how they behave and communicate once they do. The behavioral patterns — Strategy, Observer, Command, State, Template Method, and Chain of Responsibility — describe recurring shapes for distributing responsibility and passing messages between objects. But Objective-C's story here is unusual among mainstream languages, because three of its most important behavioral-style patterns are not imports from a textbook at all — they are the language's own native idiom. Delegate and Target-Action are patterns Cocoa invented and has used since before the 'design patterns' vocabulary was popularized in software at large, and Coordinator is a more recent pattern the Objective-C and Swift community converged on to solve a problem the platform's own conventions left open. This lesson covers all nine, cross-references the dedicated lessons several of them already received earlier in this course, and ends by sorting all nine into what remains current advice today and what is now more historical than practical.
Strategy: swapping an algorithm behind a common interface
The Strategy pattern encapsulates an interchangeable piece of behavior — an algorithm, a policy, a rule — behind a common protocol, so that the object using that behavior doesn't need to know or care which concrete implementation it's holding. The object holding the strategy just calls the protocol's method; which concrete class actually runs is decided elsewhere, and can change at runtime without the caller's code changing at all. This is the direct antidote to a long if/else or switch statement scattered with 'if type is A do this, if type is B do that' — instead, each 'this' and 'that' becomes its own class conforming to one protocol, and the caller picks one and forgets about the distinction.
In Objective-C, Strategy is usually just 'a property typed as a protocol.' A view controller that needs to validate form input can hold an id<InputValidator> property; a NumericInputValidator, an EmailInputValidator, and a NonEmptyInputValidator can each conform to that protocol with their own isValid: implementation, and the view controller calls [self.validator isValid:text] without a single conditional branching on which kind of validator it has. Swapping validators — say, because the user switched from an email field to a phone-number field — is just assigning a different concrete object to that same property.
This should look familiar: it is structurally the same shape as the protocol-typed dependency this course used throughout the Mocking and Dependency Injection lesson to make classes testable. A class that depends on id<NetworkFetching> rather than a concrete NSURLSession-backed class is applying the Strategy pattern to its networking behavior — the 'real' fetcher and a test double are just two interchangeable strategies conforming to the same protocol, and the class using one doesn't know or care which one it was handed. Strategy and dependency injection are, in Objective-C, close enough to being the same technique described from two different angles: one names the design goal (swappable behavior), the other names the delivery mechanism (handed in from outside rather than hardcoded).
Observer: the general pattern behind KVO and NotificationCenter
The Observer pattern describes a relationship where one or more objects (observers) register interest in another object's (the subject's) state, and are automatically notified whenever that state changes, without the subject needing to know anything concrete about who its observers are or what they'll do with the notification. The subject just announces 'something changed'; interested parties decide for themselves what to do about it. This decoupling — the subject doesn't hold direct references to concrete observer classes, and observers don't need to poll the subject on a timer to check for changes — is the entire value of the pattern.
This course has already taught two separate concrete implementations of exactly this idea, and it's worth being explicit that 'Observer pattern' is the general name for what both of them are: Key-Value Observing (KVO) is Cocoa's built-in mechanism for one object to observe changes to a specific property on another object, firing automatically whenever that property's value changes through KVO-compliant mutation. NotificationCenter is Cocoa's built-in mechanism for a much looser, broadcast-style version of the same idea — any object can post a named notification, and any number of unrelated observers, with no reference to each other at all, can register to receive it. Both are the Observer pattern; they differ in granularity and coupling (KVO watches one specific property on one specific object; NotificationCenter broadcasts named events to an arbitrary audience) but the underlying shape — subject changes, interested observers get told, subject doesn't know who they are — is identical in both.
Recognizing this matters practically, not just academically: when choosing between KVO and NotificationCenter for a new piece of code, the real question is not 'which pattern should I use' — both already are the Observer pattern — but 'how tightly coupled is this particular observation to one specific object's one specific property,' which is exactly the distinction the earlier KVO and NotificationCenter lessons drew when explaining when to reach for each one.
Command: requests as objects
The Command pattern wraps a request or an action — 'do this thing, with this data, on this receiver' — as a standalone object, rather than as a bare method call. Because the request is now an object rather than a call already in progress, it can be stored, queued, passed around, logged, and — if it's built to support it — undone later by keeping a record of what it did. A text editor's undo stack is the textbook example: every edit is captured as a command object with a do method and an undo method, and the stack of past command objects is what makes multi-level undo possible at all.
Objective-C has real concrete tools that live in exactly this territory. NSInvocation captures a message send — target, selector, and arguments — as an object that can be stored and fired later, which is close to Command in its purest, most literal form: a method call reified as data. The target-action mechanism, covered fully later in this lesson and in its own dedicated lesson, is a lighter, narrower cousin of the same idea — a control doesn't call a method directly, it stores a target object and a selector and sends that message only when the user actually acts.
That said, it's worth being honest about how this pattern is actually used in practice: explicitly hand-rolling a full Command class hierarchy — a CommandProtocol with concrete AddTextCommand, DeleteTextCommand, and so on, each carrying its own undo logic — is less common in typical Objective-C code today than the pattern's prominence in design-patterns literature might suggest. Where genuine multi-level undo is needed, Foundation's own NSUndoManager already does most of this work, registering undo actions via target-selector pairs rather than requiring the app to build its own Command class hierarchy from scratch — so the pattern's spirit shows up constantly, but a literal Command object hierarchy, built by hand, is comparatively rare outside of specialized cases.
State: behavior that depends on an internal mode
The State pattern applies when an object's behavior needs to change based on some internal mode or condition, and models that mode not as a stored enum checked by conditionals scattered through every method, but as a family of separate state objects or classes, each implementing the behavior appropriate to that one state. The object holding the current state delegates to whichever state object is active; transitioning to a new state is just swapping which state object is held, and each state's logic lives in its own class rather than being interleaved with every other state's logic inside one giant switch statement.
This course has already taught a real, concrete example of exactly this pattern living inside Foundation itself: NSOperation's state machine. An NSOperation instance moves through a well-defined sequence of states — isReady, isExecuting, isFinished, isCancelled — and its behavior at any given moment depends entirely on which state it's currently in: a finished operation won't execute again, a cancelled operation that's asked to start should finish immediately without doing its work, and so on. While Foundation doesn't expose this internally as literal separate Objective-C state-object classes the way a from-scratch implementation might, the state machine that NSOperation implements — behavior gated entirely on 'what state am I currently in' — is the State pattern in its purest conceptual form, and it's one every Objective-C developer using GCD-adjacent APIs has already relied on.
As with Command, it's worth distinguishing the pattern's underlying idea, which is everywhere and genuinely useful, from the specific implementation technique of building a literal hierarchy of interchangeable state classes, which is comparatively rare to hand-roll in typical Objective-C application code outside of larger state-machine-heavy subsystems. Most everyday state-dependent behavior in application code is still handled with an enum property and conditional logic; the full State-pattern class hierarchy tends to appear specifically when a type's behavior across states is complex enough, and its state transitions rigorous enough, to justify the extra structure — which is exactly the kind of complexity Foundation's own NSOperation has.
Template Method: a fixed skeleton, customizable steps
The Template Method pattern puts the overall skeleton of an algorithm — its fixed sequence of steps — in a base class method, while deferring one or more of those individual steps to subclasses, which override just the piece that needs to vary. The base class's method calls its own (possibly abstract, possibly default-implemented) step methods in a fixed order; a subclass overrides some of those step methods to customize specific behavior, without needing to reimplement or even fully understand the overall sequence the base class orchestrates.
This is directly the mechanism the earlier Inheritance lesson taught when it covered overriding and calling super: a subclass overrides a specific method to supply its own specialized behavior for one step, often calling super inside that override to still run the base class's default handling for part of that step before or after adding its own work. A base ReportGenerator class might define a fixed generateReport method that calls fetchData, then formatData, then renderOutput in that exact order every time; a CSVReportGenerator subclass overrides only formatData and renderOutput to produce CSV-specific output, while fetchData — inherited unchanged from the base class — stays exactly as the base class wrote it, because that step doesn't vary between report types.
Template Method is one of the most naturally-occurring patterns in ordinary object-oriented Objective-C code, precisely because it needs no special machinery beyond the inheritance and overriding this course already taught early on — any base class that calls its own overridable methods in a fixed sequence is already using it, whether or not anyone consciously named it a design pattern while writing it.
Chain of Responsibility: passing a request along until someone handles it
The Chain of Responsibility pattern arranges a series of potential handler objects in a chain, and passes a request to the first one; each handler either processes the request itself and stops the chain, or declines and forwards the request to the next handler in line, continuing until some handler in the chain actually deals with it (or the chain runs out and the request goes unhandled). The requester that originates the request knows nothing about which specific handler, if any, will end up processing it — it just hands the request to the front of the chain and lets the chain sort out the rest.
Objective-C developers building on UIKit or AppKit have been using a real, concrete, built-in implementation of exactly this pattern for as long as they've been handling events at all: the responder chain. Every UIResponder — a UIView, a UIViewController, a UIWindow, on up to the UIApplication itself — sits in a chain linked by its nextResponder property, and an event or action message that a given responder doesn't handle is automatically forwarded to nextResponder, continuing up the chain until some responder along the way actually handles it or the chain is exhausted. A touch event, a shake-motion event, or an action message sent with a nil target (covered in the Target-Action section below) can all travel up exactly this chain looking for a handler.
This is worth calling out specifically because Chain of Responsibility, as a pattern developers explicitly hand-roll from scratch — building their own custom chain of handler objects for some non-UIKit purpose — is relatively uncommon in typical Objective-C application code. Its real, everyday presence in the ecosystem is almost entirely through the responder chain UIKit and AppKit already provide; most Objective-C developers use Chain of Responsibility constantly without ever writing a nextHandler property themselves, because the platform already built the one chain that matters most for an interactive app.
Coordinator: pulling navigation logic out of view controllers
The Coordinator pattern extracts navigation and flow-control logic — which screen comes next, how a multi-step flow like onboarding or checkout is sequenced, how a modally-presented flow hands control back to whatever presented it — out of view controllers entirely, and into dedicated coordinator objects whose only job is managing that navigation. A view controller stops knowing what screen comes after it; it just reports that something happened (the user tapped 'next,' a form was submitted successfully) and lets a coordinator object decide what to do about it, including which view controller to instantiate and push next.
This is exactly the pattern this course's earlier Architecture lesson previewed when discussing the problem of view controllers accumulating too many responsibilities at once. Left to Cocoa's own defaults, a UIViewController naturally ends up both presenting its own content and deciding what comes after it — pushing the next view controller directly, in response to a button tap, right inside its own action method. That works fine for a small app, but it means navigation logic is smeared across every view controller in the flow, each one hardcoding knowledge of the specific next screen, which makes reordering a flow, reusing a screen in a different flow, or testing navigation logic independently of any actual screen all needlessly difficult.
A Coordinator object breaks that coupling: the view controller reports an event through a delegate or a block, and a coordinator — which owns a reference to the navigation controller and knows the shape of the entire flow — decides what happens next and performs the actual push, present, or pop itself. This is explicitly a modern pattern, not a historical one — it is increasingly recommended in Objective-C and UIKit codebases specifically because it addresses a real, still-current architectural weak point that MVC alone leaves unsolved, and it has only become more common, not less, as apps and their navigation flows have grown more complex over time.
Delegate: Objective-C's own defining pattern
The Delegate pattern lets one object hand off specific decisions or customization points to another object — its delegate — which conforms to an agreed-upon protocol, rather than the first object hardcoding that behavior itself. A UITableView doesn't know how many rows it has or what each row should display; it asks its delegate and data source, through well-defined protocol methods, every time it needs to know. This lesson covers Delegate only briefly by cross-reference, because it already received a full dedicated lesson earlier in this course, but it belongs on this list for one specific reason: unlike every other pattern discussed so far, Delegate is not an idea Objective-C borrowed from general software-engineering literature and happened to apply well. It is one of Cocoa's own, native, defining idioms — arguably the single most characteristic pattern in the entire platform's API design, predating the mainstream popularization of 'design patterns' as a vocabulary, and used more pervasively throughout UIKit, AppKit, and Foundation than perhaps any other single pattern on this list.
That native origin is worth dwelling on because it explains why delegate protocols feel so different from a typical Strategy-pattern protocol: a delegate protocol is usually large, with many optional methods covering many distinct decision points (respondsToSelector: checks and @optional protocol methods, both covered in the dedicated lesson, exist specifically to make that workable), where a Strategy protocol is usually small and tightly focused on one interchangeable behavior. Delegate is about a single, specific relationship between exactly one object and its one customization point; Strategy is about interchangeable, swappable implementations of one general capability. They can look superficially similar — both are 'a protocol-typed property' — but they solve different problems and read differently in real Cocoa code once you know to look for the difference.
Target-Action: Cocoa's other defining UI idiom
The Target-Action pattern is UIKit and AppKit's mechanism for connecting a user interface control — a button, a slider, a switch — to the code that should run when the user interacts with it, by storing a target object and a selector on the control itself, rather than requiring the control to hold a block or know anything concrete about what it triggers. When the user taps the button, the control sends the stored selector to the stored target, and — because a nil target is legal and triggers exactly the Chain of Responsibility traversal covered above — that message can be resolved dynamically by whatever responder up the chain happens to implement it, rather than requiring the control to hold a hard reference to one fixed object at all.
Like Delegate, Target-Action received its own full dedicated lesson earlier in this course and is only cross-referenced here rather than re-taught in depth, but it earns its place on this list of Cocoa-idiomatic patterns for the same reason Delegate does: it is not imported from general design-patterns literature, it is one of the platform's own original mechanisms, present since long before 'design patterns' was a common way for Objective-C developers to talk about their own code, and every UIControl subclass in UIKit is built on top of it. Between Delegate and Target-Action, together, most of the customization surface of a typical Cocoa view and control hierarchy is covered — Delegate for 'ask me before you do this' and 'tell me you did that,' Target-Action for 'run this specific method when the user directly interacts with this specific control.'
Code Examples
FormFieldController never branches on what kind of validator it holds — it calls one protocol method and lets whichever concrete strategy is currently assigned decide. Swapping from email validation to non-empty validation, or to any future validator, is a one-line property assignment with zero changes to FormFieldController itself. This is the same shape used throughout the Mocking/DI lesson: a protocol-typed property lets production code and test doubles both stand in as interchangeable strategies.
return [self.validator isValid:text]; // doesn't know or care which validator
Both blocks implement the Observer pattern: a subject's state change automatically reaches interested parties without the subject holding direct references to them. KVO is the tightly-scoped version (one property, one object); NotificationCenter is the loosely-scoped, broadcast version (any number of anonymous listeners). Choosing between them in new code is a question of coupling and granularity, not a question of which pattern to use -- both already are the Observer pattern.
NSInvocation captures a full message send -- target, selector, and arguments -- as a storable object rather than an in-progress call, which is the Command pattern in close to its most literal Objective-C form: a request reified as data that can be queued and fired later.
Common mistake: Reaching for NSInvocation for routine work is overkill -- it exists for genuinely dynamic dispatch scenarios (undo systems, remote proxies), not as a general substitute for calling a method directly.
Instead of a BOOL isPlaying flag checked by conditionals in every method, behavior lives on separate state objects, and 'transitioning' is just replacing which state object is currently held. This is the same underlying idea Foundation's own NSOperation state machine (isReady/isExecuting/isFinished/isCancelled) already implements internally. In everyday Objective-C code, most state-dependent logic still uses a plain enum and a switch statement -- a full State-pattern class hierarchy like this one is reserved for genuinely complex state machines.
generateReport's fixed sequence never changes; CSVReportGenerator overrides only the two steps that vary, inheriting fetchData completely unchanged -- the same override-and-optionally-call-super mechanism the earlier Inheritance lesson taught.
• (void)generateReport {... } // the template method: fixed order, never overridden sendAction:to:nil:... hands the request to nil, and UIKit walks the responder chain -- starting at the first responder and following nextResponder -- looking for the first object that implements deleteAllNotes:. Neither the view nor the requester needs to know in advance which object will end up handling it. This is a real, built-in Chain of Responsibility -- most Objective-C developers use it constantly through UIKit without ever building a custom handler chain themselves.
OnboardingViewController has zero knowledge of what screen comes next -- it reports 'the user finished onboarding' through a delegate and lets AppCoordinator decide and perform the actual navigation, exactly the separation the Architecture lesson previewed. Reusing OnboardingViewController in a different flow, or reordering the flow entirely, now requires no changes to the view controller itself -- only to the coordinator.
UploadQueue defers several distinct decisions to its delegate through one relationship, using @optional/@required and respondsToSelector: exactly as the dedicated Delegation lesson taught, rather than being one interchangeable, swappable algorithm the way a Strategy protocol is. This is Cocoa's own native idiom, not an import from general design-patterns literature -- UIKit's own data sources and delegates work identically.
logoutButton doesn't hold a block or know anything about SettingsViewController's internals -- it stores a target and a selector and sends that message only when the user actually taps, the same mechanism every UIControl subclass in UIKit is built on.
[logoutButton addTarget:self action:@selector(logoutTapped:) forControlEvents:UIControlEventTouchUpInside];
@protocol InputValidator <NSObject>
- (BOOL)isValid:(NSString *)text;
@end
@interface EmailInputValidator : NSObject <InputValidator>
@end
@implementation EmailInputValidator
- (BOOL)isValid:(NSString *)text {
NSPredicate *p = [NSPredicate predicateWithFormat:@"SELF MATCHES %@",
@"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}"];
return [p evaluateWithObject:text];
}
@end
@interface NonEmptyInputValidator : NSObject <InputValidator>
@end
@implementation NonEmptyInputValidator
- (BOOL)isValid:(NSString *)text {
return text.length > 0;
}
@end
@interface FormFieldController : NSObject
@property (nonatomic, strong) id<InputValidator> validator;
@end
@implementation FormFieldController
- (BOOL)validateText:(NSString *)text {
return [self.validator isValid:text]; // doesn't know or care which validator
}
@end// KVO: narrow, one specific property on one specific object
@interface DownloadTask : NSObject
@property (nonatomic, assign) float progress; // KVO-compliant
@end
[downloadTask addObserver:self
forKeyPath:@"progress"
options:NSKeyValueObservingOptionNew
context:NULL];
// NotificationCenter: broad, any number of unrelated observers
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(sessionDidExpire:)
name:@"UserSessionDidExpireNotification"
object:nil];
// Elsewhere, the subject just announces the change -- it has no idea who's listening
[[NSNotificationCenter defaultCenter]
postNotificationName:@"UserSessionDidExpireNotification" object:self];- (void)scheduleAction {
SEL selector = @selector(applyDiscount:);
NSMethodSignature *sig = [PricingEngine instanceMethodSignatureForSelector:selector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig];
invocation.target = self.pricingEngine;
invocation.selector = selector;
NSDecimalNumber *amount = [NSDecimalNumber decimalNumberWithString:@"5.00"];
[invocation setArgument:&amount atIndex:2]; // 0 = self, 1 = _cmd, 2 = first real arg
[self.pendingCommands addObject:invocation]; // stored, queued, replayable
}
- (void)runPendingCommands {
for (NSInvocation *invocation in self.pendingCommands) {
[invocation invoke]; // the request, fired later, as data rather than a direct call
}
[self.pendingCommands removeAllObjects];
}@protocol PlaybackState <NSObject>
- (void)play:(id)player;
- (void)pause:(id)player;
@end
@interface PlayingState : NSObject <PlaybackState>
@end
@implementation PlayingState
- (void)play:(id)player { /* already playing: no-op */ }
- (void)pause:(id)player { [player setState:[PausedState new]]; }
@end
@interface PausedState : NSObject <PlaybackState>
@end
@implementation PausedState
- (void)play:(id)player { [player setState:[PlayingState new]]; }
- (void)pause:(id)player { /* already paused: no-op */ }
@end
@interface MediaPlayer : NSObject
@property (nonatomic, strong) id<PlaybackState> state;
- (void)pressPlayPause;
@end@interface ReportGenerator : NSObject
- (void)generateReport; // the template method: fixed order, never overridden
- (NSArray *)fetchData; // shared default -- rarely overridden
- (NSString *)formatData:(NSArray *)data; // subclasses must override
- (void)renderOutput:(NSString *)formatted; // subclasses must override
@end
@implementation ReportGenerator
- (void)generateReport {
NSArray *data = [self fetchData];
NSString *formatted = [self formatData:data];
[self renderOutput:formatted];
}
- (NSArray *)fetchData { return [self.dataSource loadRecords]; }
- (NSString *)formatData:(NSArray *)data { return @""; } // must be overridden
- (void)renderOutput:(NSString *)formatted { } // must be overridden
@end
@interface CSVReportGenerator : ReportGenerator
@end
@implementation CSVReportGenerator
- (NSString *)formatData:(NSArray *)data {
return [data componentsJoinedByString:@","];
}
- (void)renderOutput:(NSString *)formatted {
[formatted writeToFile:@"report.csv" atomically:YES
encoding:NSUTF8StringEncoding error:NULL];
}
@end@implementation NotesTextView (Actions)
// This view doesn't implement deleteAllNotes: itself.
// A nil-targeted action lets the system search the responder chain for a handler.
- (IBAction)requestDeleteAll:(id)sender {
[[UIApplication sharedApplication] sendAction:@selector(deleteAllNotes:)
to:nil
from:self
forEvent:nil];
}
@end
@implementation NotesListViewController
// Sits further up the responder chain -- handles it if this view didn't.
- (void)deleteAllNotes:(id)sender {
[self.notes removeAllObjects];
[self.tableView reloadData];
}
@end@protocol OnboardingViewControllerDelegate <NSObject>
- (void)onboardingDidFinish;
@end
@interface OnboardingViewController : UIViewController
@property (nonatomic, weak) id<OnboardingViewControllerDelegate> delegate;
@end
@implementation OnboardingViewController
- (void)userTappedGetStarted {
[self.delegate onboardingDidFinish]; // reports the event -- doesn't navigate itself
}
@end
@interface AppCoordinator : NSObject <OnboardingViewControllerDelegate>
@property (nonatomic, strong) UINavigationController *navigationController;
@end
@implementation AppCoordinator
- (void)start {
OnboardingViewController *onboarding = [OnboardingViewController new];
onboarding.delegate = self;
[self.navigationController pushViewController:onboarding animated:NO];
}
- (void)onboardingDidFinish {
HomeViewController *home = [HomeViewController new];
[self.navigationController setViewControllers:@[home] animated:YES];
}
@end@protocol UploadQueueDelegate <NSObject>
@optional
- (void)uploadQueue:(UploadQueue *)queue willStartUpload:(NSString *)fileID;
- (void)uploadQueue:(UploadQueue *)queue didFailUpload:(NSString *)fileID
error:(NSError *)error;
@required
- (BOOL)uploadQueue:(UploadQueue *)queue shouldRetryUpload:(NSString *)fileID;
@end
@implementation UploadQueue
- (void)attemptUpload:(NSString *)fileID {
if ([self.delegate respondsToSelector:@selector(uploadQueue:willStartUpload:)]) {
[self.delegate uploadQueue:self willStartUpload:fileID];
}
// ... perform the upload ...
}
@end@implementation SettingsViewController
- (void)viewDidLoad {
[super viewDidLoad];
UIButton *logoutButton = [UIButton buttonWithType:UIButtonTypeSystem];
[logoutButton setTitle:@"Log Out" forState:UIControlStateNormal];
[logoutButton addTarget:self
action:@selector(logoutTapped:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:logoutButton];
}
- (void)logoutTapped:(UIButton *)sender {
[self.session endSession];
}
@endHistorical vs. modern: sorting the nine
Of the nine patterns in this lesson, five remain broadly current, everyday advice in Objective-C code written today: Observer, via its two concrete forms KVO and NotificationCenter, is used constantly and without controversy; Delegate and Target-Action are load-bearing Cocoa idioms that show no sign of going anywhere, since UIKit and AppKit themselves are built on both; Strategy, via protocol-typed properties, is standard practice and directly enables the dependency-injection patterns this course already taught; and Coordinator is, if anything, on an upward trajectory -- an increasingly recommended, actively adopted pattern for keeping navigation logic out of view controllers as apps grow. Three others are more historical in the sense that they are rarely hand-rolled from scratch in typical application code today, even though their underlying ideas remain sound and sometimes appear built into the platform anyway: Command survives mostly through NSInvocation and NSUndoManager rather than as an explicitly authored class hierarchy; standalone State-pattern class hierarchies are comparatively rare outside of genuinely complex state machines, with NSOperation standing as Foundation's own built-in example of the idea; and explicit, hand-built Chain of Responsibility is uncommon precisely because UIKit's responder chain already provides the one chain of handlers that matters most for an interactive app. The lesson to draw is not that the historical three are wrong or obsolete -- it's that Cocoa itself already absorbed them into its own APIs, so recognizing them there is more valuable day to day than writing new ones by hand.
Treating Strategy and Delegate as interchangeable because both are 'a protocol-typed property.'
Use Strategy for a small, focused protocol representing one swappable algorithm or policy; use Delegate for a single, specific relationship covering multiple distinct customization and notification points, typically with @optional methods.
They look similar mechanically but solve different design problems -- collapsing them together leads to bloated 'strategy' protocols that are really delegate protocols in disguise, or vice versa.
Hand-building a full Command class hierarchy with a base protocol, concrete command subclasses, and manual undo bookkeeping for ordinary in-app actions.
Reach for NSUndoManager for genuine undo/redo needs, and reserve a literal Command object hierarchy for cases that specifically need queuing, logging, or replay beyond what target-action or a simple method call already provides.
A hand-rolled Command hierarchy adds real structural overhead that Foundation's own undo machinery, or a plain method call, already covers for the overwhelming majority of everyday cases.
Building a custom handler-chain class from scratch to route some in-app event to whichever of several objects should handle it.
Check whether the responder chain (UIResponder, nextResponder, nil-targeted actions) already solves the problem before writing a custom chain -- it usually does for anything touching the view hierarchy.
UIKit's responder chain is a full, battle-tested Chain of Responsibility implementation already available to any UIResponder subclass; reinventing it duplicates work the platform already did.
Letting a view controller keep deciding what screen comes next as an app's navigation flows grow, on the theory that 'it's just one push, it's fine.'
Introduce a Coordinator once a flow has more than a screen or two, or needs to be reused, reordered, or tested independently of the screens it navigates between.
Navigation logic smeared across view controllers is exactly the accumulation of unrelated responsibility the Architecture lesson warned about, and it only gets harder to unwind the longer it's deferred.
What is the key structural difference between the Strategy pattern and the Observer pattern? A. Strategy has a subject notify one or more observers of a state change; Observer swaps an interchangeable algorithm behind a shared protocol B. Strategy swaps an interchangeable algorithm or behavior behind a shared protocol; Observer has a subject notify interested parties when its state changes C. They are the same pattern under two different names D. Strategy only applies to UI code; Observer only applies to networking code
B. Strategy swaps an interchangeable algorithm or behavior behind a shared protocol; Observer has a subject notify interested parties when its state changes — Strategy is about swapping which implementation of a behavior is in use; Observer is about one object announcing state changes to others without knowing who's listening.
KVO and NotificationCenter are both concrete implementations of which design pattern? A. Delegate B. Observer C. Command D. Template Method
B. Observer — Both mechanisms let interested parties be automatically notified of a change without the subject holding direct references to them -- the defining shape of Observer, differing mainly in scope (one property vs. broadcast events).
Which Foundation class did this lesson cite as a real, built-in example of the State pattern? A. NSInvocation B. NSUndoManager C. NSOperation D. NSNotificationCenter
C. NSOperation — NSOperation's isReady/isExecuting/isFinished/isCancelled state machine is a concrete example of behavior gated entirely on current internal state, the essence of the State pattern.
The Template Method pattern, as this course teaches it, relies most directly on which earlier concept? A. Category extensions on existing classes B. Overriding a method and optionally calling super, as taught in the Inheritance lesson C. Weak references to avoid retain cycles D. The @optional keyword in protocol declarations
B. Overriding a method and optionally calling super, as taught in the Inheritance lesson — Template Method is a base class calling its own overridable step methods in a fixed order; subclasses customize specific steps by overriding them, exactly the override/super mechanism from the Inheritance lesson.
What is the real, everyday, built-in example of Chain of Responsibility in UIKit and AppKit? A. The target-action mechanism B. The UIResponder chain, linked by nextResponder C. NSOperationQueue's dependency graph D. Key-Value Observing
B. The UIResponder chain, linked by nextResponder — A nil-targeted action or an unhandled event travels up the chain of UIResponder objects via nextResponder until something handles it -- Chain of Responsibility in concrete, built-in form.
Of the nine patterns covered in this lesson, which is described as increasingly recommended and gaining ground in modern Objective-C/UIKit codebases, rather than being historical? A. Command B. Standalone State-pattern class hierarchies C. Coordinator D. Explicit, hand-built Chain of Responsibility
C. Coordinator — Coordinator is explicitly marked as a modern, increasingly adopted pattern for extracting navigation logic out of view controllers, unlike the other three listed, which are comparatively uncommon to hand-roll today.
Take a hypothetical PhotoUploadViewController that currently (a) decides which compression algorithm to apply based on an if/else on file type, (b) pushes the next view controller directly inside its own 'upload succeeded' handler, and (c) exposes a single delegate method uploadDidFinish. Redesign it using three of this lesson's patterns: replace the if/else with a Strategy protocol for compression, extract the navigation decision into a Coordinator, and decide whether uploadDidFinish should stay a Delegate method or become something else.
Introduce an id<PhotoCompressionStrategy> property with concrete JPEGCompressionStrategy and HEICCompressionStrategy classes, selected once based on file type rather than branched on every call; move the 'what happens after upload succeeds' decision into an UploadCoordinator that the view controller reports to via a delegate method (uploadDidFinish stays a Delegate method precisely because it's a single relationship-specific event, not a swappable algorithm); and have the coordinator, not the view controller, perform the actual push or pop.
Ask, for each piece: is this one interchangeable algorithm (Strategy), a decision about what screen comes next (Coordinator), or a specific customization/notification point tied to this one relationship (Delegate)?