What is switch and how to deal with it?

A program without branching is a very simple and strange program. The program should think, calculate options, respond differently to various external influences and not be uniform at all. And all this is impossible without branching. Branches are implemented using the if and switch operators. So why should you fight him?

Periodically, when viewing someone else's code, I come across long methods and cumbersome constructions of nested if and switch. For example, such:

// Примеры кода приведены на 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;
        }
    }
}


Personally, these methods scare me and they just don't fit in my head. The first thing you ask for is to give names to all the magic numbers. For example, like this:

enum SectionIndex {
    LoginSection = 0,
    DetailsSection,
    ExtendedSection
}


But if you look closely, we do not take into account LoginSection if set to self.loggedIn and hide DetailsSection if reset to self.settings.showDetails. But we just convert the section value to SectionIndex based on the current state:

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


In my opinion, it already looks much nicer, especially when you consider that there are usually several branches in an object. For example, -titleForItem: inSection :, -performActionForItem: inSection: etc. But what if we recall that our language is object-oriented, and the state of the system can be entered into an object-state? Then all these methods will be reduced to one line - a call to a state object?

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


In my opinion, it’s quite beautiful. It remains only to get this condition from somewhere. You can configure state objects in the object itself or write several classes that define states.


@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 {...}
// ...


It turns out that we got rid of branching in several places, replacing it with one state setting, and the states themselves turned out to be integral, understandable and assembled in one place.

Also popular now: