2013-09-03 21 views
8

w mojej aplikacji iPhone, używam podklasy AFHTTPClient, aby uzyskać dostęp do usługi internetowej odpoczynku. Chcę, aby wszystkie moje żądania były obsługiwane przez jedną instancję mojego klienta API, więc używam wzorca singleton.Singleton wzór z parametrem

Działa to dobrze, gdy usługa działa tylko raz pod adresem URL. Mogę użyć stałej wartości do ustawienia adresu URL.

Teraz, w ostatecznej wersji aplikacji, każda aplikacja będzie komunikować się z inną usługą, która zostanie zainstalowana w sieci korporacyjnej.

Tak więc otrzymam URL usługi ze zdalnej konfiguracji. Czy wzór singletonowy nadal jest dobrym wyborem? Jak mam go sparametryzować, jeśli URL może się nawet zmienić w czasie działania aplikacji?

okrzyki

#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

Myślę, że będziesz musiał napisać jakiś kod. Jeśli adres URL zmienia się cyklicznie, jest to prosta funkcja "zmiany adresu URL" w swoim singletonie. Jeśli możesz mieć wiele adresów URL aktywnych w tym samym czasie, potrzebujesz jakiegoś katalogu lub tablicy instancji, lub masz instancję "posiadaną" przez kod, który używa tego kodu, a adresowana jest pojedynczo. –

+0

Może istnieć tylko jeden adres URL naraz. Problem polegał na tym, że po utworzeniu instancji program AFHTTPClient nie może zmienić adresu URL. Tak więc rozwiązaniem jest posiadanie całego AFHTTPClient jako prywatnej własności mojego własnego klienta, zamiast rozszerzania go. Jeśli adres URL ulegnie zmianie, mogę po prostu utworzyć nowy agent AFHTTPCl w moim singletonie. Że należy to zrobić. – Jan

Odpowiedz

1

To może być rozwiązanie. Zamiast podklasy AFHTTPClient, po prostu ustaw ją jako właściwość i ponownie ją stwórz, jeśli zmieni się adres 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 
1

Singleton nie musi być kaftan.

Ta lekcja dowiedziałem się od Cocoa Touch. Istnieje kilka klas w ramach, które używają współużytkowanych instancji, ale pozwalają na elastyczność tworzenia własnych instancji, jeśli ich potrzebujesz. NSNumberFormatter, NSDateFormatter, NSBundle, NSFileManager i wiele innych są przykładami klas, w których możesz tworzyć własne instancje, jeśli ich potrzebujesz.

W twoim przypadku, to mam dwie metody klasy, które zwracają instancji:

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

jako korzyść z boku, to zmusza oddzielnych instancji klasy i obowiązków, które mają tendencję do zacierania w pojedynczych zajęciach.

+0

Nie, to nie jest rozwiązanie. Adres URL nie może być stałą. Adres URL zostanie ustawiony dynamicznie w środowisku wykonawczym. – Jan

+0

@Jan Zaktualizowałem moją odpowiedź. Możesz wywołać '+ setSharedCorporateClientWithURL:' przy uruchomieniu z wartością początkową lub użyj zero, aby wskazać brak klienta korporacyjnego. –

+0

przy użyciu rozwiązania '+ setSharedCorporateClientWithURL:' obiekty odwołujące się do starej "współużytkowanej instancji" będą nadal rozmawiać ze starym adresem URL. Więc nie byłby to już wzór Singletona. – Jan