DEV Community

Cover image for OOP poetry part III - behavioural patterns
michael matos
michael matos

Posted on

OOP poetry part III - behavioural patterns

what are behavioural patterns in OODesign

Behavioural patterns in design patterns are concerned with communication between objects, how objects collaborate, and how responsibilities are distributed among them. They focus on the interaction patterns between objects and classes, rather than on the structure of the classes or objects themselves.

List of patterns

Chain of responsability

In the world of objects, bound in code's embrace,
Lies a pattern, a concept, with intricate grace.
Chain of Responsibility, its name does declare,
A solution profound, to design's wear and tear.

At its heart, it holds the essence of delegation,
Passing requests along, without hesitation.
When a sender's request, seeks a handler's touch,
The chain springs to action, responding as such.

Imagine a system where commands take flight,
Through layers of objects, to reach their light.
Each link in the chain, bears its own duty,
Handling requests with care, swiftness, and beauty.

For reason it stands, with a purpose so clear,
To decouple senders and receivers, so dear.
Flexibility reigns, as responsibilities flow,
Adapting to changes, with a graceful, agile bow.

In logging systems, it finds its home,
Where messages travel, not alone.
From lowest to highest, or vice versa too,
The chain handles tasks, old and new.

In graphical user interfaces, it takes its place,
Responding to events, with effortless grace.
Buttons and menus, actions abound,
The chain ensures they're properly found.

In software pipelines, it proves its might,
Processing data streams, day and night.
Each link plays its role, in a symphony grand,
Chain of Responsibility, at its command.

In the realm of web servers, where requests flood the gate,
Express.js stands tall, amidst the debate.
With middleware galore, it handles each plea,
Chain of Responsibility, it sets users free.

From parsing requests to serving static files,
Express delegates tasks with practiced smiles.
Routing requests, with paths clear and bright,
Each middleware link, guiding with might.

Authentication, logging, error handling too,
Express.js weaves them seamlessly through.
Each middleware layer, a link in the chain,
Ensuring requests don't fall in vain.

So here's to the pattern, ingenious and bright,
Weaving through systems, like stars in the night.
With elegance it solves, design's intricate chore,
Chain of Responsibility, forevermore.

import express, { Request, Response, NextFunction } from 'express';

// Define a middleware function for logging
function loggerMiddleware(req: Request, res: Response, next: NextFunction) {
    console.log(`[${new Date().toISOString()}] ${req.method} ${req.path}`);
    next(); // Pass control to the next middleware
}

// Define a middleware function for authentication
function authMiddleware(req: Request, res: Response, next: NextFunction) {
    const authToken = req.headers.authorization;

    if (!authToken || authToken !== 'secret_token') {
        return res.status(401).send('Unauthorized');
    }

    next(); // Pass control to the next middleware
}

// Define a route handler
function homeRouteHandler(req: Request, res: Response) {
    res.send('Welcome to the home page!');
}

// Create an Express application
const app = express();

// Register middleware in the chain
app.use(loggerMiddleware);
app.use(authMiddleware);

// Define a route for the home page
app.get('/', homeRouteHandler);

// Start the server
const PORT = 3000;
app.listen(PORT, () => {
    console.log(`Server is running on http://localhost:${PORT}`);
});
Enter fullscreen mode Exit fullscreen mode

Command pattern

In the vast realm of code, where structures abound,
Lies a pattern of power, with elegance crowned.
Command Design Pattern, its name whispers grace,
A solution profound, to design's complex maze.

At its core, it holds the essence of action,
Encapsulating tasks with flawless traction.
When a request seeks execution's embrace,
The command pattern steps in, with unwavering grace.

Picture a system where commands take flight,
Bound in objects, gleaming with light.
Each command encapsulates an action's delight,
Decoupling sender and receiver in design's sight.

For its purpose it stands, with clarity so clear,
To separate concerns, dispelling design's fear.
Flexibility reigns as tasks are enshrined,
In commands, where elegance and power entwined.

Amidst the realm of React's vibrant domain,
Where state management orchestrates without strain,
Redux, the sage, with its store's noble grace,
Embraces commands in a timeless embrace.

Within its core, actions spring to life,
Commands encapsulated, amidst the strife.
Each action a decree, a change to impart,
In Redux's world, a symphony's start.

As users interact with the UI's array,
Actions echo their intent, clear as day.
From clicks to inputs, each event unfurled,
Triggers a command, in the Redux world.

Through reducers and dispatch, the commands take flight,
Altering state with precision, might.
Each action a ripple, in React's gentle breeze,
Guiding the flow, with effortless ease.

So here's to the pattern, with purpose so grand,
Command Design Pattern, across the land.
In software's tapestry, it weaves its tale,
A masterpiece of design, without fail.

// CounterActions.js
export const ActionTypes = {
  INCREMENT: 'INCREMENT',
  DECREMENT: 'DECREMENT',
};

// Action creators (commands)
export const increment = () => ({
  type: ActionTypes.INCREMENT,
});

export const decrement = () => ({
  type: ActionTypes.DECREMENT,
});

// CounterReducer.js
import { ActionTypes } from './CounterActions';

const initialState = {
  count: 0,
};

// Reducer function
const counterReducer = (state = initialState, action) => {
  switch (action.type) {
    case ActionTypes.INCREMENT: // Command: INCREMENT
      return {
        ...state,
        count: state.count + 1,
      };
    case ActionTypes.DECREMENT: // Command: DECREMENT
      return {
        ...state,
        count: state.count - 1,
      };
    default:
      return state;
  }
};

export default counterReducer;

// Counter.js
import React, { useReducer } from 'react';
import counterReducer from './CounterReducer';
import { increment, decrement } from './CounterActions';

const Counter = () => {
  // Initialize state and dispatch function using useReducer hook
  const [state, dispatch] = useReducer(counterReducer, { count: 0 });

  // Command execution: Dispatching actions
  const handleIncrement = () => {
    dispatch(increment()); // Command: increment
  };

  const handleDecrement = () => {
    dispatch(decrement()); // Command: decrement
  };

  return (
    <div>
      <h1>Counter: {state.count}</h1>
      {/* Buttons triggering commands */}
      <button onClick={handleIncrement}>Increment</button>
      <button onClick={handleDecrement}>Decrement</button>
    </div>
  );
};

export default Counter;

Enter fullscreen mode Exit fullscreen mode

Iterator pattern

In the world of data, where structures abound,
A pattern emerges, with purpose profound.
Iterator Pattern, its essence does gleam,
Navigating collections like a serene dream.

At its heart lies the quest to explore,
Traversing sequences, from ceiling to floor.
When data beckons, and elements call,
Iterator responds, ensuring naught shall stall.

Imagine a journey through lists long and wide,
With each step traversed, with grace as our guide.
From start to end, or reverse if we please,
Iterator leads, with consummate ease.

Its purpose to abstract, traversal's chore,
From arrays to trees, and even more.
Flexibility reigns, as it adapts with flair,
To various structures, without a care.

In for loops, it finds its natural place,
Where iteration flows, at a steady pace.
Looping through arrays, in languages vast,
Iterator ensures no element's surpassed.

In the realm of streams, both finite and grand,
Iterator's hand guides across the land.
From reading files to parsing streams,
Iterator orchestrates, fulfilling our dreams.

So here's to the pattern, in data's embrace,
Iterator Pattern, with elegance and grace.
In software's symphony, it plays its part,
A maestro of traversal, with a beating heart.

// Define an interface for the iterator
interface Iterator<T> {
  next(): { value: T, done: boolean };
}

// Define an iterable collection for Fibonacci numbers
class FibonacciSequence implements Iterable<number> {
  [Symbol.iterator](): Iterator<number> {
    let a = 0;
    let b = 1;

    // Implement the next method of the iterator
    const next = (): { value: number, done: boolean } => {
      const value = a;
      a = b;
      b = value + b;
      return { value, done: false };
    };

    return { next };
  }
}

// Example usage
const fibonacciSequence = new FibonacciSequence();
const iterator = fibonacciSequence[Symbol.iterator]();

// Print the first 10 Fibonacci numbers
for (let i = 0; i < 10; i++) {
  const result = iterator.next();
  console.log(result.value); // Output: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34
}

Enter fullscreen mode Exit fullscreen mode

Mediator pattern

In the world of software, where systems intertwine,
There lies a pattern, with a purpose divine.
Mediator, its name, a conductor of grace,
Bringing harmony to every place.

At its core, it serves to mediate,
Between components, a bridge so great.
With elegance and finesse, it orchestrates,
Communication between disparate states.

Imagine a world where chaos reigns,
Components scattered, with no chains.
Mediator steps in, with a calming embrace,
Bringing order to the chaotic space.

Consider a complex form, with fields galore,
Each input, a component, longing to explore.
But how do they converse, how do they share,
The data they hold, with such care?

Enter the Mediator, with its guiding light,
Centralizing state, keeping components in sight.
Through events and callbacks, it fosters a link,
Between form elements, without a kink.

From Backbone to React, and frameworks in between,
Mediator's touch can always be seen.
Facilitating communication, without fuss,
Bringing cohesion to applications, without muss.

So here's to the Mediator, so noble and grand,
A pattern that brings order to every land.
In software's symphony, it plays its part,
Bringing harmony and unity from the start.

import React, { createContext, useContext, useState } from 'react';

// Define a Context to act as the Mediator
const DataContext = createContext();

// Define a DataProvider component to manage state and act as the Mediator
const DataProvider = ({ children }) => {
  // State variable managed by the DataProvider
  const [data, setData] = useState('Initial data');

  // Method to update data
  const updateData = (newData) => {
    setData(newData);
  };

  // Context value containing data and methods to update it
  const contextValue = {
    data,
    updateData,
  };

  return <DataContext.Provider value={contextValue}>{children}</DataContext.Provider>;
};

// Custom hook to access the DataContext
const useDataContext = () => useContext(DataContext);

// Child component consuming the DataContext and displaying the data
const DisplayComponent = () => {
  // Access context variable using custom hook
  const { data } = useDataContext();

  return <p>Data from context: {data}</p>;
};

// Child component consuming the DataContext and updating the data
const UpdateComponent = () => {
  // Access context variable and update method using custom hook
  const { data, updateData } = useDataContext();

  // Handler for updating data
  const handleUpdate = () => {
    const newData = 'Updated data';
    updateData(newData); // Call the updateData method from the context
  };

  return (
    <div>
      <p>Data from context: {data}</p>
      <button onClick={handleUpdate}>Update Data</button>
    </div>
  );
};

// Parent component providing the DataContext to its children
const ParentComponent = () => {
  return (
    <DataProvider>
      <div>
        <h2>Parent Component</h2>
        <p>This component provides data to its children through the DataContext.</p>
        <DisplayComponent /> {/* Child component displaying the data */}
        <UpdateComponent /> {/* Child component updating the data */}
      </div>
    </DataProvider>
  );
};

export default ParentComponent;

Enter fullscreen mode Exit fullscreen mode

memento pattern

In the land where code takes flight,
Lies a pattern of memory, shining bright.
Known as Memento, its purpose grand,
To capture states with a gentle hand.

When objects dance with transient grace,
And their states must leave no trace,
Memento whispers, "Fear not, dear friend,
For I shall hold your states till journey's end."

In times of undo, where mistakes do roam,
Memento's embrace brings solace home.
With each command to rewind or revise,
It restores the past before our eyes.

In editors where text flows free,
Undoing changes is a jubilee.
Memento preserves each edit's story,
Returning text to its former glory.

In games where players quest and fight,
Memento records each hero's plight.
With checkpoints saved at every stage,
Players can rewind to a safer page.

In applications of financial might,
Where transactions move with speed and light,
Memento safeguards each transaction's trail,
Ensuring data integrity shall never fail.

So here's to Memento, steadfast and true,
In the world of patterns, it shines anew.
With its purpose clear and its use cases vast,
It guards our states, ensuring they last.

// Originator: Represents the text editor
class TextEditor {
  private content: string;

  constructor() {
    this.content = '';
  }

  // Method to set the content of the text editor
  setContent(content: string) {
    this.content = content;
  }

  // Method to get the content of the text editor
  getContent(): string {
    return this.content;
  }

  // Method to create a memento (snapshot) of the current state
  createMemento(): Memento {
    return new Memento(this.content);
  }

  // Method to restore the state from a memento
  restoreFromMemento(memento: Memento) {
    this.content = memento.getState();
  }
}

// Memento: Represents the snapshot of the state of the text editor
class Memento {
  private state: string;

  constructor(state: string) {
    this.state = state;
  }

  // Method to get the state from the memento
  getState(): string {
    return this.state;
  }
}

// Caretaker: Manages the history of the text editor's states
class HistoryManager {
  private history: Memento[];
  private currentIndex: number;

  constructor() {
    this.history = [];
    this.currentIndex = -1;
  }

  // Method to add a memento to the history
  addMemento(memento: Memento) {
    // Clear future history when adding a new state
    this.history.splice(this.currentIndex + 1);
    this.history.push(memento);
    this.currentIndex++;
  }

  // Method to undo the last change
  undo(): Memento | null {
    if (this.currentIndex > 0) {
      this.currentIndex--;
      return this.history[this.currentIndex];
    }
    return null;
  }

  // Method to redo the last undone change
  redo(): Memento | null {
    if (this.currentIndex < this.history.length - 1) {
      this.currentIndex++;
      return this.history[this.currentIndex];
    }
    return null;
  }
}

// Usage example
const editor = new TextEditor();
const historyManager = new HistoryManager();

// Initial content
editor.setContent('Hello, world!');

// Save initial state
const initialState = editor.createMemento();
historyManager.addMemento(initialState);

// User makes changes
editor.setContent('Hello, universe!');
const currentState = editor.createMemento();
historyManager.addMemento(currentState);

// Undo the last change
const undoneState = historyManager.undo();
if (undoneState) {
  editor.restoreFromMemento(undoneState);
}

console.log(editor.getContent()); // Output: Hello, world!

// Redo the undone change
const redoneState = historyManager.redo();
if (redoneState) {
  editor.restoreFromMemento(redoneState);
}

console.log(editor.getContent()); // Output: Hello, universe!

Enter fullscreen mode Exit fullscreen mode

Oberserver pattern

In the realm where patterns weave their tale,
Lies one of observation, never frail.
'Tis the Observer, its purpose clear,
To let objects know when changes draw near.

When entities dance in a state of flux,
Observer stands ready, never to duck.
Its eyes keenly watch, its ears attuned,
To signals of change, it's always tuned.

In UIs where buttons click and fields update,
Observer whispers softly, "No need to debate."
It notifies listeners when data's transformed,
Ensuring views reflect the changes performed.

In chatrooms where messages fly,
Observer ensures each user's cry.
It broadcasts updates to all who listen,
Uniting users in a shared vision.

In games where scores rise and heroes fall,
Observer shouts loud, heeding the call.
It notifies players of events unseen,
Guiding them through each digital scene.

In the world of Node.js, Observer takes flight,
Within event-driven architecture, it finds its might.
As Node.js dances in asynchronous glee,
Observer notifies listeners of events, setting data free.

In Vue.js, Observer finds a home,
Within the reactive core, it's free to roam.
With reactivity at its heart, Vue.js shines,
Observer pattern ensures views update in time.

In Node.js, event emitters reign supreme,
From HTTP requests to file streams, it's a dream.
Observer pattern orchestrates the flow,
Notifying subscribers when events bestow.

In Vue.js, components come alive,
As data changes, they strive to thrive.
With Observer pattern, they stay in sync,
Rendering views as states blink.

So here's to Node.js and Vue.js, champions bold,
Implementing Observer, their stories unfold.
In event-driven and reactive lands they dwell,
Observer pattern ensures all is well.

// Observer interface defining the contract for observers
interface Observer {
  update(data: any): void;
}

// Observable (subject) class that emits events to observers
class Observable {
  private observers: Observer[] = [];

  // Method to subscribe an observer to the observable
  subscribe(observer: Observer) {
    this.observers.push(observer);
  }

  // Method to unsubscribe an observer from the observable
  unsubscribe(observer: Observer) {
    this.observers = this.observers.filter(obs => obs !== observer);
  }

  // Method to notify all observers when an event occurs
  notify(data: any) {
    this.observers.forEach(observer => observer.update(data));
  }
}

// Concrete observer class implementing the Observer interface
class ConcreteObserver implements Observer {
  update(data: any) {
    console.log('Received data:', data);
  }
}

// Usage example
const observable = new Observable();
const observer1 = new ConcreteObserver();
const observer2 = new ConcreteObserver();

// Subscribe observers to the observable
observable.subscribe(observer1);
observable.subscribe(observer2);

// Notify observers when an event occurs
observable.notify('Event 1');
observable.notify('Event 2');

// Unsubscribe observer1 from the observable
observable.unsubscribe(observer1);

// Notify observers again
observable.notify('Event 3');

Enter fullscreen mode Exit fullscreen mode

State pattern

In the realm of software, where patterns are found,
There's one called State, its purpose profound.
Managing objects in various states they may be,
Guiding their behavior with flexibility.

When objects transition from one state to the next,
State pattern ensures they're never perplexed.
It encapsulates state-specific behavior in classes,
Allowing objects to switch with minimal fuss.

In vending machines, where snacks await,
State pattern controls each coin's fate.
As users insert coins and make their selection,
State pattern governs with flawless direction.

In game development, where characters roam,
State pattern gives them a place to call home.
From "idle" to "running", from "jumping" to "crouching",
State pattern keeps their movements from slouching.

In traffic lights, where signals dictate,
State pattern orchestrates the flow of each rate.
From "red" to "green", from "stop" to "go",
State pattern ensures traffic's ebb and flow.

In the software's expanse, where complexity unfurls,
State pattern maps out the intricate worlds.
From states of "ready" to "waiting" and "done",
Each transition managed, each victory won.

In the realm of eCommerce, where orders take flight,
State pattern tracks them from "placed" to "shipped" in the night.
With states like "processing" and "delivered" in sight,
Smooth transitions ensure customers' delight.

In the domain of gaming, where characters roam,
State pattern guides them to their digital home.
From "idle" to "running", from "attacking" to "flee",
Each state transition ensures the game's flow is free.

In the maze of workflows, where tasks intertwine,
State pattern choreographs each step in line.
From "pending" to "approved", from "rejected" to "complete",
Efficient transitions make processes neat.

Within React's realm, where data is king,
State pattern reigns supreme, like a golden ring.
With each fetch request to the server's domain,
State pattern manages the data's gain.

From "loading" to "success" and "error" in sight,
State pattern orchestrates, ensuring all is right.
As components await data to display,
State pattern handles each state, come what may.

So here's to State pattern, with its purpose clear,
Managing objects' states without fear.
In software's vast landscape, it finds its place,
Guiding objects with elegance and grace.

import React, { useState, useEffect } from 'react';

// Define the possible states for the fetch
const FetchState = {
  Loading: 'loading',
  Success: 'success',
  Error: 'error',
};

// Define the fetch component
const FetchComponent = ({ url }) => {
  const [fetchState, setFetchState] = useState({
    state: FetchState.Loading,
    data: null,
    error: '',
  });

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch(url);
        const data = await response.json();
        setFetchState({ state: FetchState.Success, data, error: '' });
      } catch (error) {
        setFetchState({ state: FetchState.Error, data: null, error: error.message });
      }
    };

    fetchData();

    // Cleanup function
    return () => {
      // Cleanup logic if needed
    };
  }, [url]);

  return (
    <div>
      {fetchState.state === FetchState.Loading && <p>Loading...</p>}
      {fetchState.state === FetchState.Success && <p>Data: {JSON.stringify(fetchState.data)}</p>}
      {fetchState.state === FetchState.Error && <p>Error: {fetchState.error}</p>}
    </div>
  );
};

// Usage example
const App = () => {
  return (
    <div>
      <h1>Fetch Component Example</h1>
      <FetchComponent url="https://jsonplaceholder.typicode.com/posts/1" />
    </div>
  );
};

export default App;

Enter fullscreen mode Exit fullscreen mode

strategy pattern

In the realm of software, where patterns take flight,
Lies one called Strategy, a beacon of light.
With purpose profound, it shapes algorithms' course,
Guiding their behavior with elegant force.

In the heart of the code, where complexity lies,
Strategy pattern simplifies, like clear, open skies.
It encapsulates algorithms with finesse,
Allowing them to vary, adapt, and impress.

When sorting arrays or searching for keys,
Strategy pattern shines, like a cool breeze.
With algorithms swapped at runtime's command,
Complex tasks are tamed, and code becomes grand.

In games where strategies govern each move,
Strategy pattern guides, ensuring they groove.
From AI opponents to player tactics grand,
Dynamic strategies shape the game's hand.

In eCommerce realms where discounts are sought,
Strategy pattern aids, with benefits brought.
From fixed discounts to percentage rates,
Flexible strategies handle the rates.

In user interfaces where validations reign,
Strategy pattern steps in, removing the pain.
From email validations to password checks,
Custom strategies ensure security's specs.

So here's to Strategy, with its purpose clear,
In software's vast landscape, it's held dear.
From sorting algorithms to game AI,
Strategy pattern guides with a knowing eye.

example uno:

const numbers = [4, 2, 5, 1, 3];

// Sort in ascending order using the default comparison
const ascendingOrder = numbers.sort((a, b) => a - b);
console.log(ascendingOrder); // Output: [1, 2, 3, 4, 5]

// Sort in descending order using a custom comparison
const descendingOrder = numbers.sort((a, b) => b - a);
console.log(descendingOrder); // Output: [5, 4, 3, 2, 1]

Enter fullscreen mode Exit fullscreen mode

example numero deux(a bit more complex):

// Define the interface for sorting strategies
interface SortStrategy {
  sort(data: any[]): any[];
}

// Define concrete sorting strategies
class BubbleSortStrategy implements SortStrategy {
  sort(data: any[]): any[] {
    // Bubble sort implementation
    return data.slice().sort((a, b) => a - b);
  }
}

class QuickSortStrategy implements SortStrategy {
  sort(data: any[]): any[] {
    // Quick sort implementation
    const quickSort = (arr: any[]): any[] => {
      if (arr.length <= 1) return arr;
      const pivot = arr[arr.length - 1];
      const left = [];
      const right = [];
      for (let i = 0; i < arr.length - 1; i++) {
        if (arr[i] < pivot) left.push(arr[i]);
        else right.push(arr[i]);
      }
      return [...quickSort(left), pivot, ...quickSort(right)];
    };
    return quickSort(data);
  }
}

class MergeSortStrategy implements SortStrategy {
  sort(data: any[]): any[] {
    // Merge sort implementation
    const merge = (left: any[], right: any[]): any[] => {
      const result = [];
      let leftIndex = 0;
      let rightIndex = 0;
      while (leftIndex < left.length && rightIndex < right.length) {
        if (left[leftIndex] < right[rightIndex]) {
          result.push(left[leftIndex]);
          leftIndex++;
        } else {
          result.push(right[rightIndex]);
          rightIndex++;
        }
      }
      return result.concat(left.slice(leftIndex)).concat(right.slice(rightIndex));
    };

    const mergeSort = (arr: any[]): any[] => {
      if (arr.length <= 1) return arr;
      const middle = Math.floor(arr.length / 2);
      const left = arr.slice(0, middle);
      const right = arr.slice(middle);
      return merge(mergeSort(left), mergeSort(right));
    };

    return mergeSort(data);
  }
}

class SelectionSortStrategy implements SortStrategy {
  sort(data: any[]): any[] {
    // Selection sort implementation
    const selectionSort = (arr: any[]): any[] => {
      const len = arr.length;
      for (let i = 0; i < len - 1; i++) {
        let min = i;
        for (let j = i + 1; j < len; j++) {
          if (arr[j] < arr[min]) {
            min = j;
          }
        }
        if (min !== i) {
          [arr[i], arr[min]] = [arr[min], arr[i]];
        }
      }
      return arr;
    };
    return selectionSort(data.slice());
  }
}

class InsertionSortStrategy implements SortStrategy {
  sort(data: any[]): any[] {
    // Insertion sort implementation
    const insertionSort = (arr: any[]): any[] => {
      const len = arr.length;
      for (let i = 1; i < len; i++) {
        let j = i;
        const temp = arr[i];
        while (j > 0 && arr[j - 1] > temp) {
          arr[j] = arr[j - 1];
          j--;
        }
        arr[j] = temp;
      }
      return arr;
    };
    return insertionSort(data.slice());
  }
}

// Define the context class
class SortContext {
  private strategy: SortStrategy;

  constructor(strategy: SortStrategy) {
    this.strategy = strategy;
  }

  // Method to set the sorting strategy
  setStrategy(strategy: SortStrategy) {
    this.strategy = strategy;
  }

  // Method to perform sorting using the current strategy
  sort(data: any[]): any[] {
    return this.strategy.sort(data);
  }
}

// Usage example
const data = [7, 2, 5, 1, 3, 9, 4, 6, 8];

const bubbleSort = new BubbleSortStrategy();
const quickSort = new QuickSortStrategy();
const mergeSort = new MergeSortStrategy();
const selectionSort = new SelectionSortStrategy();
const insertionSort = new InsertionSortStrategy();

const context = new SortContext(bubbleSort);
console.log('Bubble Sort:', context.sort(data));

context.setStrategy(quickSort);
console.log('Quick Sort:', context.sort(data));

context.setStrategy(mergeSort);
console.log('Merge Sort:', context.sort(data));

context.setStrategy(selectionSort);
console.log('Selection Sort:', context.sort(data));

context.setStrategy(insertionSort);
console.log('Insertion Sort:', context.sort(data));

Enter fullscreen mode Exit fullscreen mode

template method pattern

In software's realm where patterns dance,
Lies one called Template, with elegance and chance.
With purpose clear, it shapes the flow,
Guiding methods with a steadfast glow.

In the heart of libraries, where frameworks shine,
Template Method pattern orchestrates, a design so fine.
With hooks and lifecycle methods in its hand,
It crafts the structure, like grains of sand.

Mocha, the testing framework of choice,
Relies on Template, with a confident voice.
Test suites and cases, in patterns arrayed,
Execute with precision, like a serenade.

Webpack, the bundler that packs with might,
Utilizes Template, in its flight.
Plugins and loaders, with methods defined,
Extend its capabilities, in a bind.

And in the world of JavaScript's delight,
Template Method pattern shines so bright.
In libraries and frameworks, it finds its place,
Guiding developers with its grace.

// Abstract class defining the template method
abstract class DataProcessor {
  // Template method defining the overall algorithm
  process(data: any[]): void {
    this.readData(data);
    this.transformData(data);
    this.writeData(data);
  }

  // Abstract methods to be implemented by subclasses
  abstract readData(data: any[]): void;
  abstract transformData(data: any[]): void;
  abstract writeData(data: any[]): void;
}

// Concrete subclass implementing the template method
class JSONDataProcessor extends DataProcessor {
  readData(data: any[]): void {
    console.log('Reading JSON data:', data);
  }

  transformData(data: any[]): void {
    console.log('Transforming JSON data...');
    // Perform transformation logic specific to JSON data
  }

  writeData(data: any[]): void {
    console.log('Writing JSON data:', data);
  }
}

// Concrete subclass implementing the template method
class CSVDataProcessor extends DataProcessor {
  readData(data: any[]): void {
    console.log('Reading CSV data:', data);
  }

  transformData(data: any[]): void {
    console.log('Transforming CSV data...');
    // Perform transformation logic specific to CSV data
  }

  writeData(data: any[]): void {
    console.log('Writing CSV data:', data);
  }
}

// Usage example
const jsonDataProcessor = new JSONDataProcessor();
jsonDataProcessor.process([{ id: 1, name: 'John' }, { id: 2, name: 'Doe' }]);

const csvDataProcessor = new CSVDataProcessor();
csvDataProcessor.process(['1,John', '2,Doe']);

Enter fullscreen mode Exit fullscreen mode

Visitor pattern

In the tapestry of code, where patterns reside,
Lies one called Visitor, with methods to guide.
With purpose profound, it traverses with grace,
Visiting objects, in a coded embrace.

In compilers and parsers, it finds its abode,
Navigating syntax, along the code road.
Each node in the tree, a visit it pays,
Transforming structures, in myriad ways.

In virtual doms of libraries grand,
Visitor pattern takes a stand.
Traversing elements, with DOM's might,
Manipulating, in the web's light.

In data structures of complex design,
Visitor pattern's use, truly shines.
From trees to graphs, it roams with glee,
Performing tasks, with strategy.

So here's to Visitor, with its journey vast,
Guiding traversals, from first to last.
In software's landscape, it leaves its mark,
A pattern of power, in the coder's arc.

uno:

// Babel Visitor implementation
const babelVisitor = {
  // Visits ArrowFunctionExpression nodes
  ArrowFunctionExpression(node, state) {
    // Transform ArrowFunctionExpression nodes
    // For example, we might want to convert arrow functions to regular functions
    // (This is just a simplified example)
    const newFunctionExpression = {
      type: 'FunctionExpression',
      params: node.params,
      body: node.body,
      generator: false,
      async: false
    };
    return newFunctionExpression;
  },

  // Visits CallExpression nodes
  CallExpression(node, state) {
    // Transform CallExpression nodes
    // For example, we might want to prepend a console.log statement before each function call
    // (This is just a simplified example)
    const newCallExpression = {
      type: 'CallExpression',
      callee: {
        type: 'MemberExpression',
        object: { type: 'Identifier', name: 'console' },
        property: { type: 'Identifier', name: 'log' }
      },
      arguments: [
        { type: 'StringLiteral', value: 'Calling function:', raw: '"Calling function:"' },
        node
      ]
    };
    return newCallExpression;
  }
};

// Babel plugin using the Visitor pattern
const babelPlugin = ({ types: t }) => {
  return {
    visitor: babelVisitor
  };
};

// Using Babel with the plugin
const babel = require('@babel/core');

const inputCode = `const add = (a, b) => { return a + b; }; console.log(add(2, 3));`;
const output = babel.transformSync(inputCode, {
  plugins: [babelPlugin]
});

console.log(output.code);

Enter fullscreen mode Exit fullscreen mode

dos:

// Visitor for generating HTML markup
class HTMLGeneratorVisitor {
  constructor() {
    this.html = '';
  }

  visitParagraph(paragraph) {
    this.html += `<p>${paragraph.content}</p>`;
  }

  visitImage(image) {
    this.html += `<img src="${image.src}" alt="${image.alt}">`;
  }

  visitHyperlink(hyperlink) {
    this.html += `<a href="${hyperlink.url}">${hyperlink.text}</a>`;
  }
}

// Element representing a paragraph in the document
class ParagraphElement {
  constructor(content) {
    this.content = content;
  }

  accept(visitor) {
    visitor.visitParagraph(this);
  }
}

// Element representing an image in the document
class ImageElement {
  constructor(src, alt) {
    this.src = src;
    this.alt = alt;
  }

  accept(visitor) {
    visitor.visitImage(this);
  }
}

// Element representing a hyperlink in the document
class HyperlinkElement {
  constructor(url, text) {
    this.url = url;
    this.text = text;
  }

  accept(visitor) {
    visitor.visitHyperlink(this);
  }
}

// Example document
const documentElements = [
  new ParagraphElement('This is a paragraph.'),
  new ImageElement('image.jpg', 'Image Alt Text'),
  new HyperlinkElement('https://example.com', 'Example Website')
];

// Create a visitor instance
const htmlGeneratorVisitor = new HTMLGeneratorVisitor();

// Traverse the document elements using the visitor
documentElements.forEach(element => element.accept(htmlGeneratorVisitor));

// Output the generated HTML
console.log(htmlGeneratorVisitor.html);

/*
<p>This is a paragraph.
</p><img src="image.jpg" alt="Image Alt Text">
<a href="https://example.com">Example Website</a>
*/
Enter fullscreen mode Exit fullscreen mode

FIN

Top comments (0)