Skip to content
Back to skills

Cross Language Design Patterns

ASecurity

Use when implementing cross-language design patterns.

  • 2 stars
  • 0 votes
  • 0 copies
  • 6 views
  • Added September 10, 2026
developmenttypescriptpythonrustgojavac++api

Works with

  • api

Security analysis

A100/100

Scanned September 10, 2026

npx -y skills add LoopyLuci/Skills --skill cross-language-design-patterns --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Cross Language Design Patterns?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Cross Language Design Patterns
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/loopyluci-cross-language-design-patterns/badge)](https://www.skillsdirectory.com/skills/loopyluci-cross-language-design-patterns)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: cross-language-design-patterns
description: "Use when implementing cross-language design patterns."
version: 1.0.0
author: Hermes Agent
license: MIT
metadata:
  hermes:
    tags: [design-patterns, gof, python, rust, typescript, architecture]
    related_skills: [software-design-patterns, api-design-rest-graphql, functional-programming-concepts]
---

# Cross-Language Design Patterns

Implementing classic GoF and modern design patterns across multiple languages — Python, Rust, TypeScript — with language-specific idioms and trade-offs.

## When to Use

- Implementing design patterns in a language you're less familiar with
- Translating patterns between languages
- Choosing the right pattern for your language's paradigm
- Teaching or learning design patterns across language boundaries
- Building multi-language systems with consistent architecture

## Pattern Selection by Language

| Pattern | Python | Rust | TypeScript |
|---------|--------|------|------------|
| Singleton | Module-level | No (use DI) | Module export |
| Factory | __init_subclass__ | Enum dispatch | Class + switch |
| Builder | Dataclass + builder | Typestate builder | Fluent API |
| Observer | __call__ / signal | Channel | EventEmitter |
| Strategy | Function passing | Trait + dyn | Interface + class |
| State | Enum + methods | State machine | State pattern |
| Command | Callable | Trait + Vec | Command class |
| Visitor | @singledispatch | Pattern matching | Visitor interface |
| Iterator | __iter__ / yield | IntoIterator | Symbol.iterator |
| Decorator | @decorator | Middleware | @decorator + HOF |

## Creational Patterns

### Singleton (Language-Specific)

```python
# Python: module-level is naturally singleton
# _singleton_store.py
_instance = None

def get_instance():
    global _instance
    if _instance is None:
        _instance = ExpensiveObject()
    return _instance


# Python alternative: metaclass
class SingletonMeta(type):
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super().__call__(*args, **kwargs)
        return cls._instances[cls]
```

```rust
// Rust: no singleton pattern needed — use DI or lazy_static!
use std::sync::OnceLock;

fn global_config() -> &'static Config {
    static CONFIG: OnceLock<Config> = OnceLock::new();
    CONFIG.get_or_init(|| Config::load())
}
```

```typescript
// TypeScript: module export is naturally singleton
// config.ts
export const config = new Config();
// or simple object literal
export const settings = {
    apiUrl: 'https://api.example.com',
    timeout: 5000,
};
```

### Builder Pattern

```python
# Python: dataclass builder pattern
from dataclasses import dataclass, field

@dataclass
class QueryBuilder:
    select_clause: str = "*"
    from_clause: str = ""
    where_clause: str = ""
    order_by_clause: str = ""
    limit_value: int = 0
    
    def select(self, columns: str) -> 'QueryBuilder':
        self.select_clause = columns
        return self
    
    def from_table(self, table: str) -> 'QueryBuilder':
        self.from_clause = table
        return self
    
    def where(self, condition: str) -> 'QueryBuilder':
        self.where_clause = f"WHERE {condition}"
        return self
    
    def build(self) -> str:
        query = f"SELECT {self.select_clause} FROM {self.from_clause}"
        if self.where_clause:
            query += f" {self.where_clause}"
        return query

# Usage: QueryBuilder().select("*").from_table("users").where("active=1").build()
```

```rust
// Rust: typestate builder pattern (compile-time safety!)
struct QueryBuilder<Select, From, Where> {
    select: Select,
    from: From,
    where_clause: Where,
}

// Empty state types
struct NoSelect;
struct SelectClause(String);
struct NoFrom;
struct FromClause(String);
struct NoWhere;
struct WhereClause(String);

impl QueryBuilder<NoSelect, NoFrom, NoWhere> {
    fn new() -> Self {
        QueryBuilder { select: NoSelect, from: NoFrom, where_clause: NoWhere }
    }
    
    fn select(self, cols: &str) -> QueryBuilder<SelectClause, NoFrom, NoWhere> {
        QueryBuilder {
            select: SelectClause(cols.to_string()),
            from: NoFrom,
            where_clause: NoWhere,
        }
    }
}

impl QueryBuilder<SelectClause, NoFrom, NoWhere> {
    fn from_table(self, table: &str) -> QueryBuilder<SelectClause, FromClause, NoWhere> {
        QueryBuilder {
            select: self.select,
            from: FromClause(table.to_string()),
            where_clause: NoWhere,
        }
    }
}

impl QueryBuilder<SelectClause, FromClause, NoWhere> {
    fn r#where(self, cond: &str) -> QueryBuilder<SelectClause, FromClause, WhereClause> {
        QueryBuilder {
            select: self.select,
            from: self.from,
            where_clause: WhereClause(cond.to_string()),
        }
    }
}

impl QueryBuilder<SelectClause, FromClause, WhereClause> {
    fn build(&self) -> String {
        format!("SELECT {} FROM {} WHERE {}",
                self.select.0, self.from.0, self.where_clause.0)
    }
}
```

```typescript
// TypeScript: fluent builder
class HttpRequestBuilder {
    private method: string = 'GET';
    private url: string = '';
    private headers: Record<string, string> = {};
    private body?: string;
    
    get(url: string): this {
        this.method = 'GET';
        this.url = url;
        return this;
    }
    
    post(url: string): this {
        this.method = 'POST';
        this.url = url;
        return this;
    }
    
    withHeader(key: string, value: string): this {
        this.headers[key] = value;
        return this;
    }
    
    withJsonBody(data: unknown): this {
        this.body = JSON.stringify(data);
        this.headers['Content-Type'] = 'application/json';
        return this;
    }
    
    async send(): Promise<Response> {
        return fetch(this.url, {
            method: this.method,
            headers: this.headers,
            body: this.body,
        });
    }
}
```

## Structural Patterns

### Adapter Pattern

```python
# Python
class EuropeanSocket:
    def voltage(self) -> int:
        return 230

class USASocket:
    def voltage(self) -> int:
        return 120

class Adapter:
    def __init__(self, socket):
        self.socket = socket
    
    def voltage(self) -> int:
        return self.socket.voltage() / 2  # Step down from 230 to 115
```

```typescript
// TypeScript
interface Logger {
    log(message: string): void;
}

class ConsoleLogger implements Logger {
    log(message: string): void {
        console.log(message);
    }
}

// External library with different interface
class ExternalLogger {
    writeMessage(msg: string, level: string): void {
        console.log(`[${level}] ${msg}`);
    }
}

// Adapter
class ExternalLoggerAdapter implements Logger {
    constructor(private external: ExternalLogger) {}
    
    log(message: string): void {
        this.external.writeMessage(message, 'INFO');
    }
}
```

## Behavioral Patterns

### Strategy Pattern

```python
# Python: functions are strategies naturally
def quick_sort(data):
    if len(data) <= 1:
        return data
    pivot = data[0]
    lesser = [x for x in data[1:] if x <= pivot]
    greater = [x for x in data[1:] if x > pivot]
    return quick_sort(lesser) + [pivot] + quick_sort(greater)

def merge_sort(data):
    if len(data) <= 1:
        return data
    mid = len(data) // 2
    left = merge_sort(data[:mid])
    right = merge_sort(data[mid:])
    return list(merge(left, right))

class Sorter:
    def __init__(self, strategy):
        self.strategy = strategy
    
    def sort(self, data):
        return self.strategy(data)

# Usage: Sorter(strategy=quick_sort).sort([3, 1, 4, 1, 5])
```

```rust
// Rust: trait-based strategy pattern
trait SortStrategy {
    fn sort(&self, data: &mut [i32]);
}

struct QuickSort;
impl SortStrategy for QuickSort {
    fn sort(&self, data: &mut [i32]) {
        data.sort();  // Simplified; actual quicksort omitted
    }
}

struct MergeSort;
impl SortStrategy for MergeSort {
    fn sort(&self, data: &mut [i32]) {
        data.sort();  // Simplified
    }
}

struct Sorter {
    strategy: Box<dyn SortStrategy>,
}

impl Sorter {
    fn new(strategy: Box<dyn SortStrategy>) -> Self {
        Sorter { strategy }
    }
    
    fn sort(&self, data: &mut [i32]) {
        self.strategy.sort(data);
    }
}
```

### Observer Pattern

```python
# Python: __call__ makes any function an observer
class Observable:
    def __init__(self):
        self._observers = []
    
    def subscribe(self, callback):
        self._observers.append(callback)
    
    def unsubscribe(self, callback):
        self._observers.remove(callback)
    
    def notify(self, *args, **kwargs):
        for observer in self._observers:
            observer(*args, **kwargs)

# Usage
def on_data_received(data):
    print(f"Got: {data}")

source = Observable()
source.subscribe(on_data_received)
source.notify("hello")
```

```rust
// Rust: channel-based observer pattern
use std::sync::mpsc;

struct EventBus {
    sender: mpsc::Sender<String>,
    receiver: mpsc::Receiver<String>,
}

impl EventBus {
    fn new() -> Self {
        let (tx, rx) = mpsc::channel();
        EventBus { sender: tx, receiver: rx }
    }
    
    fn publish(&self, event: String) {
        self.sender.send(event).unwrap();
    }
    
    fn subscribe(&self) -> mpsc::Receiver<String> {
        self.receiver.clone()
    }
}
```

## Language-Specific Idioms

### Python: Protocol Classes (Structural Typing)

```python
from typing import Protocol

class Drawable(Protocol):
    def draw(self) -> str: ...

class Circle:
    def draw(self) -> str:
        return "Circle"
    
def render(shape: Drawable):
    print(shape.draw())

render(Circle())  # Works — structural subtyping
```

### Rust: Pattern Matching (Visitor Alternative)

```rust
enum Expr {
    Number(i32),
    Add(Box<Expr>, Box<Expr>),
    Mul(Box<Expr>, Box<Expr>),
}

fn evaluate(expr: &Expr) -> i32 {
    match expr {
        Expr::Number(n) => *n,
        Expr::Add(l, r) => evaluate(l) + evaluate(r),
        Expr::Mul(l, r) => evaluate(l) * evaluate(r),
    }
}
```

### TypeScript: Discriminated Unions

```typescript
type Shape = 
    | { kind: 'circle'; radius: number }
    | { kind: 'rectangle'; width: number; height: number }
    | { kind: 'triangle'; base: number; height: number };

function area(shape: Shape): number {
    switch (shape.kind) {
        case 'circle': return Math.PI * shape.radius ** 2;
        case 'rectangle': return shape.width * shape.height;
        case 'triangle': return 0.5 * shape.base * shape.height;
    }
}
```

## Common Pitfalls

1. **Pattern overuse** — patterns aren't goals; use them to solve specific problems, not because they're "correct"
2. **Rust ownership with patterns** — Observer pattern fights Rust's ownership model; use channels
3. **Python duck typing vs. patterns** — many patterns are simpler with duck typing; don't Java-ify Python
4. **TypeScript class overhead** — prefer functions and interfaces over classes for simple patterns
5. **Pattern translation ≠ pattern matching** — a pattern in one language may be anti-pattern in another
6. **Premature abstraction** — adding Strategy pattern for a single algorithm is over-engineering

## Verification Checklist

- [ ] Pattern solves the actual problem (not applied hypothetically)
- [ ] Implementation uses language-native idioms, not transliterated Java/C++
- [ ] No unnecessary object/class overhead where functions suffice
- [ ] Pattern is testable in isolation
- [ ] Pattern doesn't fight language's type system or ownership model
- [ ] Team is familiar with the pattern in the target language

## See Also

- software-design-patterns — detailed GoF pattern reference
- api-design-rest-graphql — API-level architecture patterns
- functional-programming-concepts — FP alternatives to OOP patterns
- rust-programming-patterns — Rust-specific patterns

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.