From harness-claude
Encapsulates interchangeable algorithms behind a common interface for runtime selection, eliminating large if/else chains. Provides code examples for both class-based and idiomatic TypeScript function-based strategies.
How this skill is triggered — by the user, by Claude, or both
Slash command
/harness-claude:gof-strategy-patternThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> Encapsulate interchangeable algorithms behind a common interface for runtime selection.
Encapsulate interchangeable algorithms behind a common interface for runtime selection.
if/else chains where each branch implements a different algorithmInterface + concrete strategies:
// Strategy interface
interface PricingStrategy {
calculate(basePrice: number, quantity: number, customer: Customer): number;
}
// Concrete strategies
class StandardPricing implements PricingStrategy {
calculate(basePrice: number, quantity: number): number {
return basePrice * quantity;
}
}
class BulkDiscountPricing implements PricingStrategy {
constructor(
private readonly threshold: number,
private readonly discount: number
) {}
calculate(basePrice: number, quantity: number): number {
if (quantity >= this.threshold) {
return basePrice * quantity * (1 - this.discount);
}
return basePrice * quantity;
}
}
class MemberPricing implements PricingStrategy {
calculate(basePrice: number, quantity: number, customer: Customer): number {
const rate = customer.memberTier === 'gold' ? 0.8 : 0.9;
return basePrice * quantity * rate;
}
}
// Context — uses a strategy
class ShoppingCart {
private strategy: PricingStrategy = new StandardPricing();
private items: { name: string; price: number; qty: number }[] = [];
setStrategy(strategy: PricingStrategy): void {
this.strategy = strategy;
}
addItem(name: string, price: number, qty: number): void {
this.items.push({ name, price, qty });
}
calculateTotal(customer: Customer): number {
return this.items.reduce((total, item) => {
return total + this.strategy.calculate(item.price, item.qty, customer);
}, 0);
}
}
// Runtime selection
const cart = new ShoppingCart();
cart.addItem('Widget', 10.0, 5);
const customer: Customer = { id: 'u1', memberTier: 'gold' };
// Default
console.log(cart.calculateTotal(customer)); // 50.00
// Switch to member pricing
cart.setStrategy(new MemberPricing());
console.log(cart.calculateTotal(customer)); // 40.00
// Switch to bulk discount (20% off 10+)
cart.setStrategy(new BulkDiscountPricing(10, 0.2));
cart.addItem('Widget', 10.0, 6); // now 11 total
console.log(cart.calculateTotal(customer)); // 88.00 (20% off all)
Function-based strategies (TypeScript idiomatic — skip the class):
type SortStrategy<T> = (a: T, b: T) => number;
const sortByName: SortStrategy<{ name: string }> = (a, b) => a.name.localeCompare(b.name);
const sortByDate: SortStrategy<{ createdAt: Date }> = (a, b) =>
a.createdAt.getTime() - b.createdAt.getTime();
const sortByScore: SortStrategy<{ score: number }> = (a, b) => b.score - a.score; // descending
function sortItems<T>(items: T[], strategy: SortStrategy<T>): T[] {
return [...items].sort(strategy);
}
// No strategy objects needed — functions are the strategies
const sortedByName = sortItems(users, sortByName);
const sortedByDate = sortItems(users, sortByDate);
Strategy map for configuration-driven selection:
type CompressionType = 'gzip' | 'brotli' | 'zstd' | 'none';
interface Compressor {
compress(data: Buffer): Promise<Buffer>;
decompress(data: Buffer): Promise<Buffer>;
}
const compressionStrategies: Record<CompressionType, Compressor> = {
gzip: new GzipCompressor(),
brotli: new BrotliCompressor(),
zstd: new ZstdCompressor(),
none: new NoopCompressor(),
};
function getCompressor(type: CompressionType): Compressor {
return compressionStrategies[type];
}
const compressor = getCompressor((process.env.COMPRESSION_TYPE as CompressionType) ?? 'gzip');
const compressed = await compressor.compress(data);
Strategy vs. State: Both use polymorphism to replace conditionals. Strategy: the algorithm is chosen externally and remains stable during execution. State: the context transitions between states internally. Ask "who decides when to switch?" — external caller → Strategy, object itself → State.
Strategy vs. Template Method: Template Method uses inheritance — the base class defines the algorithm skeleton and subclasses fill in steps. Strategy uses composition — the algorithm is injected. Prefer Strategy (composition) over Template Method (inheritance) in modern TypeScript.
Anti-patterns:
Testing strategies: Each strategy can be tested in isolation without a context:
describe('BulkDiscountPricing', () => {
const strategy = new BulkDiscountPricing(10, 0.2);
it('applies discount when quantity >= threshold', () => {
expect(strategy.calculate(10, 10, mockCustomer)).toBe(80);
});
it('does not apply discount below threshold', () => {
expect(strategy.calculate(10, 9, mockCustomer)).toBe(90);
});
});
refactoring.guru/design-patterns/strategy
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeImplements the Strategy Pattern in JavaScript, enabling swapping algorithms at runtime via plain functions or a strategy registry.
Encapsulates interchangeable algorithms in separate classes, enabling runtime swapping without conditional logic. Useful when multiple behaviors exist for a task.
Generates PHP 8.4 Strategy pattern with interface, concrete strategies, resolver, optional service, and unit tests for interchangeable algorithm families like pricing or sorting.