Шаблон стратегии в Objective-C - PullRequest
0 голосов
/ 29 августа 2018

Я написал образец шаблона разработки стратегии кода.

@protocol MyProtocol
- (void)execute1;
@end


@interface BaseClass : NSObject
@property(nonatomic, assign) NSInteger commonValue;
- (void)commonCalculator;
@end


@interface DogClass : BaseClass <MyProtocol>
@end

@interface CatClass : BaseClass <MyProtocol>
@end

Кроме того, я хочу создать BaseClass для реализации общей логики.
Но нет доступа к BaseClass из типа MyProtocol.

Например

- (void)foo {
    NSInteger type = 0;

    id<MyProtocol> obj = [self simpleFactory:type];
    [obj execute1]; // It works!!

    // I'd like the following code. However compile error occurs.
    [obj commonCalculator]; // error
    obj.commonValue = 10; // error

    // I don't want the following code.
    if (type == 0 ) {
        [(DogClass *)obj commonCalculator];
        ((DogClass *)obj).commonValue = 10;
    } else {
        [(CatClass *)obj commonCalculator];
        ((CatClass *)obj).commonValue = 10;
    }
}

- (id<MyProtocol>)simpleFactory:(NSInteger)type {
    if (type == 0) {
        return [[DogClass alloc] init];
    } else {
        return [[CatClass alloc] init];
    }
}

Есть ли способ использовать общий код в BaseClass при использовании шаблона стратегии?

Ответы [ 2 ]

0 голосов
/ 30 августа 2018

Насколько я вас правильно понимаю, я попытаюсь объяснить:

1) Я согласен, что вы предпочитаете подписывать свой базовый класс на свой протокол, а не подписывать каждого ребенка 2) Если вы собираетесь использовать полиморфизм по протоколу, вы должны принести:

- (void)commonCalculator;

метод к протоколу, а не к базовому классу. Тогда вы сможете реализовать необходимую логику в нужном месте, как в базовом классе. Просто реализуйте этот метод там.

3) Также хотел бы дать совет: Инженеры Apple любят использовать шаблон Class Cluster, это частный случай заводского шаблона. Итак, в файле .h у вас будет что-то вроде этого:

@protocol MyProtocol<NSObject>

@property (assign, nonatomic, readonly) NSInteger commonValue;
- (void)commonCalculator;

- (void)execute;

@end

typedef NS_ENUM(NSUInteger, BaseClassType) {
    BaseClassTypeA,
    BaseClassTypeB,
    BaseClassTypeC
};

@interface BaseClass: NSObject<MyProtocol>

- (instancetype)initWithType:(BaseClassType)type;

@end

@interface SubclassA: BaseClass
@end

@interface SubclassB: BaseClass
@end

@interface SubclassC: BaseClass
@end

И через ваш код вы будете работать только с экземплярами базового класса, но под капотом это будут экземпляры конкретного подкласса. Итак, реализация вашего .m файла будет выглядеть так:

@implementation BaseClass

- (instancetype)initWithType:(BaseClassType)type {
    switch (type) {
        case BaseClassTypeA: {
            self = [[SubclassA alloc] init];
        }
        case BaseClassTypeB: {
            self = [[SubclassB alloc] init];
        }
        case BaseClassTypeC: {
            self = [[SubclassC alloc] init];
        }
    }
    return self;
}

@end

Как видите, это иллюстрирует пример того, как использовать шаблон стратегии (потому что у нас есть несколько стратегий, и относительно того, какой из них создан, будет выполнен необходимый метод, поэтому каждая стратегия инкапсулирует некоторый алгоритм под капотом, но он скрывается за общим интерфейсом) в сочетании с шаблоном Class Cluster (который является частным случаем шаблона Factory). Вы должны помнить, что не существует каких-либо строгих стандартов того, сколько методов вы будете вносить в протокол стратегии, если он гармонично интегрируется с дизайном вашего приложения. Такой подход делает дизайн приложения очень изящным и понятным.

0 голосов
/ 29 августа 2018

Если BaseClass реализует поведение по умолчанию для <MyProtocol>, то BaseClass должно принять и реализовать <MyProtocol>.

@interface BaseClass : NSObject <MyProtocol>
@property(nonatomic, assign) NSInteger commonValue;
- (void)commonCalculator;
@end

Затем подклассы наследуют этот протокол:

@interface DogClass : BaseClass
...
@interface CatClass : BaseClass
...

Хорошей новостью является то, что подклассы могут вызывать [super execute1], и компилятор не будет жаловаться, если вы попытаетесь использовать или передать экземпляр BaseClass как id<MyProtocol>.

Теперь, если по какой-то необъяснимой причине вам необходимо разделить код для реализации суперкласса BaseClass <MyProtocol> на его собственный модуль, это можно сделать, создав категорию BaseClass, которая принимает и реализует реализацию по умолчанию есть:

@interface BaseClass (MyProtocolDefaults) <MyProtocol>
@end

...

@implementation BaseClass (MyProtocolDefaults)
- (void)execute1
{
    ...
}
@end

Если вы сделаете это, я бы по-прежнему предлагал вам не переустанавливать протокол в ваших подклассах (даже если он совершенно легален), а вместо этого «подбирать» протокол, импортируя категорию BaseClass:

#import "BaseClass.h"
#import "BaseClass+MyProtocolDefaults.h"

@interface DogClass : BaseClass
// this class adopts <MyProtocol> indirectly through the BaseClass category
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...