很多开发者(包括经验丰富的)都会遇到类似情况:明明知道某个技术概念(比如 Protocol),但在实际编码时却想不起来用。这其实是一个非常普遍的学习阶段现象。

一、为什么知道 Protocol 却想不起用?

1. 知识与应用存在断层

  • 知道 ≠ 会用:学习技术概念时,我们往往停留在"知道它是什么",但缺乏"什么时候用它"的直觉。
  • 示例:就像知道螺丝刀的存在,但看到松动的螺丝时,第一反应可能是用手去拧,而不是找螺丝刀。

2. 惯性思维主导

  • 习惯性用 if-else:面对具体问题时,大脑会优先调用最熟悉的解决方案(比如条件分支),而不是评估所有可能性。
  • 思维路径“我需要判断不同类 → 用 isKindOfClass: → 写一堆 if-else"而不是:“这些类有共同行为 → 应该抽象成协议”

3. 缺乏设计模式的敏感度

  • Protocol 的本质是抽象行为约定,但初学者往往更关注具体实现细节(比如如何写方法),而忽略抽象设计。

4. 对 Protocol 的理解不够深刻

  • 可能你目前对 Protocol 的认知停留在:
    • “它是声明方法的列表”(表面)
    • 而不是:“它是解耦和扩展架构的工具”(本质)。

二、如何培养「用 Protocol」的思维?

1. 建立「行为抽象」的条件反射

  • 遇到以下场景时,强制自己思考 Protocol
    1. 多个类有相同的方法名(比如你的 incrementShareCount)。
    2. 需要判断对象是否能响应某个方法(respondsToSelector:)。
    3. 代码中出现 if ([obj isKindOfClass:[XXX class]])
    4. 需要跨模块通信(如 Delegate 模式)。
  • 口诀“重复方法?抽象它!类型判断?协议化!跨类调用?定义协议!”

2. 从模仿优秀代码开始

  • 研究 Apple 官方代码:iOS SDK 中大量使用 Protocol(如 UITableViewDataSource, NSCopying)。思考为什么这些场景要用 Protocol。

  • 示例

    // 为什么 UITableView 不直接提供数据,而是通过 DataSource 协议?
    // 答案:解耦!UITableView 不需要知道数据来源是数组、CoreData 还是网络。
    

3. 重构练习:将现有代码改为 Protocol 实现

  • 具体步骤

    1. 找到一段用 if-elseisKindOfClass: 的代码。
    2. 思考:这些分支是否在处理同一类行为?
    3. 如果是,提取一个 Protocol,让相关类遵守它。
    4. conformsToProtocol: 替换类型判断。
  • 你的案例重构

    // Before: 用 isKindOfClass: 判断
    if ([self isKindOfClass:[NewsDetailViewController class]]) { ... }
    
    // After: 用 Protocol 判断
    if ([self conformsToProtocol:@protocol(ShareCountIncrementable)]) { ... }
    

4. 学习设计原则

  • SOLID 原则(尤其是以下两条):
    • 开闭原则(OCP):代码应对扩展开放,对修改关闭。你的 shareNewsMenuHelper: 方法不应该因为新增支持类而被修改。
    • 依赖倒置原则(DIP):依赖抽象(Protocol),而不是具体实现(NewsDetailViewController)。
  • Protocol 如何满足 SOLID:通过定义 ShareCountIncrementable,未来新增支持类时只需遵守协议,无需修改 shareNewsMenuHelper:

5. 可视化思考工具

  • 画类图:用纸笔或工具(如 PlantUML)画出类之间的关系。如果多个类指向同一行为,就该用 Protocol。

    [NewsDetailViewController] ..|> [ShareCountIncrementable]
    [DouYinViewController] ..|> [ShareCountIncrementable]
    [ShortDramaViewController] ..|> [ShareCountIncrementable]
    

6. 代码审查时主动提问

  • 每次提交代码前问自己:
    • “这里是否有重复行为可以抽象?”
    • “是否可以用 Protocol 替代类型判断?”
    • “如果未来新增类似功能,是否需要修改现有代码?”

三、Protocol 思维的进阶技巧

1. 区分「是什么」和「能做什么」

  • Bad: 判断对象"是什么”(isKindOfClass:)。“你是 NewsDetailViewController 吗?”
  • Good: 判断对象"能做什么"(conformsToProtocol:)。“你能增加分享计数吗?”

2. 结合多态使用

  • Protocol 的真正威力在于多态。例如:

    NSArray<id<ShareCountIncrementable>> *allShareCounters = @[vc1, vc2, vc3];
    for (id<ShareCountIncrementable> counter in allShareCounters) {
        [counter incrementShareCount]; // 无需关心具体类
    }
    

3. 替代继承的利器

  • 如果多个类的共同行为无法通过继承抽象(比如它们已经是不同子类),就用 Protocol。

4. Protocol + Default Implementation

  • 通过 Category 为 Protocol 提供默认实现(类似 Swift 的 Protocol Extension):

    // ShareCountIncrementable+Default.m
    @implementation NSObject (ShareCountIncrementableDefault)
    
    - (void)incrementShareCount {
        NSLog(@"Default: Share count incremented for %@", self);
    }
    
    @end
    

    这样即使某些类未实现方法,也不会崩溃。

四、实战训练题目

  1. 场景:你发现代码中有多处 if ([obj respondsToSelector:@selector(showError)])任务:用 Protocol 重构。
  2. 场景NetworkManager 需要将数据分别传递给 HomeVCProfileVC,但它们接收数据的方法名不同(updateHomeData: vs reloadProfile:)。任务:设计一个 DataReceivable 协议统一接口。

总结

培养 Protocol 思维的关键是:

  1. 建立行为抽象的敏感度(看到重复方法或类型判断就想到 Protocol)。
  2. 从具体案例中提炼模式(多分析 Apple 的代码和自己的代码)。
  3. 通过重构巩固习惯(定期回顾旧代码,思考如何用 Protocol 改进)。

坚持几周后,你会发现 Protocol 就像呼吸一样自然! 🚀