Mobile Development 13 min read

Applying Design Patterns to Optimize the Scan Feature Architecture

The Taobao Scan feature was refactored by applying the Responsibility‑Chain, State, and Proxy design patterns, separating view and logic layers, introducing a fluent pipeline API, modular state handling, and a global proxy, which together dramatically improved code maintainability, extensibility, testability, and reduced development risk.

DaTaobao Tech
DaTaobao Tech
DaTaobao Tech
Applying Design Patterns to Optimize the Scan Feature Architecture

This article describes how the "Scan" feature in Taobao’s mobile app was refactored using classic design‑pattern principles to improve maintainability, extensibility and testability.

Background : The original Scan implementation mixed procedural code with massive view‑controller methods (some >2000 lines) and tightly coupled UI with business logic, making any change risky and hard to test.

Optimization workflow :

Understand the business capabilities.

Analyze the legacy code and verify assumptions with runtime instrumentation.

Rewrite/restructure the code using design patterns.

Write unit tests for each component.

Validate and release.

1. Responsibility‑Chain Pattern – The processing of a scanned code is naturally a chain of steps. The new structure introduces three parts:

Creator – generates data objects.

Manager – holds creators and pipelines and provides a fluent API.

Pipeline – each unit processes the data and forwards it.

Key interfaces:

@protocol TBPipelineDataCreatorDelegate
@property (nonatomic, copy) void(^generateDataBlock)(id data, NSInteger dataId);
@end

Manager definition:

@interface TBPipelineManager : NSObject
- (TBPipelineManager *(^)(id
dataCreator))addDataCreator;
- (TBPipelineManager *(^)(id
pipeline))addPipeline;
@property (nonatomic, strong) void(^throwDataBlock)(id data);
@end

Implementation of the fluent API and recursive handling:

- (TBPipelineManager *(^)(id
dataCreator))addDataCreator {
    @weakify(self);
    return ^(id
dataCreator) {
        @strongify(self);
        if (dataCreator) { [self.dataGenArr addObject:dataCreator]; }
        return self;
    };
}

- (void)handleData:(id)data {
    [self recurPipeline:self.pipelineArr.firstObject data:data];
}

- (void)recurPipeline:(id
)pipeline data:(id)data {
    if (!pipeline) return;
    [pipeline receiveData:data throwDataBlock:^(id throwData) {
        NSObject *cur = (NSObject *)pipeline;
        if (cur.tb_nextPipeline) {
            [self recurPipeline:cur.tb_nextPipeline data:throwData];
        } else {
            !self.throwDataBlock ?: self.throwDataBlock(throwData);
        }
    }];
}

2. State Pattern – Used to manage the UI state of the Scan view (no code, single code, multi‑code). The pattern consists of a StateInfo holder, a BaseState abstract class and concrete state subclasses.

@interface TBBaseStateInfo : NSObject {
    TBBaseState
*_currentState;
}
- (void)performAction;
- (void)setState:(TBBaseState
*)state;
- (TBBaseState
*)getState;
@end
@protocol TBBaseStateDelegate
- (void)perfromAction:(TBBaseStateInfo *)stateInfo;
@end

Concrete state example:

@interface TBSingleCodeState : TBBaseState
@end

@implementation TBSingleCodeState
- (void)perfromAction:(TBStateInfo *)stateInfo {
    // business logic for single‑code display
}
@end

State switching in the business layer:

- (void)setupState {
    TBSingleCodeState *single = TBSingleCodeState.new;
    TBNormalState *normal = TBNormalState.new;
    TBMultiCodeState *multi = [self getMultiCodeState];
    [self.stateInfo setState:normal forType:TBStateTypeNormal];
    [self.stateInfo setState:single forType:TBStateTypeSingleCode];
    [self.stateInfo setState:multi forType:TBStateTypeMultiCode];
}

- (void)processorA { [self.stateInfo setType:TBStateTypeNormal]; }
- (void)processorB { [self.stateInfo setType:TBStateTypeMultiCode]; }
- (void)processorC { [self.stateInfo setType:TBStateTypeSingleCode]; }

3. Proxy Pattern – Provides a plug‑in mechanism so that any part of the app can obtain Scan‑related capabilities (decode, display, processing) via a global proxy.

+ (void)registerProxy:(id)proxy withProtocol:(Protocol *)protocol {
    if (![proxy conformsToProtocol:protocol]) return;
    [[TBGlobalProxy sharedInstance].proxyDict setObject:proxy forKey:NSStringFromProtocol(protocol)];
}

+ (id)proxyForProtocol:(Protocol *)protocol {
    return [[TBGlobalProxy sharedInstance].proxyDict objectForKey:NSStringFromProtocol(protocol)];
}

+ (void)removeAll {
    [TBGlobalProxy sharedInstance].proxyDict = [NSMutableDictionary dictionary];
}

Usage example:

- (id
)scanProxy {
    if (!_scanProxy) {
        _scanProxy = [TBGlobalProxy proxyForProtocol:@protocol(TBScanProtocol)];
    }
    _scanProxy.proxyImpl = self;
    return _scanProxy;
}

- (void)registerGlobalProxy {
    [TBGlobalProxy registerProxy:[[NSClassFromString(@"TBScanProxy") alloc] init]
                    withProtocol:@protocol(TBScanProtocol)];
    [TBGlobalProxy registerProxy:[[NSClassFromString(@"TBDecodeProxy") alloc] init]
                    withProtocol:@protocol(TBDecodeProtocol)];
}

New Architecture : The logic and presentation layers are separated into distinct modules (View, Logic, Interface). The responsibility‑chain, state and proxy components are wired together, resulting in a clear, decoupled structure that is easier to test and extend.

Conclusion : By introducing these three foundational design patterns, the Scan feature became more reusable, its codebase cleaner, and the overall development effort reduced, demonstrating the practical value of systematic architectural refactoring.

design patternsmobile developmentarchitectureiOSresponsibility chainProxy PatternState Pattern
DaTaobao Tech
Written by

DaTaobao Tech

Official account of DaTaobao Technology

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.