Design Patterns Analysis: Microsoft Cognitive Services Speech SDK (JavaScript)

The microsoft/cognitive-services-speech-sdk-js repository demonstrates several professional design patterns. Here’s a detailed breakdown:


1. Singleton Pattern

Pattern Description

The Singleton pattern ensures only one instance of a class exists throughout the application and provides global access to that instance.

Implementation in the Code

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
 
import { ArgumentNullError } from "./Error.js";
import { EventSource } from "./EventSource.js";
import { IEventSource } from "./IEventSource.js";
import { PlatformEvent } from "./PlatformEvent.js";
 
export class Events {
    private static privInstance: IEventSource<PlatformEvent> = new EventSource<PlatformEvent>();
 
    public static setEventSource(eventSource: IEventSource<PlatformEvent>): void {
        if (!eventSource) {
            throw new ArgumentNullError("eventSource");
        }
 
        Events.privInstance = eventSource;
    }
 
    public static get instance(): IEventSource<PlatformEvent> {
        return Events.privInstance;
    }
}

How It Works

  • Single Instance: privInstance is a static member initialized with a new EventSource object
  • Global Access: The static instance getter provides global access
  • Setter Control: setEventSource() allows replacing the instance (useful for testing)

Why It’s Used

  • Provides a centralized event management system across the entire SDK
  • Ensures all components share the same event dispatcher
  • Allows for dependency injection and testing

Simple Example for Your Repository

// Simple Logger Singleton Pattern
export class Logger {
    private static privInstance: Logger = new Logger();
    private logs: string[] = [];
 
    private constructor() {
        // Private constructor prevents instantiation
    }
 
    public static getInstance(): Logger {
        return Logger.privInstance;
    }
 
    public log(message: string): void {
        this.logs.push(message);
        console.log(message);
    }
 
    public getLogs(): string[] {
        return this.logs;
    }
}
 
// Usage
const logger = Logger.getInstance();
logger.log("Application started");

2. Factory Pattern

Pattern Description

The Factory pattern creates objects without specifying the exact classes to instantiate, allowing the system to determine which concrete class to instantiate.

Implementation in the Code

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
 
import { IConnection } from "../common/Exports.js";
import { AuthInfo } from "./IAuthentication.js";
import { RecognizerConfig } from "./RecognizerConfig.js";
 
export interface IConnectionFactory {
    create(
        config: RecognizerConfig,
        authInfo: AuthInfo,
        connectionId?: string): Promise<IConnection>;
}
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
 
import {
    ServicePropertiesPropertyName,
} from "../common.speech/Exports.js";
import { ConnectionRedirectEvent, Events, IConnection, IStringDictionary } from "../common/Exports.js";
import { PropertyId } from "../sdk/Exports.js";
import { AuthInfo, IConnectionFactory, RecognizerConfig } from "./Exports.js";
import { QueryParameterNames } from "./QueryParameterNames.js";
 
export abstract class ConnectionFactoryBase implements IConnectionFactory {
 
    public static getHostSuffix(region: string): string {
        /*...*/
    }
 
    public abstract create(
        config: RecognizerConfig,
        authInfo: AuthInfo,
        connectionId?: string): Promise<IConnection>;
 
    protected setCommonUrlParams(
        config: RecognizerConfig,
        queryParams: IStringDictionary<string>,
        endpoint: string): void {
        /*...*/
    }
 
    protected setUrlParameter(
        propId: PropertyId,
        parameterName: string,
        config: RecognizerConfig,
        queryParams: IStringDictionary<string>,
        endpoint: string): void {
        /*...*/
    }

How It Works

  • Interface Definition: IConnectionFactory defines the contract for creating connections
  • Abstract Base: ConnectionFactoryBase provides common functionality
  • Concrete Implementations: Specific factories (like SpeechConnectionFactory, DialogConnectionFactory) implement the interface

Why It’s Used

  • Allows different types of recognizers (speech, translation, dialog) to use their own connection factories
  • Decouples the creation logic from the usage logic
  • Makes the system extensible for new connection types

Simple Example for Your Repository

// Factory Pattern Example
interface IDatabase {
    connect(): Promise<void>;
    query(sql: string): Promise<any>;
}
 
class MySQLDatabase implements IDatabase {
    async connect(): Promise<void> {
        console.log("Connecting to MySQL...");
    }
 
    async query(sql: string): Promise<any> {
        return { result: "MySQL result" };
    }
}
 
class PostgresDatabase implements IDatabase {
    async connect(): Promise<void> {
        console.log("Connecting to PostgreSQL...");
    }
 
    async query(sql: string): Promise<any> {
        return { result: "PostgreSQL result" };
    }
}
 
class DatabaseFactory {
    static create(type: "mysql" | "postgres"): IDatabase {
        switch (type) {
            case "mysql":
                return new MySQLDatabase();
            case "postgres":
                return new PostgresDatabase();
            default:
                throw new Error("Unknown database type");
        }
    }
}
 
// Usage
const db = DatabaseFactory.create("mysql");
await db.connect();
const result = await db.query("SELECT * FROM users");

3. Template Method Pattern

Pattern Description

The Template Method pattern defines the skeleton of an algorithm in a base class, allowing subclasses to override specific steps.

Implementation in the Code

    //
    // ################################################################################################################
    // IMPLEMENTATION.
    // Move to independent class
    // ################################################################################################################
    //
    protected abstract createRecognizerConfig(speechConfig: SpeechServiceConfig): RecognizerConfig;
 
    // Creates the correct service recognizer for the type
    protected abstract createServiceRecognizer(
        authentication: IAuthentication,
        connectionFactory: IConnectionFactory,
        audioConfig: AudioConfig,
        recognizerConfig: RecognizerConfig): ServiceRecognizerBase;
 
    // Does the generic recognizer setup that is common across all recognizer types.
    protected implCommonRecognizerSetup(): void {
 
        let osPlatform = (typeof window !== "undefined") ? "Browser" : "Node";
        let osName = "unknown";
        let osVersion = "unknown";
 
        if (typeof navigator !== "undefined") {
            osPlatform = osPlatform + "/" + navigator.platform;
            osName = navigator.userAgent;
            osVersion = navigator.appVersion;
        }
 
        const recognizerConfig = this.createRecognizerConfig(
            new SpeechServiceConfig(
                new Context(new OS(osPlatform, osName, osVersion))));
 
        this.privReco = this.createServiceRecognizer(
            Recognizer.getAuth(this.privProperties, this.tokenCredential),
            this.privConnectionFactory,
            this.audioConfig,
            recognizerConfig);
    }
 
    protected async recognizeOnceAsyncImpl(recognitionMode: RecognitionMode): Promise<SpeechRecognitionResult> {
        Contracts.throwIfDisposed(this.privDisposed);
        const ret: Deferred<SpeechRecognitionResult> = new Deferred<SpeechRecognitionResult>();
 
        await this.implRecognizerStop();
        await this.privReco.recognize(recognitionMode, ret.resolve, ret.reject);
        const result: SpeechRecognitionResult = await ret.promise;
        await this.implRecognizerStop();
 
        return result;
 
    }
 
    protected async startContinuousRecognitionAsyncImpl(recognitionMode: RecognitionMode): Promise<void> {
        Contracts.throwIfDisposed(this.privDisposed);
 
        await this.implRecognizerStop();
        await this.privReco.recognize(recognitionMode, undefined, undefined);
    }
 
    protected async stopContinuousRecognitionAsyncImpl(): Promise<void> {
        Contracts.throwIfDisposed(this.privDisposed);
        await this.implRecognizerStop();
    }
 
    protected async implRecognizerStop(): Promise<void> {
        if (this.privReco) {
            // Get timeout property - undefined/empty means no timeout (existing behavior)
            const timeoutProperty = this.privProperties.getProperty(
                PropertyId.Recognizer_StopTimeoutMs,
                undefined
            );
    protected createRecognizerConfig(speechConfig: SpeechServiceConfig): RecognizerConfig {
        return new RecognizerConfig(speechConfig, this.privProperties);
    }
 
    protected createServiceRecognizer(
        authentication: IAuthentication,
        connectionFactory: IConnectionFactory,
        audioConfig: AudioConfig,
        recognizerConfig: RecognizerConfig): ServiceRecognizerBase {
        const configImpl: AudioConfigImpl = audioConfig as AudioConfigImpl;
        return new SpeechServiceRecognizer(authentication, connectionFactory, configImpl, recognizerConfig, this);
    }
}

How It Works

  • Base Class: Recognizer defines the algorithm skeleton (implCommonRecognizerSetup, recognizeOnceAsyncImpl)
  • Abstract Methods: Subclasses must implement createRecognizerConfig() and createServiceRecognizer()
  • Subclass Implementations: SpeechRecognizer, TranslationRecognizer provide specific implementations

Why It’s Used

  • Different recognizer types share common recognition logic but differ in configuration creation
  • Reduces code duplication across different recognizer classes
  • Makes it easy to add new recognizer types

Simple Example for Your Repository

// Template Method Pattern Example
abstract class DataProcessor {
    // Template method - defines the algorithm skeleton
    public process(data: string): void {
        const validated = this.validate(data);
        const transformed = this.transform(validated);
        this.save(transformed);
    }
 
    protected validate(data: string): string {
        if (!data || data.trim().length === 0) {
            throw new Error("Data cannot be empty");
        }
        return data;
    }
 
    // Abstract method - subclasses must implement
    protected abstract transform(data: string): string;
 
    protected save(data: string): void {
        console.log(`Saving: ${data}`);
    }
}
 
class CSVProcessor extends DataProcessor {
    protected transform(data: string): string {
        return data.split(',').join('|');
    }
}
 
class JSONProcessor extends DataProcessor {
    protected transform(data: string): string {
        return JSON.stringify(JSON.parse(data), null, 2);
    }
}
 
// Usage
const csvProcessor = new CSVProcessor();
csvProcessor.process("name,age,city");
 
const jsonProcessor = new JSONProcessor();
jsonProcessor.process('{"name":"John","age":30}');

4. Observer Pattern (Event-Driven)

Pattern Description

The Observer pattern defines a one-to-many dependency where when one object changes state, all its dependents are notified automatically.

Implementation in the Code

export class EventSource<TEvent extends PlatformEvent> implements IEventSource<TEvent> {
    private privEventListeners: IStringDictionary<(event: TEvent) => void> = {};
    private privMetadata: IStringDictionary<string>;
    private privIsDisposed: boolean = false;
    private privConsoleListener: IDetachable = undefined;
 
    public constructor(metadata?: IStringDictionary<string>) {
        this.privMetadata = metadata;
    }
 
    public onEvent(event: TEvent): void {
        if (this.isDisposed()) {
            throw (new ObjectDisposedError("EventSource"));
        }
 
        if (this.metadata) {
            for (const paramName in this.metadata) {
                if (paramName) {
                    if (event.metadata) {
                        if (!event.metadata[paramName]) {
                            event.metadata[paramName] = this.metadata[paramName];
                        }
                    }
                }
            }
        }
 
        for (const eventId in this.privEventListeners) {
            if (eventId && this.privEventListeners[eventId]) {
                this.privEventListeners[eventId](event);
            }
        }
    }
 
    public attach(onEventCallback: (event: TEvent) => void): IDetachable {
        const id = createNoDashGuid();
        this.privEventListeners[id] = onEventCallback;
        return {
            detach: (): Promise<void> => {
                delete this.privEventListeners[id];
                return Promise.resolve();
            },
        };
    }
 
    public attachListener(listener: IEventListener<TEvent>): IDetachable {
        return this.attach((e: TEvent): void => listener.onEvent(e));
    }
 
    public attachConsoleListener(listener: IEventListener<TEvent>): IDetachable {
        if (!!this.privConsoleListener) {
            void this.privConsoleListener.detach();
        }
        this.privConsoleListener = this.attach((e: TEvent): void => listener.onEvent(e));
        return this.privConsoleListener;
    }
 
    public isDisposed(): boolean {
        return this.privIsDisposed;
    }
 
    public dispose(): void {
        this.privEventListeners = null;
        this.privIsDisposed = true;
    }
 
    public get metadata(): IStringDictionary<string> {
        return this.privMetadata;
    }
}

How It Works

  • Event Source: EventSource manages a collection of event listeners
  • Attach Method: Listeners subscribe to events using attach()
  • Notify: onEvent() notifies all registered listeners when an event occurs
  • Detachable: Listeners can unsubscribe using the returned IDetachable interface

Why It’s Used

  • Decouples event producers from consumers
  • Enables loose coupling between components
  • Allows multiple listeners to respond to the same event

Simple Example for Your Repository

// Observer/Event Pattern Example
interface IObserver {
    onNotify(message: string): void;
}
 
class EmailNotifier implements IObserver {
    onNotify(message: string): void {
        console.log(`Email sent: ${message}`);
    }
}
 
class SMSNotifier implements IObserver {
    onNotify(message: string): void {
        console.log(`SMS sent: ${message}`);
    }
}
 
class EventEmitter {
    private observers: IObserver[] = [];
 
    subscribe(observer: IObserver): void {
        this.observers.push(observer);
    }
 
    unsubscribe(observer: IObserver): void {
        this.observers = this.observers.filter(o => o !== observer);
    }
 
    emit(message: string): void {
        this.observers.forEach(observer => observer.onNotify(message));
    }
}
 
// Usage
const emitter = new EventEmitter();
const email = new EmailNotifier();
const sms = new SMSNotifier();
 
emitter.subscribe(email);
emitter.subscribe(sms);
emitter.emit("Important notification");

5. Strategy Pattern

Pattern Description

The Strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable.

Implementation in the Code

The SDK uses Strategy through different adapter implementations:

export abstract class SynthesisAdapterBase implements IDisposable {
    protected privSynthesisTurn: SynthesisTurn;
    protected privConnectionId: string;
    protected privSynthesizerConfig: SynthesizerConfig;
    protected privSynthesizer: Synthesizer;
    protected privSuccessCallback: (e: SpeechSynthesisResult) => void;
    protected privErrorCallback: (e: string) => void;
 
    public get synthesizerConfig(): SynthesizerConfig {
        return this.privSynthesizerConfig;
    }
 
    protected speakOverride: (ssml: string, requestId: string, sc: (e: SpeechSynthesisResult) => void, ec: (e: string) => void) => void = undefined;
 
    // Called when telemetry data is sent to the service.
    // Used for testing Telemetry capture.
    public static telemetryData: (json: string) => void;
    public static telemetryDataEnabled: boolean = true;
 
    public set activityTemplate(messagePayload: string) {
        this.privActivityTemplate = messagePayload;
    }
    public get activityTemplate(): string {
        return this.privActivityTemplate;
    }
 
    protected receiveMessageOverride: () => void = undefined;
 
    protected connectImplOverride: (isUnAuthorized: boolean) => void = undefined;
 
    protected configConnectionOverride: (connection: IConnection) => Promise<IConnection> = undefined;
 
    public set audioOutputFormat(format: AudioOutputFormatImpl) {
        this.privAudioOutputFormat = format;
        this.privSynthesisTurn.audioOutputFormat = format;
        if (this.privSessionAudioDestination !== undefined) {
            this.privSessionAudioDestination.format = format;
        }
        if (this.synthesisContext !== undefined) {
            this.synthesisContext.audioOutputFormat = format;
        }
    }

Why It’s Used

  • Different synthesis strategies (REST vs WebSocket) can be swapped at runtime
  • Allows customization through override properties
  • Enables flexible behavior modification without changing core code

Simple Example for Your Repository

// Strategy Pattern Example
interface IPaymentStrategy {
    pay(amount: number): Promise<boolean>;
}
 
class CreditCardPayment implements IPaymentStrategy {
    async pay(amount: number): Promise<boolean> {
        console.log(`Processing credit card payment of $${amount}`);
        return true;
    }
}
 
class PayPalPayment implements IPaymentStrategy {
    async pay(amount: number): Promise<boolean> {
        console.log(`Processing PayPal payment of $${amount}`);
        return true;
    }
}
 
class CryptoPayment implements IPaymentStrategy {
    async pay(amount: number): Promise<boolean> {
        console.log(`Processing crypto payment of $${amount}`);
        return true;
    }
}
 
class ShoppingCart {
    private strategy: IPaymentStrategy;
 
    setPaymentStrategy(strategy: IPaymentStrategy): void {
        this.strategy = strategy;
    }
 
    async checkout(amount: number): Promise<void> {
        if (!this.strategy) {
            throw new Error("Payment strategy not set");
        }
        const success = await this.strategy.pay(amount);
        if (success) {
            console.log("Payment successful!");
        }
    }
}
 
// Usage
const cart = new ShoppingCart();
cart.setPaymentStrategy(new CreditCardPayment());
await cart.checkout(99.99);
 
cart.setPaymentStrategy(new PayPalPayment());
await cart.checkout(49.99);

6. Deferred/Promise Pattern (Async Handling)

Implementation in the Code

export class Deferred<T> implements IDeferred<T> {
    private privPromise: Promise<T>;
    private privResolve: (value?: T | PromiseLike<T>) => void;
    private privReject: (reason?: any) => void;
 
    public constructor() {
        // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
        this.privPromise = new Promise<T>((resolve: (value: T) => void, reject: (reason: any) => void) => {
            this.privResolve = resolve;
            this.privReject = reject;
        });
    }
 
    public get promise(): Promise<T> {
        return this.privPromise;
    }
 
    public resolve = (result: T | Promise<T>): Deferred<T> => {
        this.privResolve(result);
        return this;
    };
 
    public reject = (error: string): Deferred<T> => {
        this.privReject(error);
        return this;
    };
}

Simple Example

// Deferred Pattern Example
class Deferred<T> {
    promise: Promise<T>;
    resolve!: (value: T) => void;
    reject!: (reason?: any) => void;
 
    constructor() {
        this.promise = new Promise<T>((resolve, reject) => {
            this.resolve = resolve;
            this.reject = reject;
        });
    }
}
 
// Usage
async function fetchUser(id: number): Promise<{ id: number; name: string }> {
    const deferred = new Deferred<{ id: number; name: string }>();
 
    setTimeout(() => {
        if (id > 0) {
            deferred.resolve({ id, name: `User ${id}` });
        } else {
            deferred.reject("Invalid user ID");
        }
    }, 1000);
 
    return deferred.promise;
}
 
const user = await fetchUser(1);
console.log(user);

Now let me search for more specific patterns:Perfect! Now I have comprehensive information about the missing patterns. Let me provide you with a detailed analysis of the patterns you may have missed.

1. Decorator Pattern (Proxy/Wrapper)

Pattern Description

The Decorator pattern allows you to attach additional responsibilities to an object dynamically, wrapping it to add new functionality without modifying the original class.

Implementation in the Code

export class WebsocketConnection implements IConnection {
 
    private privUri: string;
    private privMessageFormatter: IWebsocketMessageFormatter;
    private privConnectionMessageAdapter: WebsocketMessageAdapter; // ← Wraps another object
    private privId: string;
    private privIsDisposed: boolean = false;
 
    public constructor(
        uri: string,
        queryParameters: IStringDictionary<string>,
        headers: IStringDictionary<string>,
        messageFormatter: IWebsocketMessageFormatter,
        proxyInfo: ProxyInfo,
        enableCompression: boolean = false,
        connectionId?: string) {
        /*...*/
        this.privConnectionMessageAdapter = new WebsocketMessageAdapter(
            this.privUri,
            this.id,
            this.privMessageFormatter,
            proxyInfo,
            headers,
            enableCompression);
    }
 
    // Delegates to wrapped object
    public async dispose(): Promise<void> {
        this.privIsDisposed = true;
        if (this.privConnectionMessageAdapter) {
            await this.privConnectionMessageAdapter.close();
        }
    }
 
    public open(): Promise<ConnectionOpenResponse> {
        return this.privConnectionMessageAdapter.open();
    }
 
    public send(message: ConnectionMessage): Promise<void> {
        return this.privConnectionMessageAdapter.send(message);
    }
 
    public read(): Promise<ConnectionMessage> {
        return this.privConnectionMessageAdapter.read();
    }
 
    public get events(): EventSource<ConnectionEvent> {
        return this.privConnectionMessageAdapter.events;
    }
}

How It Works

  • Wrapper Class: WebsocketConnection wraps WebsocketMessageAdapter
  • Delegation: All methods delegate to the wrapped adapter
  • Added Functionality: WebsocketConnection adds ID management, disposal tracking, and URI handling
  • Same Interface: Both implement IConnection

Why It’s Used

  • Adds HTTP handling layer on top of WebSocket adapter
  • Separates concerns: message formatting vs. connection management
  • Allows easy swapping of implementations
  • Maintains clean API while adding functionality

Simple Example for Your Repository

// Core functionality
interface IUserService {
    getUser(id: number): User;
}
 
class BasicUserService implements IUserService {
    getUser(id: number): User {
        return { id, name: "John" };
    }
}
 
// Decorator/Wrapper - adds caching without modifying original
class CachedUserService implements IUserService {
    private cache = new Map<number, User>();
    
    constructor(private userService: IUserService) {}
 
    getUser(id: number): User {
        if (this.cache.has(id)) {
            console.log(`Getting user ${id} from cache`);
            return this.cache.get(id)!;
        }
 
        const user = this.userService.getUser(id);
        this.cache.set(id, user);
        return user;
    }
}
 
// Another Decorator - adds logging
class LoggingUserService implements IUserService {
    constructor(private userService: IUserService) {}
 
    getUser(id: number): User {
        console.log(`Fetching user ${id}`);
        const user = this.userService.getUser(id);
        console.log(`User fetched: ${user.name}`);
        return user;
    }
}
 
// Usage - stack decorators
let service: IUserService = new BasicUserService();
service = new CachedUserService(service);
service = new LoggingUserService(service);
 
const user = service.getUser(1); // Logs, checks cache, fetches

2. Builder Pattern

Pattern Description

The Builder pattern separates the construction of a complex object from its representation, allowing step-by-step object creation.

Implementation in the Code

export class DynamicGrammarBuilder {
 
    private privPhrases: string[];
    private privGrammars: string[];
    private privWeight: number = 1.0;
 
    // Step 1: Add phrases
    public addPhrase(phrase: string | string[]): void {
        if (!this.privPhrases) {
            this.privPhrases = [];
        }
 
        if (phrase instanceof Array) {
            this.privPhrases = this.privPhrases.concat(phrase);
        } else {
            this.privPhrases.push(phrase);
        }
    }
 
    // Step 2: Clear phrases
    public clearPhrases(): void {
        this.privPhrases = undefined;
    }
 
    // Step 3: Add reference grammars
    public addReferenceGrammar(grammar: string | string[]): void {
        if (!this.privGrammars) {
            this.privGrammars = [];
        }
 
        if (grammar instanceof Array) {
            this.privGrammars = this.privGrammars.concat(grammar);
        } else {
            this.privGrammars.push(grammar);
        }
    }
 
    // Step 4: Clear grammars
    public clearGrammars(): void {
        this.privGrammars = undefined;
    }
 
    // Step 5: Set weight
    public setWeight(weight: number): void {
        this.privWeight = weight;
    }
 
    // Step 6: Build the final object
    public generateGrammarObject(): Dgi {
        if (this.privGrammars === undefined && this.privPhrases === undefined) {
            return undefined;
        }
 
        const retObj: Dgi = {};
        retObj.referenceGrammars = this.privGrammars;
 
        if (undefined !== this.privPhrases && 0 !== this.privPhrases.length) {
            const retPhrases: Item[] = [];
 
            this.privPhrases.forEach((value: string): void => {
                retPhrases.push({
                    text: value,
                });
            });
 
            retObj.groups = [{ type: GroupType.Generic, items: retPhrases }];
            retObj.bias = this.privWeight;
        }
 
        return retObj;
    }
}

How It Works

  • Fluent Interface: Methods return void but modify internal state
  • Multi-Step Construction: Add phrases → add grammars → set weight → generate
  • Final Build: generateGrammarObject() returns the constructed object
  • Chainable Operations: Each method can be called in any order

Why It’s Used

  • Complex grammar objects require multiple configuration steps
  • Avoids constructor parameter explosion
  • Allows partial configuration
  • Makes code more readable and maintainable

Simple Example for Your Repository

// Builder Pattern Example
interface Query {
    select: string[];
    from: string;
    where?: string;
    orderBy?: string;
    limit?: number;
}
 
class SQLQueryBuilder {
    private query: Query = {
        select: [],
        from: ""
    };
 
    // Step 1: Select columns
    select(...columns: string[]): this {
        this.query.select = columns;
        return this;
    }
 
    // Step 2: From table
    from(table: string): this {
        this.query.from = table;
        return this;
    }
 
    // Step 3: Where condition
    where(condition: string): this {
        this.query.where = condition;
        return this;
    }
 
    // Step 4: Order by
    orderBy(column: string): this {
        this.query.orderBy = column;
        return this;
    }
 
    // Step 5: Limit
    limit(count: number): this {
        this.query.limit = count;
        return this;
    }
 
    // Step 6: Build the final query
    build(): string {
        let sql = `SELECT ${this.query.select.join(", ")} FROM ${this.query.from}`;
        if (this.query.where) sql += ` WHERE ${this.query.where}`;
        if (this.query.orderBy) sql += ` ORDER BY ${this.query.orderBy}`;
        if (this.query.limit) sql += ` LIMIT ${this.query.limit}`;
        return sql;
    }
}
 
// Usage - fluent interface
const query = new SQLQueryBuilder()
    .select("id", "name", "email")
    .from("users")
    .where("age > 18")
    .orderBy("name")
    .limit(10)
    .build();
 
console.log(query);
// SELECT id, name, email FROM users WHERE age > 18 ORDER BY name LIMIT 10

3. Disposable Pattern (Resource Management)

Pattern Description

The Disposable pattern ensures proper cleanup of resources by implementing a standardized cleanup interface.

Implementation in the Code

export interface IDisposable {
    /**
     * @returns {boolean}
     * @memberOf IDisposable
     */
    isDisposed(): boolean;
 
    /**
     * Performs cleanup operations on this instance
     * @param {string} [reason] - optional reason for disposing the instance.
     * @memberOf IDisposable
     */
    dispose(reason?: string): void;
}
export class EventSource<TEvent extends PlatformEvent> implements IEventSource<TEvent> {
    private privEventListeners: IStringDictionary<(event: TEvent) => void> = {};
    private privIsDisposed: boolean = false;
 
    // Check if disposed
    public isDisposed(): boolean {
        return this.privIsDisposed;
    }
 
    // Cleanup resources
    public dispose(): void {
        this.privEventListeners = null;
        this.privIsDisposed = true;
    }
 
    // Prevent operations on disposed objects
    public onEvent(event: TEvent): void {
        if (this.isDisposed()) {
            throw (new ObjectDisposedError("EventSource"));
        }
        // ... rest of implementation
    }
}

How It Works

  • Interface Contract: IDisposable defines the cleanup contract
  • Tracking State: privIsDisposed tracks disposal status
  • Guard Clauses: Methods check isDisposed() before operations
  • Resource Cleanup: dispose() releases all resources
  • Error Handling: Throws ObjectDisposedError if used after disposal

Why It’s Used

  • Prevents memory leaks and resource exhaustion
  • Standardizes cleanup across the SDK
  • Ensures proper event listener cleanup
  • Follows .NET/C# conventions developers recognize

Simple Example for Your Repository

// Disposable Pattern Example
interface IConnection {
    isDisposed(): boolean;
    dispose(): void;
    query(sql: string): Promise<any>;
}
 
class DatabaseConnection implements IConnection {
    private connection: any; // Internal connection
    private privIsDisposed: boolean = false;
 
    constructor(connectionString: string) {
        // Initialize connection
        this.connection = { isOpen: true };
    }
 
    isDisposed(): boolean {
        return this.privIsDisposed;
    }
 
    dispose(): void {
        if (this.privIsDisposed) return;
 
        // Clean up resources
        if (this.connection) {
            this.connection.isOpen = false;
            this.connection = null;
        }
 
        this.privIsDisposed = true;
        console.log("Database connection disposed");
    }
 
    async query(sql: string): Promise<any> {
        if (this.privIsDisposed) {
            throw new Error("Cannot use disposed connection");
        }
 
        if (!this.connection.isOpen) {
            throw new Error("Connection is not open");
        }
 
        // Execute query
        return { result: "data" };
    }
}
 
// Usage
const db = new DatabaseConnection("Server=localhost;Database=mydb");
try {
    const result = await db.query("SELECT * FROM users");
    console.log(result);
} finally {
    db.dispose(); // Always cleanup
}

4. Inheritance Hierarchy / Template Method Refinement

Pattern Description

Uses deep inheritance hierarchies to create specialized subclasses that inherit behavior from base classes.

Implementation in the Code

ServiceRecognizerBase (Abstract)
    ├── SpeechServiceRecognizer
    ├── TranslationServiceRecognizer
    ├── ConversationServiceRecognizer
    └── ConversationTranscriptionServiceRecognizer

Recognizer (Abstract)
    ├── SpeechRecognizer
    ├── TranslationRecognizer
    └── ConversationTranscriber
export class SpeechServiceRecognizer extends ServiceRecognizerBase {
 
    private privSpeechRecognizer: SpeechRecognizer;
 
    public constructor(
        authentication: IAuthentication,
        connectionFactory: IConnectionFactory,
        audioSource: IAudioSource,
        recognizerConfig: RecognizerConfig,
        speechRecognizer: SpeechRecognizer) {
        super(authentication, connectionFactory, audioSource, recognizerConfig, speechRecognizer);
        this.privSpeechRecognizer = speechRecognizer;
    }
 
    // Specialized implementation
    protected async processTypeSpecificMessages(connectionMessage: SpeechConnectionMessage): Promise<boolean> {
        // ... speech-specific message processing
    }
}

Why It’s Used

  • Specialized behavior for different recognizer types
  • Code reuse through inheritance
  • Each subclass implements only what’s different
  • Common functionality in base class

5. Proxy Pattern (Access Control)

Pattern Description

The Proxy pattern provides a surrogate or placeholder object that controls access to another real object.

Implementation in the Code

export class Connection {
    private privInternalData: ServiceRecognizerBase | SynthesisAdapterBase;
    private privEventListener: IDetachable;
    private privServiceEventListener: IDetachable;
 
    /**
     * Gets the Connection instance from the specified recognizer.
     */
    public static fromRecognizer(recognizer: Recognizer | ConversationTranscriber): Connection {
        const recoBase = recognizer.internalData as ServiceRecognizerBase;
        const ret: Connection = new Connection();
 
        ret.privInternalData = recoBase;
        ret.setupEvents();
        return ret;
    }
 
    /**
     * Starts to set up connection to the service.
     */
    public openConnection(cb?: () => void, err?: (error: string) => void): void {
        marshalPromiseToCallbacks(this.privInternalData.connect(), cb, err);
    }
 
    /**
     * Closes the connection the service.
     */
    public closeConnection(cb?: () => void, err?: (error: string) => void): void {
        // Controlled access to internal connection
    }
}

How It Works

  • Controlled Access: Connection controls access to internal ServiceRecognizerBase
  • Static Factory: fromRecognizer() creates proxy instances
  • Wrapping Calls: Methods wrap internal calls with additional logic

Why It’s Used

  • Users interact with public Connection API
  • Internal ServiceRecognizerBase is hidden
  • Prevents direct access to internal implementation
  • Provides cleaner public interface

Simple Example

// Proxy Pattern Example
interface IBankAccount {
    deposit(amount: number): void;
    withdraw(amount: number): void;
    getBalance(): number;
}
 
class RealBankAccount implements IBankAccount {
    private balance: number = 0;
 
    deposit(amount: number): void {
        this.balance += amount;
        console.log(`Deposited: $${amount}`);
    }
 
    withdraw(amount: number): void {
        this.balance -= amount;
        console.log(`Withdrew: $${amount}`);
    }
 
    getBalance(): number {
        return this.balance;
    }
}
 
class BankAccountProxy implements IBankAccount {
    private realAccount: RealBankAccount;
    private pin: string;
    private pinEntered: boolean = false;
 
    constructor(pin: string) {
        this.pin = pin;
        this.realAccount = new RealBankAccount();
    }
 
    private validatePin(pin: string): boolean {
        return this.pin === pin && (this.pinEntered = true);
    }
 
    deposit(amount: number): void {
        if (this.pinEntered) {
            this.realAccount.deposit(amount);
        } else {
            console.log("Access denied: PIN required");
        }
    }
 
    withdraw(amount: number): void {
        if (this.pinEntered) {
            this.realAccount.withdraw(amount);
        } else {
            console.log("Access denied: PIN required");
        }
    }
 
    getBalance(): number {
        return this.pinEntered ? this.realAccount.getBalance() : -1;
    }
 
    login(pin: string): boolean {
        return this.validatePin(pin);
    }
}
 
// Usage
const account = new BankAccountProxy("1234");
account.deposit(100); // Access denied
account.login("1234");
account.deposit(100); // Deposited: $100

6. Configuration/Property Pattern

Pattern Description

Uses a centralized configuration object to manage all settings and properties.

Implementation in the Code

export class PropertyCollection {
    private privKeys: string[] = [] as string[];
    private privValues: string[] = [] as string[];
 
    // Get property with default fallback
    public getProperty(key: PropertyId | string, def?: string | number | boolean): string {
        let keyToUse: string;
 
        if (typeof key === "string") {
            keyToUse = key;
        } else {
            keyToUse = PropertyId[key];
        }
 
        for (let n = 0; n < this.privKeys.length; n++) {
            if (this.privKeys[n] === keyToUse) {
                return this.privValues[n];
            }
        }
 
        if (def === undefined) {
            return undefined;
        }
 
        return String(def);
    }
 
    // Set property
    public setProperty(key: string | PropertyId, value: string): void {
        let keyToUse: string;
 
        if (typeof key === "string") {
            keyToUse = key;
        } else {
            keyToUse = PropertyId[key];
        }
 
        for (let n = 0; n < this.privKeys.length; n++) {
            if (this.privKeys[n] === keyToUse) {
                this.privValues[n] = value;
                return;
            }
        }
 
        this.privKeys.push(keyToUse);
        this.privValues.push(value);
    }
 
    // Clone configuration
    public clone(): PropertyCollection {
        const clonedMap = new PropertyCollection();
 
        for (let n = 0; n < this.privKeys.length; n++) {
            clonedMap.privKeys.push(this.privKeys[n]);
            clonedMap.privValues.push(this.privValues[n]);
        }
 
        return clonedMap;
    }
 
    // Merge configurations
    public mergeTo(destinationCollection: PropertyCollection): void {
        this.privKeys.forEach((key: string | PropertyId): void => {
            if (destinationCollection.getProperty(key, undefined) === undefined) {
                const value = this.getProperty(key);
                destinationCollection.setProperty(key, value);
            }
        });
    }
}

How It Works

  • Key-Value Storage: Properties stored as parallel arrays
  • Type-Safe Keys: PropertyId enum ensures valid property names
  • Default Values: getProperty() returns defaults if not set
  • Cloning: clone() creates independent copies
  • Merging: mergeTo() combines configurations

Why It’s Used

  • Centralized configuration management
  • Type-safe property access
  • Easy to clone and merge configurations
  • Avoids constructor parameter explosion

Simple Example

// Configuration Pattern Example
enum ConfigKey {
    DatabaseUrl = "DATABASE_URL",
    ApiPort = "API_PORT",
    LogLevel = "LOG_LEVEL",
}
 
class Config {
    private properties = new Map<string, string>();
 
    get(key: ConfigKey, defaultValue?: string): string {
        return this.properties.get(key) || defaultValue || "";
    }
 
    set(key: ConfigKey, value: string): void {
        this.properties.set(key, value);
    }
 
    clone(): Config {
        const cloned = new Config();
        this.properties.forEach((value, key) => {
            cloned.set(key as ConfigKey, value);
        });
        return cloned;
    }
}
 
// Usage
const config = new Config();
config.set(ConfigKey.DatabaseUrl, "mongodb://localhost");
config.set(ConfigKey.ApiPort, "3000");
config.set(ConfigKey.LogLevel, "debug");
 
console.log(config.get(ConfigKey.DatabaseUrl));
console.log(config.get(ConfigKey.ApiPort, "8080"));

7. Utility/Helper Pattern (Static Utility Methods)

Pattern Description

Creates utility classes with static methods that provide common functionality without requiring instantiation.

Implementation in the Code

export class StringUtils {
    public static formatString(format: string, ...args: any[]): string {
        // ... formatting logic
    }
 
    public static isEmpty(str: string): boolean {
        return !str || str.trim().length === 0;
    }
}
import { v4 as uuid } from "uuid";
 
const createGuid: () => string = (): string => uuid();
 
const createNoDashGuid: () => string = (): string => createGuid().replace(new RegExp("-", "g"), "").toUpperCase();
 
export { createGuid, createNoDashGuid };
export class Contracts {
    public static throwIfNullOrUndefined(param: any, name: string): void {
        if (param === undefined || param === null) {
            throw new Error("throwIfNullOrUndefined:" + name);
        }
    }
 
    public static throwIfNull(param: any, name: string): void {
        if (param === null) {
            throw new Error("throwIfNull:" + name);
        }
    }
 
    public static throwIfNullOrWhitespace(param: string, name: string): void {
        this.throwIfNullOrUndefined(param, name);
        if (("" + param).trim().length < 1) {
            throw new Error("throwIfNullOrWhitespace:" + name);
        }
    }
}

How It Works

  • Static Methods Only: No instance creation needed
  • Reusable Functions: Common functionality in one place
  • Parameter Validation: Guard clause helpers prevent errors
  • No State: Pure utility functions

Why It’s Used

  • Eliminates need for instantiation
  • Groups related utility functions
  • Consistent error handling
  • Easy to test and use throughout codebase

Simple Example

// Utility Pattern Example
class ValidationHelper {
    static isEmail(email: string): boolean {
        const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
        return emailRegex.test(email);
    }
 
    static isPhoneNumber(phone: string): boolean {
        const phoneRegex = /^\d{10}$/;
        return phoneRegex.test(phone);
    }
 
    static isStrongPassword(password: string): boolean {
        return password.length >= 8 && 
               /[A-Z]/.test(password) && 
               /[0-9]/.test(password) && 
               /[!@#$%^&*]/.test(password);
    }
 
    static validateAndThrow(value: any, fieldName: string): void {
        if (value === null || value === undefined) {
            throw new Error(`${fieldName} is required`);
        }
    }
}
 
// Usage
console.log(ValidationHelper.isEmail("john@example.com")); // true
console.log(ValidationHelper.isPhoneNumber("5551234567"));  // true
ValidationHelper.validateAndThrow(null, "username"); // throws