你是否在維護SocketRocket項目中的TestChat模塊時遇到過ViewController代碼臃腫、業務邏輯與UI強耦合的問題?本文將以TestChat模塊為例,詳細講解如何通過MVVM架構遷移解決這些痛點,使代碼更易維護和擴展。

重構前MVC架構分析

TestChat模塊原採用MVC架構,核心代碼集中在TCViewController.m中。該文件存在以下典型問題:

  • 職責過重:同時承擔網絡通信(SRWebSocketDelegate)、UI更新、數據處理等任務
  • 數據與UI強耦合:直接在控制器中管理消息數組_messages並處理表格刷新
  • 測試困難:業務邏輯難以單獨測試

關鍵代碼示例:

// TCViewController.m 中混合的數據處理與UI邏輯
- (void)_addMessage:(TCMessage *)message {
    [_messages addObject:message];
    [self.tableView insertRowsAtIndexPaths:@[ [NSIndexPath indexPathForRow:_messages.count - 1 inSection:0] ]
                          withRowAnimation:UITableViewRowAnimationNone];
    [self.tableView scrollRectToVisible:self.tableView.tableFooterView.frame animated:YES];
}

MVVM架構設計方案

架構分層

遷移後的架構包含以下核心組件:

深入淺出學習Struts框架(二):重構MVC模式代碼中跳轉路徑和業務邏輯_UI

核心模塊劃分

  1. Model層:TCMessage.h保持不變,專注數據存儲
  2. ViewModel層:新增TCChatViewModel.h處理業務邏輯
  3. View層:TCViewController.h僅負責UI展示與事件轉發
  4. 網絡層:封裝SRWebSocket通信邏輯到獨立服務類

分步遷移實現

1. 創建ViewModel層

新建TCChatViewModel.h和TCChatViewModel.m,將原ViewController中的業務邏輯遷移至此:

// TCChatViewModel.h
#import <Foundation/Foundation.h>
#import "TCMessage.h"

@interface TCChatViewModel : NSObject
@property (nonatomic, readonly) NSArray<TCMessage *> *messages;
@property (nonatomic, copy) void (^onMessagesUpdated)(void);

- (void)connectToServer;
- (void)sendMessage:(NSString *)message;
- (void)sendPing;
@end

2. 重構ViewController

修改TCViewController.m,使其通過綁定ViewModel實現UI更新:

// 重構後的ViewController
@implementation TCViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.viewModel = [[TCChatViewModel alloc] init];
    __weak typeof(self) weakSelf = self;
    self.viewModel.onMessagesUpdated = ^{
        [weakSelf.tableView reloadData];
    };
    [self.viewModel connectToServer];
}

// 移除原有數據處理邏輯,直接從ViewModel獲取數據
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.viewModel.messages.count;
}

@end

3. 網絡通信封裝

創建TCWebSocketService.h封裝SocketRocket通信邏輯:

// TCWebSocketService.h
#import <Foundation/Foundation.h>
#import <SocketRocket/SocketRocket.h>

@interface TCWebSocketService : NSObject <SRWebSocketDelegate>
+ (instancetype)sharedInstance;
- (void)connect:(NSString *)url;
- (void)sendMessage:(NSString *)message;
@end

重構效果對比

代碼結構優化

模塊

重構前代碼量

重構後代碼量

職責清晰度

TCViewController.m

194行

87行

大幅提升

新增ViewModel

0行

120行

單一職責

新增Service

0行

95行

職責明確

可維護性提升

  • 關注點分離:網絡邏輯、業務處理、UI展示完全分離
  • 可測試性:ViewModel可獨立進行單元測試
  • 擴展性:新增功能只需擴展ViewModel,無需修改ViewController

遷移注意事項

  1. 依賴注入:通過構造函數傳遞依賴,便於測試
  2. 綁定機制:可使用KVO或第三方框架實現ViewModel與View的響應式綁定
  3. 增量遷移:建議按功能模塊逐步遷移,而非一次性重寫

總結與展望

通過MVVM架構遷移,TestChat模塊代碼質量得到顯著提升。後續可進一步引入ReactiveCocoa實現更優雅的數據綁定,或參考SocketRocketTests中的測試結構為新架構添加完整測試覆蓋。

重構後的代碼結構遵循了開閉原則,為未來功能擴展奠定了良好基礎。建議在其他類似模塊中推廣這種架構模式,提升整個SocketRocket項目的代碼質量。