Design Patterns: Creational and Structural

Learn eight of the classic Gang-of-Four design patterns as they actually appear in Objective-C code -- the creational patterns Factory, Abstract Factory, Builder, and Singleton, and the structural patterns Adapter, Decorator, Facade, and Proxy -- through a genuine worked example of each, with an explicit sense of which of these remain broadly recommended in modern Objective-C and which should be reached for only deliberately.

A design pattern is a named, reusable solution to a recurring design problem -- not a library you import, but a shape of code that experienced developers recognize and reach for once they've seen the underlying problem enough times. Objective-C and the Cocoa/Cocoa Touch frameworks it sits inside are full of these patterns already, often so thoroughly baked into the standard libraries that it's easy to use them for years without ever naming them explicitly: dispatch_once-backed singletons, class factory methods like +stringWithFormat:, NSProxy as a genuine structural proxy, NSNotificationCenter and delegation as observer-family patterns. This lesson and the one that follows it work through seventeen named patterns split into two lessons sharing this module's title -- this one covers the four creational patterns (Factory, Abstract Factory, Builder, Singleton) and the four structural patterns (Adapter, Decorator, Facade, Proxy), with a genuine worked Objective-C example for each, not just a definition. Because some of these patterns are used very differently today than they were a decade ago, each one is explicitly marked as either a still-current, broadly recommended tool, or a pattern that made more sense historically and should now be reached for deliberately rather than by default -- with a full historical-versus-modern summary closing out the lesson.

Factory: A Method That Hides the Concrete Class From Its Caller

The Factory pattern, at its simplest, is a method that creates and returns an object while hiding the exact concrete class of what gets created from the code calling it. The caller asks for 'a shape' or 'a parser for this file type' and receives back an object satisfying that request, without needing to know, or even being able to easily discover, which specific subclass was actually instantiated. This is one of the most natural patterns in all of Objective-C, because Cocoa's own class-method convention -- covered in this course's Class Methods lesson -- already provides the exact shape a factory needs: a + method returning instancetype (or a specific class/protocol type) is, in essence, a factory method whether or not anyone calls it that.

A concrete, everyday example: a shape-drawing app defines an abstract Shape base class and several concrete subclasses -- CircleShape, SquareShape, TriangleShape -- and a factory class method + (Shape *)shapeWithType:(ShapeType)type that switches on the requested type and returns an instance of the matching concrete subclass, typed back to the caller as simply Shape *. Code calling [ShapeFactory shapeWithType:ShapeTypeCircle] gets back a fully-formed CircleShape instance but never writes the name CircleShape anywhere in its own source -- if a new shape type is added later, or an existing shape's concrete class changes, every caller of the factory method continues working unmodified, because the factory is the only place that needs to know about concrete shape classes at all.

Factory remains a genuinely common, broadly recommended pattern in modern Objective-C, not a historical curiosity -- it's the same idea behind Cocoa conveniences like + [NSString stringWithFormat:] or + [NSArray arrayWithObjects:...], and reaching for a class factory method whenever a caller shouldn't need to know or care about a concrete subclass is good, current practice, not something modern Objective-C code has moved past.

Abstract Factory: A Factory of Factories

Abstract Factory is Factory applied one level up: instead of a single method creating one kind of object, an Abstract Factory is an object (often itself created via a factory method, or configured once and reused) whose entire job is producing a *family* of related objects that are meant to be used together, guaranteeing that whatever combination of concrete classes it hands out is internally consistent. The classic illustration is a cross-platform UI toolkit: a UIThemeFactory protocol might declare - (Button *)createButton; and - (Checkbox *)createCheckbox;, with a LightThemeFactory concrete implementation returning light-styled buttons and checkboxes, and a DarkThemeFactory returning dark-styled equivalents of both -- the calling code asks its current theme factory for a button and a checkbox and is guaranteed, by construction, that it never accidentally mixes a light button with a dark checkbox, because whichever concrete factory it holds produces only matched, same-family objects.

The Objective-C code for this looks like a small step up from the plain Factory example: a UIThemeFactory protocol declares the family of creation methods, LightThemeFactory and DarkThemeFactory each conform to it and implement every method with their own consistent styling, and calling code holds an id<UIThemeFactory> property (itself an application of dependency inversion from the Architecture lesson) rather than a concrete factory class, so switching the entire app's theme is a matter of swapping which concrete factory instance that property holds.

This lesson marks Abstract Factory explicitly as a heavier-weight pattern, more commonly seen in larger or older/legacy Objective-C codebases than in typical modern code, and worth being honest about that: the family-of-related-objects problem it solves is real, but it's a genuinely narrower problem than the one plain Factory solves, and reaching for a full Abstract Factory when a codebase only ever needs one or two independently-creatable object types (rather than families that must stay internally consistent) is usually over-engineering. Before introducing an Abstract Factory, it's worth confirming there's an actual family-consistency requirement driving it, not just a vague sense that 'factory of factories' sounds more sophisticated than a plain factory method.

Builder: Step-by-Step Construction, Contrasted With Designated Initializers

The Builder pattern constructs a complex object incrementally, through a sequence of configuration calls on a separate builder object, finishing with a single call that assembles everything configured so far into the final, often immutable, result. It's most useful when an object has many optional pieces of configuration and constructing it through a single giant initializer call -- with many parameters, several of them nil or default in the common case -- would be unwieldy or hard to read at the call site. A NSURLRequestBuilder illustrates the idea: [[[[NSURLRequestBuilder new] setURL:url] setMethod:@"POST"] setHeader:@"Content-Type" value:@"application/json"] chains a sequence of configuration calls, each returning the builder itself so the calls can be chained, before a final - (NSURLRequest *)build; call assembles everything into an actual, immutable NSURLRequest.

It's worth being explicit about the contrast with something this course already taught at length: Objective-C's own designated-initializer convention, from the Initialization Patterns lesson, already solves a meaningful chunk of the same problem that Builder addresses in languages without that convention -- a well-designed class with a single designated initializer taking the truly required parameters, plus convenience initializers layering sensible defaults on top for common cases, gives callers a fairly ergonomic, chainable-feeling construction story ([[MyObject alloc] init]) without needing a separate builder object at all. Builder earns its keep specifically when the number of independently-optional configuration points is large enough that even a family of convenience initializers becomes unwieldy to maintain, or when construction genuinely needs to happen across multiple, possibly conditional, steps spread across different pieces of calling code before a final object is produced.

Because Objective-C's designated-initializer convention already covers so much of the same ground, Builder is noticeably less idiomatic in Objective-C than it is in some other languages -- it shows up, but less often than Factory or Facade, and a builder introduced where a couple of well-placed convenience initializers would have done the job just as clearly adds an extra class and some chained-call ceremony for comparatively little benefit. When reaching for Builder in Objective-C, it's worth first checking honestly whether the same construction problem could be solved more simply and more idiomatically with a well-designed initializer instead.

Singleton: One Instance, dispatch_once, and a Pattern to Use Deliberately

Singleton guarantees that a class has exactly one instance, globally accessible, for the entire lifetime of the app -- and in Objective-C, the standard, thread-safe way to implement it is exactly what this course's Concurrency: Synchronization lesson already covered as the canonical dispatch_once use case: a class method, conventionally named + (instancetype)sharedInstance, holding a static variable and a static dispatch_once_t token, where the block passed to dispatch_once constructs the single instance and is guaranteed by GCD to run exactly once no matter how many threads call sharedInstance concurrently. Cocoa itself is full of singletons built exactly this way -- [NSNotificationCenter defaultCenter], [NSFileManager defaultManager], [UIApplication sharedApplication] -- which is a large part of why the pattern feels so natural and idiomatic to reach for in Objective-C specifically.

That very naturalness is exactly why this lesson marks Singleton with an explicit, deliberate warning rather than presenting it as simply a normal tool: Singleton is a pattern that has historically been very heavily overused across Objective-C and Cocoa codebases, precisely because dispatch_once makes it so mechanically easy to reach for that developers reach for it far more often than the problem actually calls for. A class doesn't need to be a singleton just because a codebase only currently happens to construct one instance of it -- that's an accident of how the app is used today, not a structural requirement, and wrapping ordinary objects in sharedInstance singletons 'just in case' or 'for convenience' is a habit worth actively resisting.

The real cost of overusing Singleton is exactly what this course's Mocking and Dependency Injection lesson already described in general terms: a singleton is hidden global state, reachable from literally anywhere in the app with no need to be passed in or declared as a dependency, which makes it very difficult to substitute a fake or mock version of it in a unit test, and makes the true dependencies of any given class harder to see just by reading its interface. The concrete, modern alternative that lesson already taught is the fix here too -- express the dependency as a protocol-typed property injected into the class that needs it (id<UserSession> rather than [UserSession sharedInstance] called directly), so that in production a real singleton-backed implementation can still be supplied, while a unit test supplies a fake conforming to the same protocol instead. Singleton is not banned by this lesson -- there are genuine cases (a hardware resource, an app-wide notification hub) where exactly one instance truly is a structural requirement -- but it should be chosen deliberately, for that reason, and not simply because it's the easiest way to make an object reachable from anywhere.

Adapter: Making One Interface Look Like Another

Adapter converts one interface into a different interface that calling code already expects, without modifying the underlying object being adapted -- typically because that underlying object comes from a library you don't control, or predates a newer protocol your code has standardized on, and changing it directly isn't an option. In Objective-C the adapter is usually a small wrapper class, or sometimes a category, implementing the protocol the caller expects while internally delegating every call to the incompatible object it wraps, translating each call's shape (method names, parameter types, return types) along the way.

A concrete example: a codebase has standardized its image-loading call sites on an id<ImageLoading> protocol declaring - (void)loadImageFromURL:(NSURL *)url completion:(void (^)(UIImage *))completion;, but needs to integrate a legacy third-party analytics image cache class, LegacyImageCache, whose only relevant method is - (void)fetchImage:(NSString *)urlString callback:(id)target selector:(SEL)sel; -- an entirely different shape, using a string instead of NSURL, and a target-action callback instead of a block. An LegacyImageCacheAdapter class conforms to id<ImageLoading>, holds an internal LegacyImageCache instance, and its -loadImageFromURL:completion: implementation converts the NSURL to a string, calls the legacy method with itself as the target and an internal selector, and inside that selector's implementation, invokes the completion block it stashed away -- translating fully between the two shapes in both directions.

This is closely related to a concept this course's C++/Objective-C++ lesson already touched on: a bridging wrapper class written in Objective-C++ that presents an Objective-C-friendly interface over an incompatible C++ class is, structurally, the exact same Adapter idea applied specifically at the Objective-C/C++ language boundary rather than between two Objective-C interfaces. Adapter remains a broadly current, recommended pattern in modern Objective-C -- integrating any third-party or legacy code whose interface doesn't match what the rest of a codebase expects is a routine, ongoing need, and a small, focused adapter class is usually a far better answer than reshaping call sites throughout the app to match the mismatched library instead.

Decorator: Wrapping to Add Behavior, Contrasted With Categories

Decorator wraps an object to add extra behavior around it, while preserving the exact same interface the original object presented -- the caller of a decorated object cannot tell, from the interface alone, whether it's talking to the plain original object or to some number of decorators stacked around it, and decorators can typically be composed, wrapping each other in a chain. The CachingArticleRepository example from the previous Architecture lesson was already a genuine instance of this pattern: it conforms to the exact same id<ArticleRepository> protocol as the plain network repository it wraps, adds caching behavior around it, and any code holding an id<ArticleRepository> reference works identically regardless of whether it's actually holding the plain repository or the caching decorator around it.

A second, more visual example: a LoggingButton decorator wraps any object conforming to a Tappable protocol (declaring - (void)performTap;), implements that same protocol itself, and its -performTap implementation logs a message and then calls -performTap on the wrapped object -- id<Tappable> logged = [[LoggingButton alloc] initWithWrapped:realButton]; produces an object that behaves exactly like realButton from the outside, plus the added logging behavior, and could itself be wrapped by a second decorator (say, one adding analytics tracking) without either decorator needing to know about the other.

It's worth explicitly contrasting this with Objective-C categories, which this course covered in depth in an earlier lesson: a category is sometimes used informally to achieve a decorator-like effect -- adding a method to an existing class so instances gain new behavior -- but a category modifies the original class's behavior in place, for every instance of that class throughout the entire app, whereas a true Decorator wraps a specific instance in a new, separate object and leaves the original class and its other instances completely untouched. That difference matters: a category-based 'decoration' cannot be selectively applied to just some instances or composed by wrapping the same object twice with two different added behaviors the way a real Decorator chain can, so reaching for an actual wrapping Decorator object, rather than a category, is the right choice whenever the added behavior should apply to specific instances rather than to every instance of a class everywhere.

Facade: One Simple Interface Over Subsystem Complexity

Facade provides one simplified, unified interface over a more complex subsystem made up of several collaborating parts, so callers of the facade never need to see or understand that internal complexity at all. It's one of the most common, still-current patterns in everyday Objective-C code, and this lesson's clearest, most practical example is a networking facade: a single APIClient class exposing a handful of straightforward, high-level methods like - (void)fetchUserProfile:(NSString *)userID completion:(void (^)(UserProfile *, NSError *))completion;, while internally coordinating NSURLSession request construction, header configuration, response status-code checking, NSJSONSerialization parsing (the exact concerns this course's JSON and Serialization lesson covered), and mapping the parsed dictionary into a typed UserProfile model object.

The value a caller gets from that Facade is direct and substantial: any view controller, service, or ViewModel elsewhere in the app calls [apiClient fetchUserProfile:userID completion:...] and receives back either a fully-formed model object or an error, with zero awareness that a raw HTTP request, response validation, or JSON parsing ever happened underneath. If the underlying networking library changes later -- swapping NSURLSession configuration details, adding retry logic, changing how errors are classified -- every one of those changes happens entirely inside APIClient, and not one caller anywhere in the app needs to be touched, because the facade's simple public interface hasn't changed even though everything behind it has.

Facade remains squarely a broadly recommended, everyday pattern in modern Objective-C -- it's essentially the natural shape of any well-designed manager or client class sitting in front of a genuinely complicated subsystem, and deliberately hiding that complexity behind a small, well-named set of methods is good practice regardless of which broader architecture (from the previous lesson) a codebase is organized around.

Proxy: Controlling Access, and NSProxy as Objective-C's Deepest Version

Proxy is an object that stands in for another object, controlling access to it -- a proxy can add checks before forwarding a call through (an access-control or logging proxy), defer the real work until it's actually needed (a lazy-loading proxy), or sit transparently between a caller and a remote or expensive resource. A concrete Objective-C example, at the ordinary object level: an AccessControlledFileProxy conforms to the same id<FileAccess> protocol as a real LocalFile object, holds a reference to that real object plus a currentUserRole, and its -readContents implementation checks the role before either forwarding the call through to the real file object or refusing with an error -- calling code holding an id<FileAccess> reference cannot tell whether it's talking to a real file directly or to this access-controlling proxy standing in front of one.

That ordinary-object-level version is a manually written proxy — you author the checking-and-forwarding logic by hand, method by method. Objective-C also has a genuinely deeper, runtime-level proxy mechanism, and this lesson explicitly cross-references it: this course's earlier Message Forwarding lesson covered NSProxy, an entirely separate root class from NSObject whose sole purpose is standing in for another object at the message-dispatch level, forwarding *any* message sent to it via -forwardInvocation: without needing to declare or implement each forwarded method individually the way AccessControlledFileProxy above does by hand. Where a manually written proxy like the file-access example must implement every method of the protocol it proxies, an NSProxy subclass can transparently stand in for an object of essentially any interface, intercepting and forwarding messages generically -- which is why NSProxy is fairly called Objective-C's most powerful, most general proxy mechanism, built directly into the runtime rather than assembled by hand.

Proxy remains a broadly current, recommended pattern where it genuinely fits -- lazy loading of an expensive resource, access control in front of a sensitive operation, or transparent runtime-level forwarding via NSProxy for cases (like fully dynamic remote-object stand-ins) where implementing a full protocol by hand would be impractical. The judgment call is simply picking the right level: a small, manually written proxy class for a narrow, known interface, versus NSProxy-based runtime forwarding when the interface being proxied is broad, dynamic, or not fully known in advance.

The Classic Objective-C Singleton, via dispatch_once

dispatch_once guarantees the block runs exactly once even under concurrent access from multiple threads, which is exactly why it's the standard, thread-safe way to write a Cocoa singleton -- see the Concurrency: Synchronization lesson for the mechanism in full. [self alloc] rather than [AnalyticsService alloc] lets a subclass correctly get its own shared instance if the class is ever subclassed, though many real-world singletons skip that nuance since they're rarely subclassed in practice. The comment at the bottom is the point this section makes explicitly: before writing a new sharedInstance, ask whether the class genuinely needs to be globally unique, or whether a protocol-typed, injected dependency would serve the same purpose with far better testability.

@implementation AnalyticsService

+ (instancetype)sharedInstance {
    static AnalyticsService *instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
    });
    return instance;
}

@end

// Reached for deliberately, not by default -- consider whether the dependency
// should instead be expressed as an injected id<SomeProtocol> property, per the
// Mocking and Dependency Injection lesson, before defaulting to sharedInstance.

Code Examples

The caller receives a fully usable Shape * and can call any method the Shape base class declares, but never names the concrete CircleShape class anywhere in its own code. Adding a new shape type later only requires a new case inside ShapeFactory; every existing caller keeps working without modification.

Shape *shape = [ShapeFactory shapeWithType:ShapeTypeCircle];

DarkThemeFactory guarantees every component it produces -- the button and the checkbox alike -- shares consistent dark styling. Calling code holds an id<UIThemeFactory> property rather than a concrete factory class, so the entire app's visual theme can be switched by swapping which concrete factory that property points to, with no risk of accidentally mixing a light button with a dark checkbox. A LightThemeFactory conforming to the same protocol, implementing both methods with light-appropriate colors instead, completes the pair of matched families this pattern exists to guarantee.

@interface DarkThemeFactory: NSObject <UIThemeFactory> Each set... call returns self, which is what makes the chained syntax possible, and the final -build call assembles everything configured so far into a single, immutable NSURLRequest. This is a genuinely useful shape when the number of optional configuration points is large; for a request with only one or two always-required parameters, a plain designated initializer, per the Initialization Patterns lesson, would likely be clearer and more idiomatic Objective-C.

[[[[[NSURLRequestBuilder new] setURL:url] setMethod:@"POST"] setHeader:@"Content-Type" value:@"application/json"] build];

Common mistake: Reaching for a Builder class reflexively for an object with only a couple of parameters, where a well-designed initializer would be both simpler and more idiomatic in Objective-C.

The sharedInstance method itself is the standard, correct, thread-safe way to write an Objective-C singleton when one is genuinely needed. The second half of the example shows the deliberate alternative this section emphasizes: rather than every class that needs session data calling [UserSession sharedInstance] directly, a class can instead accept an id<UserSession>-typed dependency through its initializer, letting production code pass the real singleton while a unit test passes a fake -- getting the convenience of a single shared instance in production without the hidden-global-state cost in tests.

dispatch_once(&onceToken, ^{ instance = [[self alloc] init]; });

Common mistake: Reaching for [ClassName sharedInstance] as the default way to make any object reachable, rather than asking first whether the class truly needs to be globally unique.

Calling code only ever sees id<ImageLoading> and its clean, block-based -loadImageFromURL:completion: method; it never touches LegacyImageCache's awkward string-URL, target-action-callback interface directly. The adapter absorbs the translation between the two shapes -- converting NSURL to a string on the way in, and converting a target-action callback into a stashed completion block on the way out.

[self.legacyCache fetchImage:url.absoluteString target:self selector:@selector(legacyDidFetch:)];

LoggingButton conforms to the exact same id<Tappable> protocol as whatever it wraps, so any caller holding an id<Tappable> reference cannot distinguish a plain tappable object from one wrapped in this decorator. The original object's own -performTap behavior is fully preserved -- the decorator calls through to it -- with logging added transparently around that call. A second decorator (say, one adding analytics) could wrap this LoggingButton in turn, since it too conforms to id<Tappable> -- decorators compose by design.

[_wrapped performTap]; // preserve original behavior, add logging around it

Every caller elsewhere in the app -- a ViewModel, a service, a view controller -- calls this one method and gets back either a ready-to-use UserProfile object or an error, with no awareness that a URL was built, an NSURLSession task was created and resumed, or a JSON dictionary was parsed underneath. All of that complexity, and any future changes to it, stays entirely inside APIClient.

• (void)fetchUserProfile:(NSString *)userID completion:(void (^)(UserProfile *, NSError *))completion;

AccessControlledFileProxy conforms to the same id<FileAccess> protocol as the real file object it wraps, and a caller holding an id<FileAccess> reference cannot tell whether it's talking to the real object or this proxy standing in front of it. Because it's a hand-written proxy, it must implement every method of FileAccess explicitly; NSProxy, covered in the Message Forwarding lesson, forwards arbitrary messages at the runtime level instead, without that per-method implementation burden, making it Objective-C's more general and more powerful proxy mechanism.

return [_realFile readContents]; // forward through to the real object

@interface ShapeFactory : NSObject
+ (Shape *)shapeWithType:(ShapeType)type;
@end

@implementation ShapeFactory
+ (Shape *)shapeWithType:(ShapeType)type {
    switch (type) {
        case ShapeTypeCircle:   return [[CircleShape alloc] init];
        case ShapeTypeSquare:   return [[SquareShape alloc] init];
        case ShapeTypeTriangle: return [[TriangleShape alloc] init];
    }
    return nil;
}
@end

// Caller never writes CircleShape/SquareShape/TriangleShape anywhere:
Shape *shape = [ShapeFactory shapeWithType:ShapeTypeCircle];
@protocol UIThemeFactory <NSObject>
- (UIButton *)createButton;
- (UISwitch *)createCheckbox;
@end

@interface DarkThemeFactory : NSObject <UIThemeFactory>
@end

@implementation DarkThemeFactory
- (UIButton *)createButton {
    UIButton *b = [UIButton buttonWithType:UIButtonTypeSystem];
    b.backgroundColor = [UIColor blackColor];
    b.tintColor = [UIColor whiteColor];
    return b;
}
- (UISwitch *)createCheckbox {
    UISwitch *s = [[UISwitch alloc] init];
    s.onTintColor = [UIColor whiteColor];
    return s;
}
@end
@interface NSURLRequestBuilder : NSObject
- (instancetype)setURL:(NSURL *)url;
- (instancetype)setMethod:(NSString *)method;
- (instancetype)setHeader:(NSString *)key value:(NSString *)value;
- (NSURLRequest *)build;
@end

// Chained configuration, each call returning self:
NSURLRequest *request =
    [[[[[NSURLRequestBuilder new] setURL:url]
                                  setMethod:@"POST"]
                                  setHeader:@"Content-Type" value:@"application/json"]
                                  build];
@implementation UserSession

+ (instancetype)sharedInstance {
    static UserSession *instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
    });
    return instance;
}

@end

// Preferred modern alternative for testability -- inject a protocol instead:
@interface ProfileViewModel : NSObject
- (instancetype)initWithSession:(id<UserSession>)session; // real sharedInstance OR a test fake
@end
@protocol ImageLoading <NSObject>
- (void)loadImageFromURL:(NSURL *)url completion:(void (^)(UIImage *image))completion;
@end

@interface LegacyImageCacheAdapter : NSObject <ImageLoading>
@property (nonatomic, strong) LegacyImageCache *legacyCache;
@end

@implementation LegacyImageCacheAdapter {
    void (^_pendingCompletion)(UIImage *);
}

- (void)loadImageFromURL:(NSURL *)url completion:(void (^)(UIImage *))completion {
    _pendingCompletion = [completion copy];
    [self.legacyCache fetchImage:url.absoluteString target:self selector:@selector(legacyDidFetch:)];
}

- (void)legacyDidFetch:(UIImage *)image {
    if (_pendingCompletion) { _pendingCompletion(image); }
}

@end
@protocol Tappable <NSObject>
- (void)performTap;
@end

@interface LoggingButton : NSObject <Tappable>
- (instancetype)initWithWrapped:(id<Tappable>)wrapped;
@end

@implementation LoggingButton {
    id<Tappable> _wrapped;
}

- (instancetype)initWithWrapped:(id<Tappable>)wrapped {
    if (self = [super init]) { _wrapped = wrapped; }
    return self;
}

- (void)performTap {
    NSLog(@"Tap occurred on %@", NSStringFromClass([_wrapped class]));
    [_wrapped performTap]; // preserve original behavior, add logging around it
}

@end
@interface APIClient : NSObject
- (void)fetchUserProfile:(NSString *)userID
               completion:(void (^)(UserProfile *profile, NSError *error))completion;
@end

@implementation APIClient

- (void)fetchUserProfile:(NSString *)userID
               completion:(void (^)(UserProfile *, NSError *))completion {
    NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"https://api.example.com/users/%@", userID]];
    NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url
        completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
            if (error) { completion(nil, error); return; }
            NSError *jsonError;
            NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];
            if (jsonError) { completion(nil, jsonError); return; }
            completion([UserProfile profileFromDictionary:json], nil);
        }];
    [task resume];
}

@end
@protocol FileAccess <NSObject>
- (NSData *)readContents;
@end

@interface AccessControlledFileProxy : NSObject <FileAccess>
- (instancetype)initWithRealFile:(id<FileAccess>)realFile role:(UserRole)role;
@end

@implementation AccessControlledFileProxy {
    id<FileAccess> _realFile;
    UserRole _role;
}

- (NSData *)readContents {
    if (_role != UserRoleAdmin) {
        NSLog(@"Access denied: insufficient role for file read.");
        return nil;
    }
    return [_realFile readContents]; // forward through to the real object
}

@end

// NSProxy, by contrast, forwards ANY message generically via -forwardInvocation:
// without implementing each method by hand -- see the Message Forwarding lesson.

Historical vs. Modern Recommendation, at a Glance

This is a summary of the section that follows, not a substitute for reading the reasoning behind each pattern's placement.

Broadly recommended today          Use deliberately, not by default
  --------------------------         ---------------------------------
  Factory     (still current)        Singleton     (historically overused)
  Facade      (still current)        Abstract Factory (heavier, less common now)
  Adapter     (still current)        Builder       (less idiomatic in Obj-C)
  Proxy / NSProxy (still current)

  Rule of thumb: the left column solves a problem that shows up
  constantly in ordinary code. The right column solves a narrower
  problem -- reach for it only once you've confirmed that narrower
  problem genuinely applies, not as a reflexive default.

Historical vs. Modern Recommendation: A Summary

Of the eight patterns this lesson covered, four remain broadly recommended in modern Objective-C without much qualification: Factory (hiding a concrete class behind a creation method) is everyday, idiomatic practice, matching Cocoa's own class-method conventions; Facade (a simple interface over a complex subsystem, such as a networking client hiding NSURLSession and JSON parsing) is likewise a normal, current shape for any well-designed manager or client class; Adapter (translating one interface into another a caller expects) remains a routine, necessary tool for integrating third-party or legacy code with a mismatched interface; and Proxy, especially via NSProxy's runtime-level message forwarding, remains Objective-C's most powerful current mechanism for a generic stand-in object. Three patterns are worth using more cautiously and deliberately, given how the language and community practice have evolved: Singleton, because dispatch_once made it so mechanically easy that it was historically overused far beyond cases that structurally needed a single instance, with real testability costs from the hidden global state it creates; Abstract Factory, a heavier pattern more common in larger or legacy codebases, worth reaching for only when there's a genuine family-of-related-objects requirement rather than by default; and Builder, which is less idiomatic in Objective-C specifically because the language's own designated-initializer convention already covers much of the same ground more simply for objects that aren't extremely configuration-heavy. None of the cautionary three are wrong to use -- they are simply patterns whose cost should be weighed deliberately against the problem actually in front of you, rather than reached for automatically.

Writing a + (instancetype)sharedInstance for a new class as the default, automatic way to make it reachable, without asking whether it structurally needs to be globally unique.

Reserve Singleton for cases with a genuine structural requirement for exactly one instance, and otherwise express the dependency as a protocol-typed property injected into whatever needs it, per the Mocking and Dependency Injection lesson.

Overused singletons create hidden global state reachable from anywhere in the app, which makes a class's true dependencies invisible from its interface and makes substituting a fake object for testing far harder than it needs to be.

Building a full Abstract Factory -- a protocol declaring several related creation methods, with multiple concrete conforming factory classes -- for a codebase that only ever needs to create one kind of object, with no requirement that multiple objects be produced as a matched, internally consistent family.

Reach for a plain Factory (a single creation method) unless there's a genuine requirement that several related objects be produced together and stay consistent with each other -- confirm that family-consistency requirement exists before adding Abstract Factory's extra layer.

Abstract Factory solves a narrower, more specific problem (matched families of objects) than plain Factory does (hiding one concrete class); introducing it without that narrower problem actually being present just adds indirection with no corresponding benefit.

Writing a Builder class with several chained set... methods for an object that only has one or two required parameters and no meaningfully optional configuration.

Prefer a well-designed designated initializer (with convenience initializers layering on sensible defaults) for objects with a small, largely-required parameter list, and reserve Builder for objects with genuinely many independently optional configuration points.

Objective-C's own designated-initializer convention already solves much of the problem Builder addresses in other languages; adding a whole extra builder class for a simple object mostly just adds ceremony without a matching increase in clarity.

Using an Objective-C category to informally 'decorate' every instance of a class with extra behavior, when the goal is really to add that behavior to just one specific instance without touching the rest.

Use a true Decorator -- a separate wrapper object conforming to the same interface -- when the added behavior should apply to specific instances only; reserve categories for behavior that genuinely should apply to every instance of the class throughout the app.

A category modifies a class's behavior globally, for every instance, everywhere the app runs; a real Decorator wraps one specific object and leaves the original class and its other instances completely unaffected, which is a meaningfully different guarantee.

What does the Factory pattern hide from the code that calls it? A. The number of properties the created object has B. The concrete class actually being instantiated -- the caller works only with a base class or protocol type C. Whether the method is a class method or an instance method D. The memory management (retain/release) of the created object

B. The concrete class actually being instantiated -- the caller works only with a base class or protocol type — A Factory method returns an object typed as a base class or protocol, without the caller needing to know or name the specific concrete subclass actually instantiated -- exactly what +shapeWithType: does in this lesson's worked example.

Per this lesson, why is Abstract Factory marked as a heavier-weight pattern more common in larger or legacy Objective-C codebases than in typical modern code? A. Because Abstract Factory cannot be implemented using Objective-C protocols B. Because it solves a narrower problem than plain Factory -- guaranteeing a family of related objects stays internally consistent -- and is over-engineering when that family-consistency requirement isn't actually present C. Because Abstract Factory always requires subclassing NSObject twice D. Because Apple has formally deprecated the pattern in recent Xcode versions

B. Because it solves a narrower problem than plain Factory -- guaranteeing a family of related objects stays internally consistent -- and is over-engineering when that family-consistency requirement isn't actually present — Abstract Factory's value is specifically guaranteeing that several related objects, produced together, stay consistent as a family; introducing it without that specific requirement being present is unnecessary complexity compared to a plain Factory method.

How does Objective-C's designated-initializer convention relate to the Builder pattern? A. They are unrelated and solve completely different problems B. Designated/convenience initializers already solve much of the same step-by-step, optional-configuration construction problem Builder addresses in other languages, which is why Builder is less idiomatic in Objective-C than in some other languages C. Builder must always be implemented as a designated initializer in Objective-C D. Designated initializers can only be used inside a Builder's -build method

B. Designated/convenience initializers already solve much of the same step-by-step, optional-configuration construction problem Builder addresses in other languages, which is why Builder is less idiomatic in Objective-C than in some other languages — Because a well-designed designated initializer plus convenience initializers already gives callers a fairly ergonomic construction story for common cases, Builder's extra chained-configuration-object ceremony is comparatively less necessary in Objective-C, and it's worth checking whether an initializer would do the job before reaching for a builder class.

What is the primary risk this lesson associates with the historical overuse of the Singleton pattern in Objective-C/Cocoa codebases? A. Singletons make an app's binary noticeably larger B. Singletons are always slower at runtime than ordinary objects C. Singletons create hidden global state reachable from anywhere, making a class's true dependencies less visible and making it harder to substitute fakes or mocks for unit testing D. dispatch_once is not actually thread-safe, despite common belief

C. Singletons create hidden global state reachable from anywhere, making a class's true dependencies less visible and making it harder to substitute fakes or mocks for unit testing — The lesson is explicit that dispatch_once itself is a correct, thread-safe mechanism -- the concern is architectural: a singleton reachable from anywhere hides a class's real dependencies and makes injecting a test fake in its place much harder than with a protocol-typed, injected dependency.

What is the key difference between a hand-written Objective-C proxy (like an access-controlling wrapper implementing a specific protocol) and an NSProxy subclass? A. NSProxy can only be used for networking-related proxies B. A hand-written proxy must implement each method of the protocol it proxies individually, while NSProxy forwards arbitrary messages generically at the runtime level via -forwardInvocation:, without implementing each method by hand C. There is no real difference; NSProxy is just Apple's official name for the same hand-written pattern D. A hand-written proxy is always faster than NSProxy at runtime

B. A hand-written proxy must implement each method of the protocol it proxies individually, while NSProxy forwards arbitrary messages generically at the runtime level via -forwardInvocation:, without implementing each method by hand — NSProxy, covered in depth in the earlier Message Forwarding lesson, is a runtime-level forwarding mechanism that can stand in for essentially any interface without per-method implementations, which is why it's described here as Objective-C's most powerful, most general proxy mechanism.

According to this lesson's closing historical-vs-modern summary, which group of patterns remains broadly recommended in modern Objective-C without much qualification? A. Singleton, Abstract Factory, and Builder B. Factory, Facade, Adapter, and Proxy C. Only Factory, since all the other seven patterns covered in this lesson are considered outdated D. None of the eight patterns covered in this lesson remain recommended today

B. Factory, Facade, Adapter, and Proxy — The lesson's summary places Factory, Facade, Adapter, and Proxy in the broadly-recommended-today group, while marking Singleton, Abstract Factory, and Builder as patterns to use more cautiously and deliberately given modern alternatives and idioms.

A codebase has a PrinterManager class implemented as a classic +sharedInstance singleton via dispatch_once, used directly (via [PrinterManager sharedInstance] ...) from about a dozen different view controllers and services throughout the app. The team now wants to unit test one of those services without ever touching a real printer. Using the patterns from this lesson, describe a concrete refactoring that addresses this, and name which pattern(s) it draws on.

Define an id<Printing> protocol declaring only the methods the service actually calls on PrinterManager (e.g. - (void)printDocument:(Document *)doc completion:(void (^)(NSError *))completion;). Have PrinterManager (still a singleton, since a physical printer resource may genuinely be a structural one-instance case) conform to that protocol, and change the service to accept an id<Printing> dependency through its initializer rather than calling [PrinterManager sharedInstance] directly inside its own methods. Production code continues to construct the service with [PrinterManager sharedInstance] passed in as that protocol-typed dependency, so runtime behavior is unchanged, but a unit test can now construct the service with a lightweight fake object conforming to id<Printing> instead, with no real printer ever touched. This combines the Singleton section's deliberate-alternative guidance with the Architecture lesson's dependency inversion -- the singleton itself is not necessarily removed, but the service's dependency on it becomes an injected protocol rather than a direct, hard-coded call.

Consider extracting a protocol describing what the service actually needs from PrinterManager, and injecting a conforming object rather than calling the singleton directly -- this is the deliberate-Singleton-alternative approach from the Singleton section, combined with dependency inversion from the Architecture lesson.