带参数的单例模式

时间:2013-09-03 15:31:42

标签: objective-c design-patterns

在我的iPhone应用程序中,我正在使用AFHTTPClient的子类来访问其余的Web服务。我希望我的所有请求都由我的API客户端的一个实例处理,因此我使用单例模式。

当服务仅在一次URL上运行时,这可以正常工作。我可以使用常量值来设置URL。

现在,在应用程序的最终版本中,每个应用程序实际上将与将安装在公司网络中的另一个服务进行通信。

所以我将从远程配置中获取服务URL。单身人士模式在这里仍然是一个不错的选择吗?如果在应用程序运行期间URL实际上甚至可以更改,我应该如何参数化呢?

欢呼声

#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

2 个答案:

答案 0 :(得分:1)

单身模式不一定是紧身衣。

我从Cocoa Touch学到的这一课。框架中有几个使用共享实例的类,但如果需要,可以灵活地创建自己的实例。 NSNumberFormatter,NSDateFormatter,NSBundle,NSFileManager以及许多其他类都是类的示例,您可以根据需要创建自己的实例。

在你的情况下,我将有两个返回实例的类方法:

+ (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];
   }
}

作为附带好处,这会强制分离在单例类中容易模糊的类和实例职责。

答案 1 :(得分:1)

这可能是解决方案。而不是子类化AFHTTPClient,只需将其设置为属性并在URL更改时重新实例化它:

#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