DEV Community

Cover image for How Efficient is Delegation Design Pattern in Angular
Kenneth Tengco
Kenneth Tengco

Posted on

How Efficient is Delegation Design Pattern in Angular

A quick introduction to Delegation Design Pattern. As the name implies, it delegates the context to a Delegate.


“You may not need State Management”


Intent

  • To reduce chaotic dependencies between the context.

  • A team of developers can work simultaneously on what they want to do with the context.

  • Prevents direct public method calls from the Delegator since it’s only an internal class used by the Event class.

  • Prevents changing of the original context.

  • Also, it resolves communication in micro frontend architecture.

How to Implement

  • Declare an interface of the Delegator and define the desired method between the Delegator and Delegate.

ICovid19TrackerDelegateEvent

  • Declare the Delegator class and implement the interface. Make sure the Delegator class accepts a Map Object of Delegate from the constructor.

Covid19TrackerDelegator

  • Declare the Event class as Injectable and Singleton. Then in the constructor, instantiate the Delegator class and assign it to a private property.

Covid19TrackerEventService

  • Make sure the Event class has:

A Map Object of Delegate, so we can pass it when instantiating the Delegator class.<br>

A Map Object of Delegate, so we can pass it when instantiating the Delegator class.

A public method to register and unregister the Delegate.

A public method to register and unregister the Delegate.

  • Declare the Delegate and implement the Delegator interface. To register the Delegate, use the Event class as a Dependency Injection of the Delegate and call the register method.

Covid19FeedComponent


This is the Abstract class and implements the interface of the Delegator for code reuse.

This is the Abstract class and implements the interface of the Delegator for code reuse.

In the constructor, register the Delegate using the Event class and save the Delegate ID. We will use the Delegate ID to unregister when the component is going to be destroyed.

In the constructor, register the Delegate using the Event class and save the Delegate ID. We will use the Delegate ID to unregister when the component is going to be destroyed.

These are the methods that can be reused for the Concrete class.

These are the methods that can be reused for the Concrete class.

Below is an example of Concrete classes that serve as the Delegate.

This Concrete class is used to display the total number of active cases and the last update on Covid19.

This Concrete class is used to display the total number of active cases and the last update on Covid19.

Covid19FeedSummaryComponent

Covid19FeedSummaryComponent

This Concrete class is used to display the top five countries with the highest number of active cases of Covid19.

This Concrete class is used to display the top five countries with the highest number of active cases of Covid19.

Covid19FeedCountryRankComponent

Covid19FeedCountryRankComponent

Below is a Concrete class used to display the list of countries grouped by continent with the highest number of active cases of Covid19.

import { Component, ChangeDetectionStrategy } from "@angular/core";

import { Covid19Statistic } from "../../covid19-tracker/model";
import { Covid19GroupStatisticAdapter } from "../../model";
import { Covid19FeedComponent } from "../covid19-feed.component";

type GroupKey = string;

@Component({
    selector: "covid19-feed-list-component",
    templateUrl: "./covid19-feed.list.component.html",
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class Covid19FeedListComponent extends Covid19FeedComponent {

    private readonly listDictionary: Map<GroupKey, Covid19Statistic[]> = new Map();
    private readonly groupDictionary: Map<GroupKey, Covid19GroupStatisticAdapter> = new Map();

    public groups: Covid19GroupStatisticAdapter[] = [];

    public override statisticUpdate(statistic: Covid19Statistic[]): void {
        this.updateGroupList(statistic);
        this.sortGroupList();
    }

    public getList(group: Covid19Statistic): Covid19Statistic[] {
        const groupKey = this.generateGroupKey(group);
        const list = this.listDictionary.get(groupKey);

        if (list === undefined) {
            throw new Error(`'${groupKey}' groupKey is not found in the dictionary.`);
        }

        return list;
    }

    private updateGroupList(statistic: Covid19Statistic[]) {
        for (const stat of statistic) {
            if (stat.continent === "All") {
                continue;
            }

            const groupKey = this.generateGroupKey(stat);
            const group = this.listDictionary.get(stat.continent);

            if (stat.continent === stat.country) {
                this.groupDictionary.set(groupKey, new Covid19GroupStatisticAdapter(stat));

                if (group === undefined) {
                    this.listDictionary.set(groupKey, []);
                }

                continue;
            }

            if (group === undefined) {
                this.listDictionary.set(groupKey, [stat]);
            } else {
                group.push(stat);
            }
        }
    }

    private generateGroupKey(statistic: Covid19Statistic): string {
        return statistic.continent;
    }

    private sortGroupList(): void {
        this.groups = Array.from(this.groupDictionary.values())
            .sort((compareA: Covid19GroupStatisticAdapter, compareB: Covid19GroupStatisticAdapter) => {
                return compareB.statistic.cases.active - compareA.statistic.cases.active;
            });

        for (const list of this.listDictionary.values()) {
            list.sort((compareA: Covid19Statistic, compareB: Covid19Statistic) => {
                return compareB.cases.active - compareA.cases.active;
            });
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Covid19FeedListComponent

Covid19FeedListComponent


  • Finally, we will use the Event class to delegate some context.

This component has three Delegate components in the template, see the figure below.

This component has three Delegate components in the template, see the figure below.

Component template

On initialization of the component, it will call the load statistics method. This is an event method that delegates some context. Let’s review the method quickly.

Class method review

  • The statistic loading method accepts three states. LOADING, SUCCESS, and ERROR and delegate this context to the (concrete class) Delegate. The Delegate will receive this state and do what it needs to be done.

  • The statistic update method accepts an Array of “Covid19Statistic” (the model) class instances and delegates this context to the (concrete class) Delegate. The same thing, the Delegate will receive this state and do what needs to be done.


That technically sums up how Delegation Design Pattern works. Below is the output of the application.

The API used is for testing purposes only, so these numbers are not real.

The API used is for testing purposes only, so these numbers are not real.

Pros

  • The Delegate has clear method definitions of the Delegator to handle the context being delegated.

  • The files are easy to track because of the implementation of the interface of the Delegator.

  • Open/Closed Principle – Easy to introduce a new Delegate without breaking the existing client code.

  • Single Responsibility Principle – The communication is in one place that comes from the Event class, and using the Delegator interface makes it much easier to comprehend and maintain.

  • Can reduce coupling between various components or classes.

  • Can reuse the individual components or classes much more manageable.

Cons

  • When the interface of the Delegator is somehow abused, meaning it does not have segregation of interfaces or different behaviors are in one place.

Conclusion

  • Delegator Interface – used to describe the methods to communicate between the Delegator and Delegate class.

  • Delegator Class – implements the Delegator interface and delegates the context to the Delegate and is only used internally by the Event class.

  • Event Class – uses the Delegator class as internal and calls its method to delegate the context.

  • Delegate Class – implements the Delegator interface and uses the Event class to register or unregister itself. Also, it can call some event method from the Event class to trigger some Delegator method.


By the way, did you notice the Adapter model? If you don’t know how it works or need more explanation about it, subscribe now to get more updates. 😉

Adapter model


Thanks for reading!

Top comments (0)