Как сделать мой Singleton класс расширяемым?

У нас есть одноэлементный класс в одной из наших статических библиотек. Это синглтон, потому что мы хотим быть в состоянии "помнить" его состояние во все времена. По сути, это синглтон управления пользователями.

Он имеет свойство User *user и имеет такие методы, как - (void)authenticateUser.

Мы хотим передать это клиенту, который захочет создать свой собственный метод - (void)authenticateUser. Для этого я предположил, что они расширят класс UserManager и просто переопределяют этот метод.

Однако, поскольку это singleton, он имеет методы типа: - (id)sharedInstance и пользовательский метод alloc. Все они выглядят так:

static UserManager *_sharedInstance = nil;

+ (id)sharedInstance {
    @synchronized([UserManager class]) {
        if(!_sharedInstance){
            id temp = [[self alloc] init];
            [temp class];
        }
        return _sharedInstance;
    }
    return nil;
}

+ (id)alloc {
    @synchronized([UserManager class]) {
        NSAssert(_sharedInstance == nil, @"Attempted to allocate a second instance of a singleton");
        _sharedInstance = [super alloc];
        return _sharedInstance;
    }
    return nil;
}

Итак, учитывая это, возможно ли подкласс и расширить этот класс UserManager? Могу ли я создать ChildUserManager, который переопределяет функцию? Или мне придется переписать эти одноэлементные методы для работы с "новым" дочерним классом?

Есть ли способ изменить эти методы, чтобы третьи стороны могли легко расширить этот класс?

Спасибо

Ответ 1

Дизайн Beter должен был бы использовать композицию, а затем наследование. Объявить протокол AuthenticationDelegate

@protocol AuthenticationDelegate
@optional
-(void) authenticateUser:(User*)inUser;
@end

У вас есть свойство в UserManager, которое по умолчанию указывает на UserManager.

@class UserManager : NSObject <AuthenticationDelegate> {
    ......
} 

@property (assign) id<AuthenticationDelegate>  authenticator

+ (id)sharedInstance;

Ваши клиенты, если они хотят аутентифицироваться по их методу, затем должны подтвердить свой протокол AuthenticationDelegate и реализовать свои методы. Они должны установить свойство аутентификатора для своего класса, который ограничивается. Однако его синглтон. Поэтому они могут установить его, как только объект будет создан. Следовательно, они могут использовать свой Authenticator.

Но убедитесь, что дозатор аутентификации указывает на нуль. Вы можете реализовать метод setAuthenticator: так, чтобы аутентификатор указывал на UserManager, когда клиенты устанавливают это на nil.

static UserManager *_sharedInstance = nil;

@implementation UserManager

@synthasize authenticator;

+ (id)sharedInstance {
    @synchronized([UserManager class]) {
        if(!_sharedInstance){
            id temp = [[self alloc] init];
            [temp class];
        }
        return _sharedInstance;
    }
    return nil;
}

+ (id)alloc {
    @synchronized([UserManager class]) {
        NSAssert(_sharedInstance == nil, @"Attempted to allocate a second instance of a     singleton");
        _sharedInstance = [super alloc];
        return _sharedInstance;
    }
    return nil;
}

-(void)init {
    self = [super init];
    if (self) {
        self.authenticator = nil;
    }
}

-(void)setAuthenticator:(id<AuthenticationDelegate>)inAuthenticator {
    if (!inAuthenticator) {
        __authenticator = self;
    } else {
        __authenticator = inAuthenticator;
    }
}

#pragma mark - AuthenticationDelegate

-(void) authenticateUser:(User*)inUser
{
    // Your Authentication Code.
}

Надеюсь, что это поможет...

Ответ 2

Лучшая одноэлементная модель:

// UserManager.h

#import <Foundation/Foundation.h>

@interface UserManager : NSObject

+(instancetype)sharedInstance;

// clue for improper use (produces compile time error)
+(instancetype) alloc __attribute__((unavailable("alloc not available, call sharedInstance instead")));
-(instancetype) init  __attribute__((unavailable("init not available, call sharedInstance instead")));
+(instancetype) new   __attribute__((unavailable("new not available, call sharedInstance instead")));

@end


// UserManager.m

#import "UserManager.h"

@implementation UserManager

+(instancetype) sharedInstance {
    static dispatch_once_t pred;
    static id shared = nil;
    dispatch_once(&pred, ^{
        shared = [[super alloc] initUniqueInstance];
    });
    return shared;
}

-(instancetype) initUniqueInstance {
    return [super init];
}

@end

Если ваш подкласс, единственный экземпляр будет настроен на все, что вызывается первым, например:

[Animal sharedInstance];    // instance set to Animal
[[Cow sharedInstance] moo]; // unknown selector 'moo' because the instance is Animal

Только это будет работать:

[[Cow sharedInstance]moo];