Swiftorial Logo
Home
Swift Lessons
Tutorials
Learn More
Career
Resources

Angular Design Patterns

Design patterns provide solutions to common software design problems and help developers create scalable and maintainable applications. This guide covers various design patterns used in Angular applications.

Singleton Pattern

The Singleton pattern ensures that a class has only one instance and provides a global point of access to it. In Angular, services are singletons by default when provided in the root:

// my-service.service.ts
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class MyService {
  private static instance: MyService;

  private constructor() {
    if (MyService.instance) {
      throw new Error('You can only create one instance!');
    }
    MyService.instance = this;
  }

  // Service methods here
}

Factory Pattern

The Factory pattern provides a way to create objects without specifying the exact class of object that will be created. In Angular, you can use factories for dependency injection:

// my-factory.ts
import { InjectionToken } from '@angular/core';

export const MY_SERVICE_TOKEN = new InjectionToken('MY_SERVICE_TOKEN');

export function myServiceFactory() {
  return new MyService();
}

// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { MY_SERVICE_TOKEN, myServiceFactory } from './my-factory';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule],
  providers: [{ provide: MY_SERVICE_TOKEN, useFactory: myServiceFactory }],
  bootstrap: [AppComponent]
})
export class AppModule { }

Observer Pattern

The Observer pattern allows objects to be notified of changes in other objects. In Angular, you can use RxJS observables to implement the Observer pattern:

// my-service.service.ts
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class MyService {
  private subject = new Subject();

  emit(value: any) {
    this.subject.next(value);
  }

  getObservable() {
    return this.subject.asObservable();
  }
}

// my-component.component.ts
import { Component, OnInit } from '@angular/core';
import { MyService } from './my-service.service';

@Component({
  selector: 'app-my-component',
  templateUrl: './my-component.component.html',
  styleUrls: ['./my-component.component.css']
})
export class MyComponent implements OnInit {
  constructor(private myService: MyService) {}

  ngOnInit() {
    this.myService.getObservable().subscribe(value => {
      console.log('Received value:', value);
    });
  }

  emitValue() {
    this.myService.emit('Hello, Observer Pattern!');
  }
}

Decorator Pattern

The Decorator pattern allows behavior to be added to an individual object, dynamically. In Angular, decorators are used to attach metadata to classes, methods, and properties:

// my-component.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-my-component',
  templateUrl: './my-component.component.html',
  styleUrls: ['./my-component.component.css']
})
export class MyComponent {
  // Component logic here
}

Facade Pattern

The Facade pattern provides a simplified interface to a complex subsystem. In Angular, you can create a facade service to encapsulate complex logic and provide a simple API:

// my-facade.service.ts
import { Injectable } from '@angular/core';
import { MyService } from './my-service.service';

@Injectable({
  providedIn: 'root'
})
export class MyFacadeService {
  constructor(private myService: MyService) {}

  doSomething() {
    // Complex logic
    this.myService.emit('Facade Pattern');
  }
}

// my-component.component.ts
import { Component, OnInit } from '@angular/core';
import { MyFacadeService } from './my-facade.service';

@Component({
  selector: 'app-my-component',
  templateUrl: './my-component.component.html',
  styleUrls: ['./my-component.component.css']
})
export class MyComponent implements OnInit {
  constructor(private myFacade: MyFacadeService) {}

  ngOnInit() {
    this.myFacade.doSomething();
  }
}

Key Points

  • Design patterns provide solutions to common software design problems.
  • The Singleton pattern ensures a class has only one instance and provides a global point of access to it.
  • The Factory pattern provides a way to create objects without specifying the exact class of object that will be created.
  • The Observer pattern allows objects to be notified of changes in other objects using RxJS observables.
  • The Decorator pattern adds behavior to an individual object dynamically using Angular decorators.
  • The Facade pattern provides a simplified interface to a complex subsystem.

Conclusion

Using design patterns in Angular helps you build scalable and maintainable applications. By implementing patterns such as Singleton, Factory, Observer, Decorator, and Facade, you can solve common design problems and improve the structure of your code. Happy coding!