Các mẫu thiết kế phổ biến trong phát triển iOS

Trong quá trình xây dựng ứng dụng iOS, các mẫu thiết kế (design patterns) giúp tổ chức mã nguồn hiệu quả, dễ bảo trì và mở rộng. Dưới đây là những mẫu thiết kế thường gặp cùng ví dụ minh họa thực tế.

1. Delegate (Ủy quyền)

Khi một lớp cần giao nhiệm vụ cho lớp khác mà không biết trước lớp nào sẽ đảm nhận, delegate là lựa chọn tối ưu. Mẫu này giúp tách biệt trách nhiệm giữa các thành phần.

Ví dụ điển hình: UITableViewDataSource và UITableViewDelegate — hai giao thức cho phép UITableView lấy dữ liệu và xử lý tương tác mà không cần biết chi tiết đối tượng cung cấp.

@protocol DataProviderDelegate <NSObject>
- (NSInteger)numberOfItems;
- (NSString *)titleForItemAtIndex:(NSInteger)index;
@end

2. Observer (Quan sát viên)

Mẫu này cho phép một đối tượng thông báo sự kiện đến nhiều đối tượng khác mà không cần biết danh tính cụ thể của chúng. Phù hợp để đồng bộ trạng thái giữa Model, View và Controller.

NotificationCenter là hiện thực phổ biến nhất: bất kỳ nơi nào trong ứng dụng đều có thể đăng ký lắng nghe hoặc phát tín hiệu.

// Đăng ký
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleEvent:) name:@"DataUpdated" object:nil];

// Gửi thông báo
[[NSNotificationCenter defaultCenter] postNotificationName:@"DataUpdated" object:nil];

3. Singleton (Đơn thể)

Đảm bảo chỉ tồn tại duy nhất một thể hiện của lớp trong suốt vòng đời ứng dụng, thường dùng để quản lý tài nguyên toàn cục như kết nối mạng, cấu hình hệ thống.

Ví dụ: [UIApplication sharedApplication]. Để đảm bảo tính đơn thể, cần ghi đè phương thức allocWithZone: và kiểm soát việc khởi tạo.

+ (instancetype)sharedManager {
    static MyManager *instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
    });
    return instance;
}

4. Command (Lệnh)

Đóng gói yêu cầu dưới dạng đối tượng, cho phép lưu trữ, truyền tải, hoãn thực thi hoặc xếp hàng lệnh. Cơ chế Target-Action của UIKit chính là hiện thực của mẫu này.

[button addTarget:self action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];

5. Flyweight (Chia sẻ nhẹ)

Tiết kiệm bộ nhớ bằng cách tái sử dụng các đối tượng có trạng thái giống nhau. UITableView tái sử dụng UITableViewCell là ví dụ kinh điển.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"];
    }
    // Cập nhật nội dung
    return cell;
}

6. Decorator (Trang trí)

Bổ sung hành vi cho đối tượng mà không thay đổi lớp gốc. Trong Objective-C, Category là cách hiện thực tự nhiên của mẫu này.

@interface NSString (Hashing)
- (NSString *)md5Hash;
@end

7. Strategy (Chiến lược)

Đóng gói các thuật toán vào các lớp riêng biệt, cho phép hoán đổi linh hoạt. NSArray hỗ trợ sắp xếp với selector tùy chọn là minh chứng rõ ràng.

NSArray *sorted = [array sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
    return [obj1.name compare:obj2.name];
}];

8. Factory (Nhà máy)

Tạo đối tượng thông qua một lớp trung gian, giúp che giấu logic khởi tạo và dễ dàng thay thế loại đối tượng được tạo ra.

+ (id)createVehicleWithType:(VehicleType)type {
    switch (type) {
        case VehicleTypeCar:
            return [[Car alloc] init];
        case VehicleTypeBike:
            return [[Bike alloc] init];
        default:
            return nil;
    }
}

Lưu ý: Không nên lạm dụng khi hệ thống còn non trẻ vì làm tăng độ phức tạp không cần thiết.

9. Class Cluster (Cụm lớp)

Ẩn chi tiết cài đặt bên trong bằng cách trả về các lớp con phù hợp từ một giao diện chung. NSNumber và UIButton là ví dụ tiêu biểu.

NSNumber *number = [[NSNumber alloc] initWithInt:42]; // Thực tế là NSCFNumber
UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem]; // Lớp con ẩn

10. MVC & MVVM

MVC: Chia ứng dụng thành Model (dữ liệu), View (giao diện), Controller (điều phối). Tuy nhiên, Controller thường phình to do chứa cả logic hiển thị.

MVVM: Tách logic hiển thị ra khỏi Controller, giao cho ViewModel — lớp trung gian chuyển đổi dữ liệu Model thành định dạng phù hợp cho View. Giúp Controller mảnh mai hơn và dễ test.

@interface UserViewModel : NSObject
@property (nonatomic, strong) NSString *displayName;
@property (nonatomic, assign) BOOL isPremiumUser;
- (void)loadFromModel:(User *)user;
@end

Thẻ: iOS objective-c design-patterns MVC mvvm

Đăng vào ngày 21 tháng 6 lúc 16:54