Программа без ветвления — очень простая и странная программа. Программа должна думать, просчитывать варианты, по-разному реагировать на различные внешние воздействия и вообще не быть однообразной. А это все невозможно без ветвлений. Ветвления реализуются при помощи опреаторов if и switch. Так зачем е с ним бороься?
Периодически, при просмотре чужого кода, мне попадаются длинные методы и громоздкие конструкции вложенных if и switch. Например, такие:
Меня лично, такие методы пугают и они просто не помещяются в моей голове. Первое что просится — это дать названия всем магичесским числам. Например, так:
Но если присмотреться, то мы не учитываем LoginSection если установлено значение self.loggedIn и прячем DetailsSection если сброшено значение self.settings.showDetails. Но мы просто преобразуем значение section в SectionIndex исходя из текущего состояния:
По-моему, уже выглядит гораздо приятнее, особенно если учесть, что таких ветвлений в объекте обычно несколько. Например, -titleForItem:inSection:, -performActionForItem:inSection: и т.п. А что если вспомнить о том, что язык у нас объектно-ориентированный, и состояние системы можно занести в объект-состояние? Тогда все эти методы сократятся до одной строки — обращение к объекту-состояния?
По-моему, вполне красиво. Осталось только откуда-то досать это самое состояние. Можн настраивать объекты состояний в самом объекте или написать нескольуо классов, определяющих состояния.
Получается, что мы избавились от ветвлений в нескольких местах, заменив его одной установкой состояния, а сами состояния получились целостными, понятными и собранными в одном месте.
Периодически, при просмотре чужого кода, мне попадаются длинные методы и громоздкие конструкции вложенных 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 {...}
// ...
Получается, что мы избавились от ветвлений в нескольких местах, заменив его одной установкой состояния, а сами состояния получились целостными, понятными и собранными в одном месте.