Algorithms and Design Patterns
A simple npm package containing common design patterns and algorithms.
- Design Patterns: Singleton, Factory, Prototype patterns and more
- Algorithms: Sort, Tree Search, Shortest Path and more
- DevContainer: Ready-to-use development environment
npm install algopatThe Adapter pattern is a structural design pattern that allows objects with incompatible interfaces to work together by wrapping an existing object with a new interface.
Adapter is an abstract class. To use it, extend it and implement the desired interface, delegating calls to the _target property.
import { Adapter } from 'design-patterns'
| Method / Property | Description | Example |
|---|---|---|
constructor(target: T) |
Initializes the adapter with the target object. | const adapter = new Adapter(targetInstance); |
_target: T |
Protected property holding the reference to the adapted target. | this._target.incompatibleRequest(); (used within subclasses) |
The Factory Method pattern is a creational design pattern that defines an interface for creating objects, but allows subclasses to alter the type of objects that will be created. This pattern enables a class to delegate the instantiation logic to its subclasses, promoting loose coupling and greater flexibility in object creation.
Creator is an abstract class. To use it, extend it and implement the make(...args) method to create and return instances of your desired type.
import { Creator } from 'design-patterns'
| Method / Property | Description | Example |
|---|---|---|
make(...args): T |
Abstract method to create and return an instance of type T. |
const product = factory.make('param'); |
The Prototype pattern is a creational design pattern that enables the creation of new objects by copying existing instances, known as prototypes. This approach is useful when the cost of creating a new object is expensive or complex. Instead of instantiating new objects directly, you clone a prototype and then customize it as needed. This pattern helps reduce the need for subclassing, supports dynamic object creation, and simplifies the process of creating objects with similar configurations or states.
Prototype is an abstract class—extend it in your own classes and implement clone() and initialize(...args) to enable cloning and initialization of your objects.
import { Prototype } from 'design-patterns'
| Method / Property | Description | Example |
|---|---|---|
clone(): T |
Abstract method to clone the object and return a new instance. | const copy = prototype.clone(); |
initialize(...args: any[]): void |
Abstract method to initialize the object's state. | prototype.initialize('id', 'name'); |
The PrototypeRegistry is a concrete implementation of the prototype registry pattern. It allows you to register, unregister, and retrieve prototype instances by a unique string identifier. This enables efficient cloning and management of object prototypes, making it easy to create new instances based on existing templates without coupling your code to specific classes.
To use PrototypeRegistry, create an instance, register your prototypes with unique ids, and retrieve them as needed. You can then clone the retrieved prototype to create new objects with the same structure and state.
import { PrototypeRegistry } from 'design-patterns'
| Method / Property | Description | Example |
|---|---|---|
register(id, prototype) |
Registers a prototype instance with a unique string id. | registry.register('product1', productInstance); |
unregister(id) |
Removes a prototype from the registry by its id. | registry.unregister('product1'); |
get(id) |
Retrieves a registered prototype by id. Returns the prototype or undefined. |
const proto = registry.get('product1'); |
AbstractPrototypeRegistry is an abstract class. Extend it to implement your own registry logic.
import { AbstractPrototypeRegistry } from 'design-patterns'
| Method / Property | Description | Example |
|---|---|---|
register(id, prototype) |
Abstract method to register a prototype instance with a unique string id. | registry.register('product1', productInstance); |
unregister(id, prototype) |
Abstract method to remove a prototype from the registry by its id. | registry.unregister('product1', productInstance); |
get(id) |
Abstract method to retrieve a registered prototype by id. | const proto = registry.get('product1'); |
The Singleton pattern ensures that a class has only one instance and provides a global point of access to it. To use, extend the Singleton base class and access the instance via instance().
import { Singleton } from 'design-patterns'
| Method / Property | Description | Example |
|---|---|---|
instance() |
Returns the single instance of the singleton class. | const instance = Singleton.instance(); |
constructor(factory: InstanceCreator<T>) |
Protected constructor to initialize the singleton instance with a factory. | class MySingleton extends Singleton<MySingleton> { protected constructor() { super({ make: () => new MySingleton() }); } } |
The Insertion Sort algorithm is a simple, intuitive sorting technique that builds the sorted array one item at a time. It works by iterating through the array, taking each element, and inserting it into its correct position relative to the already sorted portion of the array. This algorithm is efficient for small or nearly sorted datasets and is stable (preserves the order of equal elements).
import { insertionSort } from 'design-patterns'
| Argument | Description | Default Value | Example |
|---|---|---|---|
arr |
The array to sort. | — | [5, 3, 8, 4, 2] |
compareFn |
Function to compare two elements. Should return -1, 0, or 1. | (a, b) => (a < b ? -1 : a > b ? 1 : 0) |
(a, b) => a.localeCompare(b) |
options |
Optional settings for sorting. See below for details. | { mutable: true } |
{ mutable: false } |
Options
| Option | Description | Default Value | Example |
|---|---|---|---|
mutable |
If true, sorts the input array in-place. If false, returns a sorted copy. |
true |
false |
The Selection Sort algorithm sorts an array by repeatedly finding the minimum (or maximum, with a custom comparator) element from the unsorted part and moving it to the beginning. It is simple to implement and works well for small arrays, but is generally less efficient than more advanced algorithms for large datasets.
import { selectionSort } from 'design-patterns'
| Argument | Description | Default Value | Example |
|---|---|---|---|
arr |
The array to sort. | — | [5, 2, 9, 1, 5, 6] |
compareFn |
Function to compare two elements. Should return -1, 0, or 1. | (a, b) => (a < b ? -1 : a > b ? 1 : 0) |
(a, b) => b - a |
options |
Optional settings for sorting. See below for details. | { mutable: true } |
{ mutable: false } |
Options
| Option | Description | Default Value | Example |
|---|---|---|---|
mutable |
If true, sorts the input array in-place. If false, returns a sorted copy. |
true |
false |
The Merge Sort algorithm is an efficient, stable, divide-and-conquer sorting technique. It recursively splits the array into halves, sorts each half, and then merges the sorted halves back together. Merge Sort guarantees O(n log n) time complexity and is stable (preserves the order of equal elements).
import { mergeSort } from 'design-patterns'
| Argument | Description | Default Value | Example |
|---|---|---|---|
arr |
The array to sort. | — | [5, 3, 8, 4, 2] |
compareFn |
Function to compare two elements. Should return -1, 0, or 1. | (a, b) => (a < b ? -1 : a > b ? 1 : 0) |
(a, b) => b - a |
options |
Optional settings for sorting. See below for details. | { mutable: false } |
{ mutable: true } |
Options
| Option | Description | Default Value | Example |
|---|---|---|---|
mutable |
If true, sorts the input array in-place. If false, returns a sorted copy. |
false |
true |
The easiest way to get started is using the provided devcontainer:
- Install Docker
- Install VS Code with the Dev Containers extension
- Open this project in VS Code
- When prompted, click "Reopen in Container"
- The container will build and install all dependencies automatically
If you prefer to use a different development environment you can simply install dependencies using npm:
npm installnpm run buildThis will compile TypeScript files to JavaScript in the dist/ directory.
# Run all tests
npm test
# Run tests in watch mode
npm run test:watch
# Run tests with coverage
npm run test:coverage# Lint code
npm run lint
# Fix linting issues
npm run lint:fix
# Format code
npm run format
# Check code formatting
npm run format:checknpm run cleannpm run build- Compile TypeScript to JavaScriptnpm test- Run all testsnpm run test:watch- Run tests in watch modenpm run test:coverage- Run tests with coverage reportnpm run clean- Remove build outputnpm run lint- Lint TypeScript filesnpm run lint:fix- Fix linting issues automaticallynpm run format- Format code with Prettiernpm run format:check- Check code formattingnpm run prepublishOnly- Clean and build before publishing
This project uses GitHub Actions to automatically publish to npm when changes are pushed to the main branch. The workflow will:
- Run tests and quality checks
- Create a git tag based on the version in
package.json - Publish to npm
- Create a GitHub release
To enable automated publishing, you need to add the following secrets to your GitHub repository:
- NPM_TOKEN: Your npm authentication token
- Go to npmjs.com and log in
- Go to your profile → Access Tokens
- Create a new token with "Automation" type
- Add it as a repository secret named
NPM_TOKEN
- The workflow triggers on pushes to the
mainbranch - It skips publishing if the commit message contains
[skip ci]or[no publish] - It only publishes if the version hasn't been tagged before
- The workflow runs all quality checks before publishing
To publish manually:
- Update the version in
package.json - Run
npm run buildto ensure the latest code is compiled - Run
npm publish
The prepublishOnly script will automatically clean and build the project before publishing.
MIT