一、iOS 中组件化与模块化的区别
1. 定义与核心目标
特性
模块化(Modularization)
组件化(Componentization)
2. 典型场景
• 模块化:将项目拆分为 NetworkModule、UtilsModule、BaseUIKit 等,模块间通过头文件引用通信。
• 组件化:将电商 App 拆分为 HomeComponent、GoodsComponent、CartComponent,每个组件可独立运行,通过路由跳转。
二、组件间通信的实现方式
组件化的核心挑战是解耦组件依赖,常见通信方式如下:
1. 基于协议/代理(Protocol/Delegate)
适用场景:单向通信(如 A 组件调用 B 组件的某个功能)。
优点:强类型约束,编译期检查;缺点:组件间需引入协议头文件,存在间接依赖。
代码案例(Objective-C)
• 定义公共协议(跨组件接口)
// 公共协议(通常放在基础库或中间件中)
@protocol CartComponentProtocol <NSObject>
- (void)showCartViewController; // 展示购物车页面
@end
• B 组件(购物车组件)实现协议
// CartViewController.h
@interface CartViewController : UIViewController <CartComponentProtocol>
- (void)showCartViewController {
// 展示购物车逻辑
}
@end
• A 组件(首页组件)通过协议调用
// HomeViewController.m
@property (nonatomic, weak) id<CartComponentProtocol> cartDelegate; // 弱引用代理
// 触发通信
[self.cartDelegate showCartViewController];
2. 通知中心(NSNotification / NotificationCenter)
适用场景:一对多广播通信(如全局状态变更通知)。
优点:组件间无直接依赖;缺点:弱类型,运行时风险,难以追踪调用链。
代码案例(Swift)
• B 组件(接收通知)
// CartComponent.swift
NotificationCenter.default.addObserver(
self,
selector: #selector(updateCartBadge),
name: NSNotification.Name("UpdateCartBadge"),
object: nil
)
@objc func updateCartBadge(notification: NSNotification) {
guard let count = notification.userInfo?["count"] as? Int else { return }
// 更新购物车角标
}
• A 组件(发送通知)
// HomeComponent.swift
NotificationCenter.default.post(
name: NSNotification.Name("UpdateCartBadge"),
object: nil,
userInfo: ["count": 5]
)
3. 路由机制(Router Pattern)
适用场景:复杂组件间跳转、参数传递(如页面跳转、服务调用)。
核心思想:通过统一的路由中心,用字符串标识(如 URL 或组件名)映射到具体组件,避免直接依赖。
实现步骤:
1. 定义路由中心(Router)
// Router.h(Objective-C)
@interface Router : NSObject
+ (instancetype)shared;
// 注册组件:将 URL 或组件名映射到类/方法
- (void)registerURLPattern:(NSString *)pattern toClass:(Class)cls;
// 路由跳转:通过 URL 或组件名调用组件功能
- (UIViewController *)openURL:(NSString *)url withParams:(NSDictionary *)params;
@end
// Router.swift(Swift)
class Router {
static let shared = Router()
private var routeMap = [String: AnyClass]() // URL 到类的映射
func register(urlPattern: String, to cls: AnyClass) {
routeMap[urlPattern] = cls
}
func open(url: String, params: [String: Any] = [:]) -> UIViewController? {
guard let cls = routeMap[url] as? UIViewController.Type else { return nil }
let vc = cls.init()
// 传递参数(需约定参数协议)
if let paramsVC = vc as? RouteParamsHandler {
paramsVC.handle(params: params)
}
return vc
}
}
2. 组件注册路由
购物车组件注册页面路由
// CartComponent.m
+ (void)load {
[[Router shared] registerURLPattern:@"cart://show" toClass:[CartViewController class]];
}
3. 跨组件调用
首页组件通过路由打开购物车
// HomeComponent.m
UIViewController *cartVC = [[Router shared] openURL:@"cart://show" withParams:@{@"userId": @"123"}];
[self.navigationController pushViewController:cartVC animated:YES];
4. 中间件模式(Protocol + 工厂类)
适用场景:组件需暴露复杂服务(如获取数据、操作功能)。
实现:定义中间件协议,组件实现协议,通过工厂类获取实例。
代码案例(Objective-C)
• 定义中间件协议
// UserServiceProtocol.h
@protocol UserServiceProtocol <NSObject>
- (NSString *)getCurrentUserName;
- (void)logout;
@end
• 用户组件实现协议
// UserService.m
@interface UserService : NSObject <UserServiceProtocol>
- (NSString *)getCurrentUserName { return @"张三"; }
- (void)logout { /* 登出逻辑 */ }
@end
• 工厂类获取服务
// ServiceFactory.h
+ (id<UserServiceProtocol>)getUserService {
// 动态加载用户组件(如通过运行时或配置文件)
return [[UserService alloc] init];
}
三、组件化实战:路由跳转案例(Objective-C)
1. 组件结构
Project
├─ BaseComponent // 基础库(含路由中心、公共协议)
├─ HomeComponent // 首页组件(Target: HomeModule)
├─ CartComponent // 购物车组件(Target: CartModule)
└─ AppDelegate // 主工程
2. 路由中心实现(BaseComponent/Router.h)
#import <UIKit/UIKit.h>
@protocol RouteParamsHandler <NSObject>
- (void)handleRouteParams:(NSDictionary *)params;
@end
@interface Router : NSObject
+ (instancetype)shared;
- (void)registerURL:(NSString *)url toViewControllerClass:(Class)vcClass;
- (UIViewController *)openURL:(NSString *)url withParams:(NSDictionary *)params;
@end
3. 购物车组件注册路由(CartComponent/CartViewController.m)
#import "CartViewController.h"
#import "Router.h"
@implementation CartViewController
+ (void)load {
// 组件加载时注册路由(利用 +load 方法自动执行)
[[Router shared] registerURL:@"cart://view" toViewControllerClass:self.class];
}
- (void)handleRouteParams:(NSDictionary *)params {
self.userId = params[@"userId"]; // 接收参数
}
@end
4. 首页组件触发路由跳转(HomeComponent/HomeViewController.m)
#import "HomeViewController.h"
#import "Router.h"
- (IBAction)goToCartButtonTapped:(id)sender {
UIViewController *cartVC = [[Router shared] openURL:@"cart://view" withParams:@{@"userId": @"123"}];
[self.navigationController pushViewController:cartVC animated:YES];
}
5. 路由中心实现细节(BaseComponent/Router.m)
#import "Router.h"
@interface Router ()
@property (nonatomic, strong) NSMutableDictionary<NSString *, Class> *routeMap;
@end
@implementation Router
+ (instancetype)shared {
static dispatch_once_t onceToken;
static Router *instance;
dispatch_once(&onceToken, ^{
instance = [[Router alloc] init];
});
return instance;
}
- (void)registerURL:(NSString *)url toViewControllerClass:(Class)vcClass {
self.routeMap[url] = vcClass;
}
- (UIViewController *)openURL:(NSString *)url withParams:(NSDictionary *)params {
Class vcClass = self.routeMap[url];
if (!vcClass) {
NSLog(@"未找到路由:%@", url);
return nil;
}
UIViewController *vc = [[vcClass alloc] init];
if ([vc respondsToSelector:@selector(handleRouteParams:)]) {
[vc performSelector:@selector(handleRouteParams:) withObject:params];
}
return vc;
}
@end
四、总结
1. 核心区别:
模块化是“分而治之”,解决代码复用;组件化是“独立自治”,解决复杂业务解耦与团队协作。
2. 通信选择:
简单场景用 协议/代理 或 通知;
复杂场景(如页面跳转、跨组件服务调用)用 路由机制(推荐,解耦最彻底)。
3. 最佳实践:
定义统一的中间件协议或路由规范,避免组件直接依赖;
利用 +load 或初始化方法自动注册组件路由,减少手动配置。
通过组件化与路由机制,可实现“组件独立开发、动态组装”,大幅提升大型项目的可维护性与协作效率。