Strategy

Posted by Dustin Boston .


The strategy pattern decouples the client from its implementation, allowing the client to use different algorithms interchangeably without knowing the specifics of each algorithm’s implementation.

« V C « E A e C a o C m l . o l n o a g g n i t e n i o . t d e . v c l r e a x g a r V i V x t t . l e a t a t o I i t l h l » r n i d e i m i t s a S d I d e V t t a n a r a o r t t t f l r a i e e a i - t o r ( c d > e n f ) e ( V g S a ( ) a y t c ) l A r e i » a ( d t ) a s t t e r ( « P A e a ) C h l . t o o g g e n n o . g c e r y r V i V / e a t a v t l h l a e i m i l S d I d i t a n a d r t t t a a i e e t t o r ( o e n f ) r g S a y t c B r e » a ( t ) « V A e « M A e S a l . C o l . t l g g o n g g r i o . n t o . a d r c h r t a i V r V i V e t t a e a t a g i h l t l h l y o m i e i m i » n I d S d I d S n a t a n a t t t r t t t r e e a i e e a r ( t o r ( t f ) e n f ) e a g S a g c y t c y e C r e ( » a ( ) t )

Source Code Listing

code.ts

type Strategy<T> = {
  validate(value: T): boolean;
  algorithmInterface(): void;
};

// ConcreteStrategyA
export class EmailValidationStrategy implements Strategy<string> {
  validate(value: string): boolean {
    return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.exec(value) !== null;
  }

  algorithmInterface(): void {
    console.log("ConcreteStrategyA");
  }
}

// ConcreteStrategyB
export class PhoneValidationStrategy implements Strategy<string> {
  validate(value: string): boolean {
    return /^(\+?\d{1,2}[-\s]?|)(?:\d{3}[-\s]?){2}\d{4}$/.exec(value) !== null;
  }

  algorithmInterface(): void {
    console.log("ConcreteStrategyB");
  }
}

// ConcreteStrategyC
export class MonthValidationStrategy implements Strategy<string> {
  validate(value: string): boolean {
    return (
      /^(Jan|Feb|March|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)$/.exec(value) !==
      null
    );
  }

  algorithmInterface(): void {
    console.log("ConcreteStrategyC");
  }
}

export class Context {
  constructor(private readonly strategy: Strategy<string>) {}

  // ContextInterface(): void {
  //   this.strategy.algorithmInterface();
  // }

  validate(value: string): boolean {
    return this.strategy.validate(value);
  }
}

test.ts

import {describe, test, expect} from "bun:test";
import {
  Context,
  EmailValidationStrategy,
  MonthValidationStrategy,
  PhoneValidationStrategy,
} from "./code.ts";

describe("Strategy Pattern", () => {
  test("should validate email", () => {
    const context = new Context(new EmailValidationStrategy());
    const result = context.validate("no-reply@email.clutter");
    expect(result).toBe(true);
  });

  test("should validate phone", () => {
    const context = new Context(new PhoneValidationStrategy());
    const result = context.validate("123-456-7890");
    expect(result).toBe(true);
  });

  test("should validate month", () => {
    const context = new Context(new MonthValidationStrategy());
    const result = context.validate("Jan");
    expect(result).toBe(true);
  });
});