2013-09-03 66 views
8

trong ứng dụng iPhone của tôi, tôi đang sử dụng lớp con của AFHTTPClient để truy cập một dịch vụ web còn lại. Tôi muốn tất cả các yêu cầu của mình được xử lý bởi một cá thể của ứng dụng khách API của tôi vì vậy tôi sử dụng một mẫu đơn.Mẫu Singleton với tham số

Tính năng này hoạt động tốt khi dịch vụ chỉ chạy trên một URL. Tôi có thể sử dụng giá trị không đổi để đặt URL.

Bây giờ, trong phiên bản cuối cùng của ứng dụng, mỗi ứng dụng sẽ thực sự nói chuyện với một dịch vụ khác sẽ được cài đặt trong mạng công ty.

Vì vậy, tôi sẽ nhận URL dịch vụ từ cấu hình từ xa. Mẫu Singleton vẫn là một lựa chọn tốt ở đây? Làm thế nào tôi có thể tham số hóa nó nếu URL thực sự có thể thay đổi trong suốt thời gian chạy của ứng dụng?

cổ vũ

#import "FooAPIClient.h" 
#import "AFJSONRequestOperation.h" 

static NSString * const kFooAPIBaseURLString = @"http://192.168.0.1"; 

@implementation FooAPIClient 

+ (instancetype)sharedClient { 
    static FooAPIClient *_sharedClient = nil; 
    static dispatch_once_t onceToken; 
    dispatch_once(&onceToken, ^{ 
     _sharedClient = [[self alloc] initWithBaseURL:[NSURL URLWithString:kFooAPIBaseURLString]]; 
    }); 

    return _sharedClient; 
} 

- (id)initWithBaseURL:(NSURL *)url { 
    self = [super initWithBaseURL:url]; 
    if (!self) { 
     return nil; 
    } 

    [self registerHTTPOperationClass:[AFJSONRequestOperation class]]; 
    [self setDefaultHeader:@"Accept" value:@"application/json"]; 

    return self; 
} 

@end 
+0

Tôi nghĩ bạn sẽ phải viết một số mã. Nếu URL thay đổi liên tục thì đó là vấn đề đơn giản của chức năng "thay đổi URL" trên singleton của bạn. Nếu bạn có thể có nhiều URL hoạt động cùng một lúc thì bạn cần một số loại thư mục hoặc mảng các cá thể, hoặc có trường hợp "sở hữu" bởi mã sử dụng nó, so với địa chỉ thông qua một singleton. –

+0

Chỉ có thể có một URL cùng một lúc. Vấn đề là, một khi đã khởi tạo, AFHTTPClient không thể thay đổi URL của nó. Vì vậy, giải pháp là để có toàn bộ AFHTTPClient như là một tài sản riêng của khách hàng của riêng tôi thay vì mở rộng nó. Nếu URL thay đổi, tôi chỉ có thể tạo một AFHTTPClient mới trong singleton của tôi. Nên làm vậy. – Jan

Trả lời

1

Đây có thể là giải pháp. Thay vì phân lớp AFHTTPClient, chỉ cần đặt nó làm thuộc tính và tạo lại nó nếu URL thay đổi:

#import "FooAPIClient.h" 
#import "AFJSONRequestOperation.h" 
#import "AFHTTPClient.h" 

static NSString * const kFooAPIBaseURLString = @"http://192.168.0.1"; 

@interface FooAPIClient() 
@property AFHTTPClient * httpClient; 
@end 

@implementation FooAPIClient 

+ (instancetype)sharedClient { 
    static FooAPIClient *_sharedClient = nil; 
    static dispatch_once_t onceToken; 
    dispatch_once(&onceToken, ^{ 
     _sharedClient = [[self alloc] initWithBaseURL:[NSURL URLWithString:kFooAPIBaseURLString]]; 
    }); 

    return _sharedClient; 
} 

- (id)initWithBaseURL:(NSURL *)url { 

    self = [super init]; 
    if (!self) { 
     self.httpClient = [self setupClientForURL:url]; 
    } 
    return self; 
} 

-(AFHTTPClient*) setupClientForURL:(NSURL*) url { 
    AFHTTPClient * httpClient = [[AFHTTPClient alloc] initWithBaseURL:url]; 
    [httpClient registerHTTPOperationClass:[AFJSONRequestOperation class]]; 
    [httpClient setDefaultHeader:@"Accept" value:@"application/json"]; 
    return httpClient; 
} 

#pragma mark - RemoteConfigurationDelegate 

-(void) apiURLChanged:(NSURL*) newURL { 
    self.httpClient = [self setupClientForURL:newURL]; 
} 


#pragma mark - Public 

-(void) consumeAPI:(CompletionBlock) completion { 
    [self.httpClient getPath:@"foo" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) { 
     if(completion) { 
      completion(responseObject, nil); 
     } 
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) { 
     if(completion) { 
      completion(nil, error); 
     } 
    }]; 
} 



@end 
1

Các Singleton pattern không phải là một bó.

Bài học này tôi đã học được từ Cocoa Touch. Có một số lớp trong khung làm việc sử dụng các cá thể chia sẻ, nhưng cho phép bạn linh hoạt trong việc tạo ra các cá thể của riêng bạn nếu bạn cần chúng. NSNumberFormatter, NSDateFormatter, NSBundle, NSFileManager và nhiều thứ khác là các ví dụ về các lớp mà bạn có thể tạo các cá thể của riêng mình nếu bạn cần chúng.

Trong trường hợp của bạn, tôi sẽ có hai phương pháp lớp đó trở lại trường hợp:

+ (instancetype)sharedClient { 
    static FooAPIClient *instance = nil; 
    static dispatch_once_t onceToken; 
    dispatch_once(&onceToken, ^{ 
     instance = [[self alloc] initWithBaseURL:[NSURL URLWithString:kFooAPIBaseURLString]]; 
    }); 

    return instance; 
} 

static FooAPIClient *FooSharedCorporateInstance; 

+ (instancetype)sharedCorporateClient { 
    @syncronized (FooSharedCorporateInstance) { 
     return FooSharedCorporateInstance; 
    } 
} 

+ (void)setSharedCorporateClientWithURL:(NSURL *)URL { 
    @syncronized (FooSharedCorporateInstance) { 
     FooSharedCorporateInstance = [[self alloc] initWithBaseURL:URL]; 
    } 
} 

Là một lợi ích phụ, điều này buộc lớp và dụ trách nhiệm riêng biệt mà có xu hướng mờ trong lớp singleton.

+0

Không, đây không phải là giải pháp. URL không thể là hằng số. URL sẽ được đặt động trong thời gian chạy. – Jan

+0

@Tôi có thể cập nhật câu trả lời của mình không. Bạn có thể gọi '+ setSharedCorporateClientWithURL:' khi khởi động với giá trị ban đầu, hoặc sử dụng nil để chỉ ra không có khách hàng doanh nghiệp nào. –

+0

với giải pháp '+ setSharedCorporateClientWithURL:', các đối tượng có tham chiếu đến một "phiên bản được chia sẻ" cũ sẽ tiếp tục nói chuyện với URL cũ. Vì vậy, nó sẽ không phải là một mẫu Singleton nữa. – Jan

Các vấn đề liên quan