设计模式示例

先写出最直观的烂代码,然后一步步因为需求变更而“被迫”演进成设计模式。


演进案例一:电商收银台的计费引擎(策略模式)

语言:Swift

场景:计算用户的最终支付金额

需求 1:所有用户全额支付,无任何折扣

First Attempt: Raw Implementation

class PriceCalculator {
    func calculate(price: Double, userType: String) -> Double {
        return price
    }
}

代码很简单,生活很美好。直到运营提出了新需求。


需求 2:VIP 用户打 8 折,普通用户原价

Second Attempt: Using If-Else

class PriceCalculator {
    func calculate(price: Double, userType: String) -> Double {
        if userType == "VIP" {
            return price * 0.8
        }
        return price
    }
}

暂时还能忍受。


需求 3:新增“新用户立减20元”,新增“年底大促满100减50”

Third Attempt: Switch / If-Else Hell

class PriceCalculator {
    func calculate(price: Double, userType: String) -> Double {
        if userType == "VIP" {
            return price * 0.8
        } else if userType == "NewUser" {
            return price - 20.0
        } else if userType == "YearEndPromo" {
            return price >= 100 ? price - 50 : price
        }
        return price
    }
}

坏味道

  1. 违反开闭原则:每次运营搞活动,你都要修改这个核心类,测试要重新测一遍,风险极大。
  2. 逻辑臃肿:如果有 50 种活动,这个方法会有几千行。

需求 4:活动规则复杂多变,甚至需要动态下发

Fourth Attempt: Protocol (Strategy Pattern)

我们将“怎么算钱”这件事抽象成一个协议(接口)。

// 1. 定义策略协议
protocol DiscountStrategy {
    func applyDiscount(price: Double) -> Double
}

// 2. 具体的策略实现
struct VipDiscount: DiscountStrategy {
    func applyDiscount(price: Double) -> Double { return price * 0.8 }
}

struct NewUserDiscount: DiscountStrategy {
    func applyDiscount(price: Double) -> Double { return price - 20 }
}

struct NormalDiscount: DiscountStrategy {
    func applyDiscount(price: Double) -> Double { return price }
}

// 3. 上下文 (Context)
class PriceCalculator {
    // 注入策略
    func calculate(price: Double, strategy: DiscountStrategy) -> Double {
        return strategy.applyDiscount(price: price)
    }
}

// 使用
let calculator = PriceCalculator()
let finalPrice = calculator.calculate(price: 100, strategy: VipDiscount())

现在,新增活动只需要新建一个 struct,无需修改 Calculator。


优化:Swift 的函数式演进 (Functional Approach)

在 Swift 中,如果一个协议只有一个方法,我们甚至不需要创建那么多 struct/class。函数本身就是一种策略

Fifth Attempt: Strategies as Closures

// 定义一个闭包类型:输入原价,返回折后价
typealias DiscountStrategy = (Double) -> Double

class PriceCalculator {
    func calculate(price: Double, strategy: DiscountStrategy) -> Double {
        return strategy(price)
    }
}

// 预定义的策略库
struct Strategies {
    static let vip: DiscountStrategy = { $0 * 0.8 }
    static let newUser: DiscountStrategy = { $0 - 20 }
    static let normal: DiscountStrategy = { $0 }
}

// 使用
let calc = PriceCalculator()
// 方式 A: 使用预定义策略
calc.calculate(price: 100, strategy: Strategies.vip)

// 方式 B: 现场临时定义一个“打9折”的策略 (极度灵活)
calc.calculate(price: 100, strategy: { price in
    return price * 0.9 
})

最终形态:利用 Swift 的闭包特性,策略模式变成了简单的函数传递。既保留了扩展性,又消除了大量的样板代码。


演进案例二:登录表单校验(责任链模式)

语言:Objective-C

场景:用户点击登录,需要依次校验:账号非空 -> 格式正确 -> 密码长度 -> 是否包含非法字符。

需求 1:简单的账号密码非空检查

First Attempt: Hard Code in ViewController

- (void)loginWithEmail:(NSString *)email password:(NSString *)password {
    if (email.length == 0) {
        NSLog(@"邮箱不能为空");
        return;
    }
    if (password.length == 0) {
        NSLog(@"密码不能为空");
        return;
    }
    [self doLoginRequest];
}

需求 2:增加格式校验、最小长度校验、黑名单校验

Second Attempt: The Massive Method

- (void)loginWithEmail:(NSString *)email password:(NSString *)password {
    if (email.length == 0) {
        NSLog(@"邮箱不能为空");
        return;
    }
    if (![email containsString:@"@"]) {
        NSLog(@"邮箱格式错误");
        return;
    }
    if (password.length < 6) {
        NSLog(@"密码太短");
        return;
    }
    if ([email containsString:@"admin"]) { // 模拟黑名单
        NSLog(@"禁止使用此账号");
        return;
    }
    
    [self doLoginRequest];
}

坏味道

  1. 这个方法像面条一样越来越长。
  2. 如果我想先校验“黑名单”,再校验“格式”,需要手动调整代码顺序,容易出错。
  3. 很难复用(比如“注册”页面也需要同样的邮箱校验逻辑)。

需求 3:校验逻辑可复用,且支持动态配置校验顺序

Third Attempt: Chain of Responsibility (Classic OO)

我们需要把每一个校验逻辑拆分成独立的“Handler”。

抽象基类

@class LoginContext; // 封装参数的对象

@interface Validator : NSObject
@property (nonatomic, strong) Validator *nextValidator;
- (BOOL)validate:(LoginContext *)context;
@end

@implementation Validator
- (BOOL)validate:(LoginContext *)context {
    // 默认行为:如果自己没处理,就甩给下一个;如果没有下一个,说明全通过
    if (self.nextValidator) {
        return [self.nextValidator validate:context];
    }
    return YES;
}
@end

具体实现

// 邮箱非空校验
@interface EmailEmptyValidator : Validator
@end
@implementation EmailEmptyValidator
- (BOOL)validate:(LoginContext *)context {
    if (context.email.length == 0) {
        NSLog(@"邮箱为空");
        return NO; // 校验失败,链条终止
    }
    return [super validate:context]; // 传递给下一个
}
@end

// 密码长度校验
@interface PasswordLenValidator : Validator
@end
@implementation PasswordLenValidator
- (BOOL)validate:(LoginContext *)context {
    if (context.password.length < 6) {
        NSLog(@"密码太短");
        return NO;
    }
    return [super validate:context];
}
@end

组装链条

Validator *v1 = [EmailEmptyValidator new];
Validator *v2 = [PasswordLenValidator new];
v1.nextValidator = v2; // 链起来

[v1 validate:context];

优化:消除手动链表的繁琐 (Manager / Array Approach)

在 OC 中,手动写 v1.next = v2; v2.next = v3 既罗嗦又容易搞错指针。我们可以做一个“管理器”来遍历数组,这在逻辑上等同于责任链。

Fourth Attempt: Validation Manager

@interface ValidationManager : NSObject
- (void)addValidator:(Validator *)validator;
- (BOOL)runValidation:(LoginContext *)context;
@end

@implementation ValidationManager {
    NSMutableArray *_validators;
}
- (instancetype)init {
    if (self = [super init]) { _validators = [NSMutableArray array]; }
    return self;
}
- (void)addValidator:(Validator *)validator {
    [_validators addObject:validator];
}
- (BOOL)runValidation:(LoginContext *)context {
    for (Validator *v in _validators) {
        // 这里不再调用 next,而是由 Manager 统一调度
        // 注意:Validator 类里的 [super validate] 逻辑需要去掉,改为只负责自己的逻辑
        if (![v check:context]) { 
            return NO; 
        }
    }
    return YES;
}
@end

// 使用
ValidationManager *mgr = [ValidationManager new];
[mgr addValidator:[EmailEmptyValidator new]];
[mgr addValidator:[PasswordLenValidator new]];
// 随意调整顺序
// [mgr addValidator:[BlacklistValidator new]]; 

if ([mgr runValidation:context]) {
    [self doLogin];
}

总结:从 iOS 开发视角看设计模式

  1. 策略模式 (Strategy)

    • iOS 常见场景UITableViewdelegate 其实就是策略模式的一种变体(把“怎么渲染cell”的策略交给代理去实现)。还有排序算法、图片缓存策略(LRU/FIFO)。
    • 演进总结Hard Code -> Enum -> Protocol -> Closure
  2. 责任链模式 (Chain of Responsibility)

    • iOS 常见场景UIResponder 的事件传递链(点击 View,没处理就传给 SuperView,直到 ViewController)。
    • 演进总结Massive Method -> LinkedList Class -> Array Pipeline

最终建议
在 Swift/OC 开发中,不要过度封装。就像《精益编程》里说的,如果一个简单的 Block 或者 Switch 能清晰解决问题,就不要搞出 10 个类来实现一个“模式”。模式是用来解决痛点的,不是用来展示技术的。

posted @ 2026-01-21 22:11  CoderWGB  阅读(0)  评论(0)    收藏  举报