How to Refactor a Switch Statement with Object Composition in Angular

Using Creational Patterns like Factory Method to refactor switch statements in Angular

Image for post
Image for post
Made by Itchimonji

Why do we use switch statements?

We use them because switch statements work the way our brain works. If we have to implement a selection of different things — like a selection of themes, or types of tea/coffee we want to cook, or a selection of cars we want to buy with our saved money — the logical way is to implement different junctions to map all the possibilities: we code like a flow.

Example code

In Angular, using an HTML element like <select> seduces us to handle it with switch statements to check the chosen value. Let’s take a list of themes as an example:

<div class="container">
<h1>Select theme with switch statement</h1>
<mat-form-field appearance="fill">
<mat-label>Theme selection</mat-label>
<mat-select [(ngModel)]="selectedTheme" (selectionChange)="selectionChanged()">
<mat-option value="dark">dark theme</mat-option>
<mat-option value="light">light theme</mat-option>
<mat-option value="red">red theme</mat-option>
<mat-option value="green">green theme</mat-option>
</mat-select>
</mat-form-field>
</div>
public selectionChanged() {
switch (this.selectedTheme): void {
case 'dark':
console.log('changed theme to dark');
// do something more with the theme
break
;
case 'light':
console.log('changed theme to light');
// do something more with the theme
break
;
case 'red':
console.log('changed theme to red');
// do something more with the theme
break
;
case 'green':
console.log('changed theme to green');
// do something more with the theme
break
;
default:
console.log('What ever..');
}
}
Image for post
Image for post

Is there a better way?

Yes, of course — Object Composition is a nice method for refactoring switch statements and for making your code more flexible and decoupled. Because you abstract a simple use case into a complex and easy to handle software design principle (in our case: a pattern), it reduces the maintenance effort and makes it more reusable. Sometimes it’s very hard to find the right way and the suitable software architecture — but with more practise about Design Patterns and Software Architecture, it could actually be easy.

Object Composition & Factory Method

Object Composition can help. Regarding the theme example, you only need to abstract the use case of the feature —managing themes, classes of themes. So, why using string attributes and no real classes? You can, for example, couple these concrete classes with an interface or an abstract class at design time:

made by @Itchimonji
made by @Itchimonji
Made by Itchimonji
Image for post
Image for post
Made by Itchimonji
Image for post
Image for post
Made by Itchimonji

The final code

So, let’s show how the code looks, if we implement this principle in Angular (from UML to code).

>>> theme.model.tsexport interface Theme {
name: string;
getStylename(): string;
getFontname(): string;
}

export class DarkTheme implements Theme {
public name = 'dark';

public getStylename(): string {
return this.name;
}

public getFontname(): string {
return 'Arial';
}
}

export class LightTheme implements Theme {
public name = 'light';

public getStylename(): string {
return this.name;
}

public getFontname(): string {
return 'Comic Sans MS';
}
}
>>> theme.creator.tsimport { DarkTheme, LightTheme, RedTheme, Theme } from './theme.model';

export abstract class ThemeCreator {
public abstract factoryMethod(): Theme;
}

export class DarkThemeCreator extends ThemeCreator {
public factoryMethod(): Theme {
return new DarkTheme();
}
}

export class LightThemeCreator extends ThemeCreator {
public factoryMethod(): Theme {
return new LightTheme();
}
}
>>> theme.facade.tsimport { DarkThemeCreator, LightThemeCreator, RedThemeCreator, ThemeCreator } from './theme.creator';
import { Theme } from './theme.model';

export function addAvailableThemesToList(): Theme[] {
return [
clientCode(new DarkThemeCreator()),
clientCode(new LightThemeCreator()),
clientCode(new RedThemeCreator())
];
}

function clientCode(creator: ThemeCreator): Theme {
return creator.factoryMethod();
}
>>> theming-with-factory-method.component.tsexport class ThemingWithFactoryMethodComponent implements OnInit {

public selectedTheme: Theme;
public themes: Theme[];

constructor() {
this.themes = new Array<Theme>();
}

ngOnInit(): void {
this.themes = addAvailableThemesToList();
}

public selectionChanged(): void {
// read abstract class
console.log('changed theme to ' + this.selectedTheme.name);
}

public trackByFn(index, item: Theme): string {
return item.name;
}
}
<div class="container">
<h1>Select attribute with factory method</h1>

<mat-form-field appearance="fill">
<mat-label>Theme selection</mat-label>
<mat-select [(ngModel)]="selectedTheme" (selectionChange)="selectionChanged()">
<mat-option *ngFor="let theme of themes; trackBy: trackByFn" [value]="theme">
{{ theme.name }}
</mat-option>
</mat-select>
</mat-form-field>

</div>

Conclusion

With the family of creational patterns, especially the Factory Method Pattern of this example, you can refactor switch statements to comply with the Open-Closed-Principle.

Written by

Fullstack Software Engineer | Trained Mathematical-Technical Software Developer | Employee at Energy2market

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store