Ứng Dụng Các Mẫu Thiết Kế Cốt Lõi Trong Phát Triển iOS

Trong quy trình xây dựng ứng dụng trên nền tảng iOS, việc áp dụng các mẫu thiết kế (design patterns) đã được kiểm chứng giúp cải thiện tính modular, dễ bảo trì và khả năng mở rộng của hệ thống. Dưới đây là phân tích chi tiết về sáu mẫu thiết kế thường gặp nhất cùng với cách triển khai cụ thể bằng ngôn ngữ Objective-C.

1. Mẫu Singleton (Đơn Thể)

Mục đích: Đảm bảo rằng một lớp chỉ có duy nhất một thể hiện (instance) trong suốt vòng đời ứng dụng và cung cấp điểm truy cập toàn cục đến thể hiện đó.

Trường hợp sử dụng: Phù hợp cho các đối tượng quản lý tài nguyên chung như cấu hình ứng dụng, quản lý mạng, hoặc bộ nhớ đệm nơi cần trạng thái đồng nhất.

Triển khai trong Objective-C:

// File header
@interface AppConfig : NSObject
+ (instancetype)sharedConfig;
@end

// File implementation
@implementation AppConfig
+ (instancetype)sharedConfig {
    static AppConfig *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[super allocWithZone:NULL] init];
    });
    return sharedInstance;
}
@end

Lưu ý: Cơ chế `dispatch_once` đảm bảo tính an toàn thread và khởi tạo đối tượng chỉ diễn ra một lần duy nhất.

2. Mẫu Delegate (Ủy Quyền)

Mục đích: Tách biệt logic xử lý sự kiện khỏi đối tượng phát sinh sự kiện, cho phép một对象 chuyển giao trách nhiệm thực thi cho một đối tượng khác.

Trường hợp sử dụng: Rất phổ biến trong UIKit để xử lý các sự kiện tương tác người dùng hoặc hoàn tác tác vụ bất đồng bộ mà không gây phụ thuộc chặt chẽ giữa các lớp.

Các bước thực hiện:

  1. Khai báo một Protocol định nghĩa các phương thức cần thực hiện.
  2. Tạo một property delegate dạng weak để tránh retain cycle.
  3. Gọi các phương thức trong protocol khi sự kiện xảy ra.
  4. Đối tượng nhận ủy quyền tuân thủ protocol và implement logic.

Ví dụ minh họa:

// Định nghĩa Protocol
@protocol NetworkDelegate <NSObject>
- (void)didFinishLoadingData;
@end

// Đối tượng ủy quyền
@interface NetworkManager : NSObject
@property (nonatomic, weak) id<NetworkDelegate> delegate;
- (void)fetchData;
@end

@implementation NetworkManager
- (void)fetchData {
    // Giả lập quá trình tải dữ liệu
    [self.delegate didFinishLoadingData];
}
@end

// Đối tượng nhận ủy quyền
@interface MainViewController : UIViewController <NetworkDelegate>
@end

@implementation MainViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    NetworkManager *manager = [[NetworkManager alloc] init];
    manager.delegate = self;
    [manager fetchData];
}

- (void)didFinishLoadingData {
    NSLog(@"Dữ liệu đã tải xong");
}
@end

3. Mẫu Observer (Quan Sát)

Mục đích: Thiết lập mối quan hệ phụ thuộc một-nhiều giữa các đối tượng. Khi đối tượng nguồn thay đổi trạng thái, tất cả đối tượng phụ thuộc sẽ được thông báo tự động.

Trường hợp sử dụng: Đồng bộ hóa dữ liệu giữa Model và View, hoặc gửi thông báo giữa các module không liên quan trực tiếp.

Cách triển khai:

  • KVO (Key-Value Observing): Cơ chế có sẵn trong NSObject để theo dõi thay đổi thuộc tính.
  • NSNotificationCenter: Cơ chế broadcast thông báo đến nhiều đối tượng lắng nghe.

Ví dụ KVO:

// Đối tượng bị quan sát
@interface UserProfile : NSObject
@property (nonatomic, strong) NSString *username;
@end

// Đăng ký quan sát
[self.userProfile addObserver:self 
                   forKeyPath:@"username" 
                      options:NSKeyValueObservingOptionNew 
                      context:NULL];

// Xử lý khi có thay đổi
- (void)observeValueForKeyPath:(NSString *)keyPath 
                      ofObject:(id)object 
                        change:(NSDictionary<NSKeyValueChangeKey, id> *)change 
                       context:(void *)context {
    if ([keyPath isEqualToString:@"username"]) {
        NSLog(@"Tên người dùng mới: %@", change[NSKeyValueChangeNewKey]);
    }
}

// Hủy quan sát
- (void)dealloc {
    [self.userProfile removeObserver:self forKeyPath:@"username"];
}

Ví dụ Notification:

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

// Nhận thông báo
[[NSNotificationCenter defaultCenter] addObserver:self 
                                         selector:@selector(handleLogout:) 
                                             name:@"UserLoggedOut" 
                                           object:nil];

- (void)handleLogout:(NSNotification *)notification {
    // Cập nhật giao diện đăng xuất
}

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

4. Mẫu Factory Method (Phương Thức Factory)

Mục đích: Định nghĩa một giao diện để tạo đối tượng, nhưng để các lớp con quyết định lớp nào sẽ được khởi tạo. Điều này giúp trì hoãn việc khởi tạo cụ thể xuống lớp con.

Trường hợp sử dụng: Khi hệ thống cần tạo ra các đối tượng thuộc cùng một hệ thống phân cấp mà không cần biết trước lớp cụ thể.

Triển khai trong Objective-C:

// Sản phẩm chung
@interface Shape : NSObject
- (void)draw;
@end

// Sản phẩm cụ thể
@interface Circle : Shape
@end
@implementation Circle
- (void)draw {
    NSLog(@"Vẽ hình tròn");
}
@end

@interface Square : Shape
@end
@implementation Square
- (void)draw {
    NSLog(@"Vẽ hình vuông");
}
@end

// Factory base
@interface ShapeFactory : NSObject
+ (Shape *)createShape;
@end
@implementation ShapeFactory
+ (Shape *)createShape {
    @throw [NSException exceptionWithName:@"AbstractMethod" reason:@"Must override" userInfo:nil];
    return nil;
}
@end

// Factory cụ thể
@interface CircleFactory : ShapeFactory
@end
@implementation CircleFactory
+ (Shape *)createShape {
    return [[Circle alloc] init];
}
@end

// Sử dụng
Shape *myShape = [CircleFactory createShape];
[myShape draw];

5. Mẫu Strategy (Chiến Lược)

Mục đích: Định nghĩa một họ các thuật toán, đóng gói từng thuật toán riêng biệt và làm cho chúng có thể thay thế lẫn nhau. Chiến lược cho phép thuật toán thay đổi độc lập với khách hàng sử dụng nó.

Trường hợp sử dụng: Khi cần thay đổi hành vi运行时 (runtime), ví dụ như các phương thức nén dữ liệu khác nhau hoặc các thuật toán sắp xếp.

Triển khai trong Objective-C:

// Interface chiến lược
@protocol CompressionStrategy <NSObject>
- (void)compressFile:(NSString *)filePath;
@end

// Chiến lược cụ thể 1
@interface ZipCompression : NSObject <CompressionStrategy>
@end
@implementation ZipCompression
- (void)compressFile:(NSString *)filePath {
    NSLog(@"Nén file %@ bằng định dạng ZIP", filePath);
}
@end

// Chiến lược cụ thể 2
@interface RarCompression : NSObject <CompressionStrategy>
@end
@implementation RarCompression
- (void)compressFile:(NSString *)filePath {
    NSLog(@"Nén file %@ bằng định dạng RAR", filePath);
}
@end

// Context sử dụng chiến lược
@interface CompressorContext : NSObject
@property (nonatomic, strong) id<CompressionStrategy> strategy;
- (void)execute:(NSString *)file;
@end

@implementation CompressorContext
- (void)execute:(NSString *)file {
    [self.strategy compressFile:file];
}
@end

// Sử dụng
CompressorContext *context = [[CompressorContext alloc] init];
context.strategy = [[ZipCompression alloc] init];
[context execute:@"data.txt"];

context.strategy = [[RarCompression alloc] init];
[context execute:@"data.txt"];

6. Mẫu Composite (Thành Phần)

Mục đích: Kết hợp các đối tượng thành cấu trúc cây để biểu diễn hệ thống phân cấp "phần - toàn bộ". Mẫu này cho phép khách hàng đối xử với các đối tượng đơn lẻ và các组合 đối tượng một cách thống nhất.

Trường hợp sử dụng: Quản lý các cấu trúc phân cấp như hệ thống file, menu ngữ cảnh, hoặc cây view trong giao diện.

Triển khai trong Objective-C:

// Component interface
@protocol MenuComponent <NSObject>
- (void)display;
@end

// Leaf node (Nút lá)
@interface MenuItem : NSObject <MenuComponent>
@property (nonatomic, copy) NSString *name;
@end

@implementation MenuItem
- (void)display {
    NSLog(@"Menu Item: %@", self.name);
}
@end

// Composite node (Nút nhánh)
@interface Menu : NSObject <MenuComponent>
@property (nonatomic, strong) NSMutableArray<id<MenuComponent>> *items;
- (void)addItem:(id<MenuComponent>)component;
@end

@implementation Menu
- (instancetype)init {
    if (self = [super init]) {
        _items = [NSMutableArray array];
    }
    return self;
}

- (void)addItem:(id<MenuComponent>)component {
    [self.items addObject:component];
}

- (void)display {
    NSLog(@"--- Menu ---");
    for (id<MenuComponent> item in self.items) {
        [item display];
    }
}
@end

// Sử dụng
Menu *mainMenu = [[Menu alloc] init];
MenuItem *item1 = [[MenuItem alloc] init];
item1.name = @"Open";
MenuItem *item2 = [[MenuItem alloc] init];
item2.name = @"Save";

[mainMenu addItem:item1];
[mainMenu addItem:item2];
[mainMenu display];

Thẻ: ios-development objective-c design-patterns singleton delegate

Đăng vào ngày 21 tháng 5 lúc 19:41