Переключатель Objective-C с использованием объектов? - PullRequest
14 голосов
/ 19 сентября 2008

Я занимаюсь программированием на Objective-C, которое включает в себя анализ NSXmlDocument и заполнение свойств объектов из результата.

Первая версия выглядела так:

if([elementName compare:@"companyName"] == 0) 
  [character setCorporationName:currentElementText]; 
else if([elementName compare:@"corporationID"] == 0) 
  [character setCorporationID:currentElementText]; 
else if([elementName compare:@"name"] == 0) 
  ...

Но мне не нравится шаблон if-else-if-else, который это производит. Глядя на оператор switch, я вижу, что могу обрабатывать только ints, chars и т. Д., А не объекты ... так есть ли лучший шаблон реализации, о котором я не знаю?

Кстати, я действительно придумал лучшее решение для настройки свойств объекта, но я хочу узнать конкретно о паттерне if - else против switch в Objective-C

Ответы [ 14 ]

2 голосов
/ 19 сентября 2008

Наиболее распространенный рефакторинг, предложенный для исключения операторов if-else или switch, - это введение полиморфизма (см. http://www.refactoring.com/catalog/replaceConditionalWithPolymorphism.html).. Устранение таких условий наиболее важно при их дублировании. В случае синтаксического анализа XML, такого как ваш пример, вы по существу перемещение данных в более естественную структуру, чтобы вам не пришлось дублировать условные выражения в других местах. В этом случае оператор if-else или switch, вероятно, достаточно хорош.

1 голос
/ 04 ноября 2008

То, что мы сделали в наших проектах, где нам нужно делать подобные вещи снова и снова, это настроить статический CFDictionary, отображающий строки / объекты для проверки на простое целочисленное значение. Это приводит к коду, который выглядит следующим образом:

static CFDictionaryRef  map = NULL;
int count = 3;
const void *keys[count] = { @"key1", @"key2", @"key3" };
const void *values[count] = { (uintptr_t)1, (uintptr_t)2, (uintptr_t)3 };

if (map == NULL)
    map = CFDictionaryCreate(NULL,keys,values,count,&kCFTypeDictionaryKeyCallBacks,NULL);


switch((uintptr_t)CFDictionaryGetValue(map,[node name]))
{
    case 1:
        // do something
        break;
    case 2:
        // do something else
        break;
    case 3:
        // this other thing too
        break;
}

Если вы ориентируетесь только на Leopard, вы можете использовать NSMapTable вместо CFDictionary.

1 голос
/ 21 сентября 2008

В этом случае, я не уверен, что вы можете легко реорганизовать класс, чтобы ввести полиморфизм, как предполагает Брэдли, так как это класс, родной от Какао. Вместо этого Objective-C может сделать это, используя категорию класса для добавления метода elementNameCode к NSSting:

   typedef enum { 
       companyName = 0,
       companyID,  
       ...,
       Unknown
    } ElementCode;

    @interface NSString (ElementNameCodeAdditions)
    - (ElementCode)elementNameCode; 
    @end

    @implementation NSString (ElementNameCodeAdditions)
    - (ElementCode)elementNameCode {
        if([self compare:@"companyName"]==0) {
            return companyName;
        } else if([self compare:@"companyID"]==0) {
            return companyID;
        } ... {

        }

        return Unknown;
    }
    @end

В вашем коде вы теперь можете использовать переключатель на [elementName elementNameCode] (и получать соответствующие предупреждения компилятора, если вы забудете проверить один из членов enum и т. Д.).

Как указывает Брэдли, это может не стоить того, если логика используется только в одном месте.

0 голосов
/ 09 февраля 2013

Как и в Lvsti, я использую блоки для выполнения схемы переключения объектов.

Я написал очень простую цепочку, основанную на блоках фильтров, которая берет n блоков фильтров и выполняет каждый фильтр для объекта.
Каждый фильтр может изменить объект, но должен вернуть его. Неважно, что.

NSObject + Functional.h

#import <Foundation/Foundation.h>
typedef id(^FilterBlock)(id element, NSUInteger idx, BOOL *stop);

@interface NSObject (Functional)
-(id)processByPerformingFilterBlocks:(NSArray *)filterBlocks;
@end

NSObject + Functional.m

@implementation NSObject (Functional)
-(id)processByPerformingFilterBlocks:(NSArray *)filterBlocks
{
    __block id blockSelf = self;
    [filterBlocks enumerateObjectsUsingBlock:^( id (^block)(id,NSUInteger idx, BOOL*) , NSUInteger idx, BOOL *stop) {
        blockSelf = block(blockSelf, idx, stop);
    }];

    return blockSelf;
}
@end

Теперь мы можем настроить n FilterBlocks для проверки различных случаев.

FilterBlock caseYES = ^id(id element, NSUInteger idx, BOOL *breakAfter){ 
    if ([element isEqualToString:@"YES"]) { 
        NSLog(@"You did it");  
        *breakAfter = YES;
    } 
    return element;
};

FilterBlock caseNO  = ^id(id element, NSUInteger idx, BOOL *breakAfter){ 
    if ([element isEqualToString:@"NO"] ) { 
        NSLog(@"Nope");
        *breakAfter = YES;
    }
    return element;
};

Теперь мы вставляем блок, который мы хотим проверить, в цепочку фильтров в массиве:

NSArray *filters = @[caseYES, caseNO];

и может выполнить его на объекте

id obj1 = @"YES";
id obj2 = @"NO";
[obj1 processByPerformingFilterBlocks:filters];
[obj2 processByPerformingFilterBlocks:filters];

Этот подход может использоваться для переключения, но также и для любого (условного) приложения цепочки фильтров, так как блоки могут редактировать элемент и передавать его.

...