If statements are probably the most common thing that makes your code spaghetti. I’d like to share a few techniques that can help you make your code more readable.

Let’s examine the following example:

class Foo {

    String getName(String input) { 
        if ( "a".equals(input) ) {
            return "1";
        } else if ( "b".equals(input) ) {
            return "2";
        } else if ( "c".equals(input) ) {
            return "3";
        } else if ( "d".equals(input) ) {
            return "4";
        }
    }
}

Maps

One of the techniques is to use map instead of if statements.

class Foo {

    private static final Map<String, String> NAMES = Map.of(
            "a", "1",
            "b", "2",
            "c", "3",
            "d", "4"
    );

    String getName(String input) { 
        return NAMES.get(input);
    }
}

The above example is simply key-value pair mapping, but with help of basic functional interfaces you can use this also for a business logic.

@Slf4j
class Foo {

    private static final Map<String, Consumer<String>> NAMES = Map.of(
            "a", input- > log.info("business 1"),
            "b", input -> log.info("business 2"),
            "c", input -> log.info("business 3",
            "d", nput -> log.info("business 4"
    );

    void doSomeStuff(String input) { 
        NAMES.get(input).accept(input);
    }
}

“If Applicable” pattern - aka IF inversion of control

A really nice one. You should consider using it if you expect that “if” logic will grow up. It looks like this:

// (1)
interface LogicResolver { 
    void doLogic(Object... input);
    
    // (2)
    boolean isApplicable(Request input);
}

// (3)
class Foo implements LogicResolver {
    @Override
    public void doLogic(String input) { 
        // do some Foo stuff
    }
    
    @Override
    public isApplicable(Request input) {
        return request.getURI().startsWith("/foo")
    }
}

class Bar implements LogicResolver {
    @Override
    public void doLogic(String input) { 
        // do some Bar stuff
    }
    
    @Override
    public isApplicable(Request input) {
        return request.getURI().startsWith("/bar")
    }
}
// (4)
class SpringBootBean { 
    private final List<LogicResolver> resolvers;
    
    SpringBootBean(List<LogicResolver> resolvers) {
        this.resolvers = resolvers;
    }
    
    void handleRequest(Request request) { 
        LogicResolver resolver = resolvers.stream()
                 .filter(resolver -> resolver.isApplicable(request))
                 .findFirst()
                 .orElseThrow(IllegalStateException::new);
                 
        resolver.doLogic(request);
    }
}
(1) -> you need common interface for a logic. Like "if body block"
(2) -> condition. Like "if condition statement"
(3) -> a few implementations. Like next "else ifs"
(4) -> example how you can use it in spring boot application

Above example is a Java example, but this pattern can be easily implement in Typescript, or Javascript. I’m using it in Angular as well.

Angular tip

A tricky part for angular is that you cannot inject list of “interfaces”. The solution is to use InjectionToken.

export interface LogicResolver { 
    doLogic(Request request): void;
    
    isApplicable(Request request): boolean;
}

export const LOGIC_RESOLVERS = new InjectionToken<LogicResolver[]>('LOGIC_RESOLVERS');

@NgModule({
  declarations: [],
  providers: [
    {
      provide: LOGIC_RESOLVERS,
      useFactory: () => {
        return [
          new FooLogicResolver(inject(HttpClient)),
          new BarLogicResolver(inject(HttpClient)),
        ];
      },
    },
  ],
  imports: [],
  exports: [],
})
export class FooModule {}
@Injectable()
export class WhateverService { 
    constructor(@Inject(LOGIC_RESOLVERS) private readonly resolvers: LogicResolver[]) {
    }
    
    handleRequest(request: Request): void {
        const resolver = this.resolvers.filter(resolver => resolver.isApplicable(request));
        if ( !resolver ) throw new Error(`Cannot find logic resolver for request ${request}`);
        resolver.doLogic(request);         
    }
}