Как стать автором
Обновить

Что такое switch и как с ним бороться?

Время на прочтение3 мин
Количество просмотров9.2K
Программа без ветвления — очень простая и странная программа. Программа должна думать, просчитывать варианты, по-разному реагировать на различные внешние воздействия и вообще не быть однообразной. А это все невозможно без ветвлений. Ветвления реализуются при помощи опреаторов if и switch. Так зачем е с ним бороься?

Периодически, при просмотре чужого кода, мне попадаются длинные методы и громоздкие конструкции вложенных if и switch. Например, такие:

// Примеры кода приведены на Objective C, но без проблем могут быть перенесены на другой язык программирования
- (NSUInteger)numberOfItemsInSection:(NSUInteger)section {
    if (self.loggedIn) {
        swtich (section) {
            case 0:
                return 2;
            case 1: {
                if (self.settings.showDetails) {
                    return 5;
                }
                else {
                    return 3;
                }
            }
            case 2:
                return 3;
        }
    }
    else {
        swtich (section) {
            case 1: {
                if (self.settings.showDetails) {
                    return 5;
                }
                else {
                    return 3;
                }
            }
            case 2:
                return 3;
        }
    }
}


Меня лично, такие методы пугают и они просто не помещяются в моей голове. Первое что просится — это дать названия всем магичесским числам. Например, так:

enum SectionIndex {
    LoginSection = 0,
    DetailsSection,
    ExtendedSection
}


Но если присмотреться, то мы не учитываем LoginSection если установлено значение self.loggedIn и прячем DetailsSection если сброшено значение self.settings.showDetails. Но мы просто преобразуем значение section в SectionIndex исходя из текущего состояния:

- (SectionIndex)sectionIndexForSection:(NSUInteger)section {
    if (!self.loggedIn) {
        ++section;
    }
    if (!self.settings.showDetails) {
        ++section;
    }
    return (SectionIndex)section;
}

- (NSUInteger)numberOfItemsInSection:(NSUInteger)section {
    SectionIndex sectionIndex = [self sectionIndexForSection:section];
    switch (sectionIndex) {
        case LoginSection:
            return [self numberOfItemsInLoggedInSection];
        case DetailsSection:
            return [self numberOfItemsInDetailsSection];
        case ExtendedSection:
            return [self numberOfItemsInExtendedSection]
        }
    }
}


По-моему, уже выглядит гораздо приятнее, особенно если учесть, что таких ветвлений в объекте обычно несколько. Например, -titleForItem:inSection:, -performActionForItem:inSection: и т.п. А что если вспомнить о том, что язык у нас объектно-ориентированный, и состояние системы можно занести в объект-состояние? Тогда все эти методы сократятся до одной строки — обращение к объекту-состояния?

- (NSUInteger)numberOfSections {
    retun [self.state numberOfSections];
}

- (NSUInteger)numberOfItemsInSection:(NSUInteger)section {
    retun [self.state numberOfItemsInSection:section];
}

- (NSString *)titleForItem:(NSUInteger)item inSection:(NSUInteger)section {
    retun [self.state titleForItem:item inSection:section];
}

- (void)performActionForItem:(NSUInteger)item inSection:(NSUInteger)section {
    retun [self.state performActionForItem:item inSection:section];
}


По-моему, вполне красиво. Осталось только откуда-то досать это самое состояние. Можн настраивать объекты состояний в самом объекте или написать нескольуо классов, определяющих состояния.


@interface State : NSObject
@property (nonatomic, strong) NSArray *sections;
// ...
@end


@implementation State
- (NSUInteger)numberOfSections {
    return [self.sections count];
}

- (NSUInteger)numberOfItemsInSection:(NSUInteger)section {
    return [self.sections[section] count];
}

- (NSString *)titleForItem:(NSUInteger)item inSection:(NSUInteger)section {
    return self.sections[section][item][@"title"];
}

- (void)performActionForItem:(NSUInteger)item inSection:(NSUInteger)section {
    void(^actionBlock)() = self.sections[section][item][@"action"];
    actionBlock();
}

// Настройка состояний
- (void)setupForState {
    NSMutableDictionary *state = [NSMutableDictionary dictionary];
    if (self.loggedIn) {
        [state addObject:[self loggedInSection]];
    }
    if (self.showDetails) {
        [state addObject:[self detailsSection]];
    }
    [state addObject:[self extendedSection]];
    self.sections = state;
}

- (NSArray *)loggedInSection {...}
- (NSArray *)detailsSection {...}
- (NSArray *)extendedSection {...}
// ...


Получается, что мы избавились от ветвлений в нескольких местах, заменив его одной установкой состояния, а сами состояния получились целостными, понятными и собранными в одном месте.
Теги:
Хабы:
Всего голосов 25: ↑8 и ↓17-9
Комментарии18

Публикации

Истории

Ближайшие события

One day offer от ВСК
Дата16 – 17 мая
Время09:00 – 18:00
Место
Онлайн
Конференция «Я.Железо»
Дата18 мая
Время14:00 – 23:59
Место
МоскваОнлайн
Антиконференция X5 Future Night
Дата30 мая
Время11:00 – 23:00
Место
Онлайн
Конференция «IT IS CONF 2024»
Дата20 июня
Время09:00 – 19:00
Место
Екатеринбург
Summer Merge
Дата28 – 30 июня
Время11:00
Место
Ульяновская область