Strategy pattern
Aug 04, 2021 / 2 min read
Summary
- Definitions
- Usage
Definitions
TODO
Without strategy pattern
// Don't do this
const main = async (processor, order) => {
if (processor === 'coinbase') {
withCoinbase(order)
} else if (processor === 'paypal') {
withPayPal(order)
} else if (processor === 'payexpress') {
withPayExpress(order)
} else {
throw new Error({ message: `Processor '${processor}' is unknown to checkout method.` })
}
}
With strategy pattern
- Define different use cases
const withCoinbase = (order: IOrder): Promise<ICheckout> => {
return new Promise(async (resolve, reject) => {
// do something
})
}
const withPayExpress (order: IOrder): Promise<ICheckout> => {
return new Promise(async (resolve, reject) => {
// do something
})
}
const withPayPal (order: IOrder): Promise<ICheckout> {
return new Promise((resolve, reject) => {
// do something
})
}
- Define strategies
const getStrategies = (): {processor: string, handler: (order:IOrder) => void}[] => {
return [
{ processor: 'coinbase', handler: withCoinbase },
{ processor: 'paypal', handler: withPayPal },
{ processor: 'payexpress', handler: withPayExpress }
]
}
- Use it in your main function as follow
const main = async (processor) => {
const strategy = getStrategies().find(strategy => strategy.processor === processor)
if (!strategy) {
throw new Error({ message: `Processor '${processor}' is unknown to checkout method.` })
}
const checkout = await strategy.handler(order)
}
Full source code
// Define handlers
const withCoinbase = (order: IOrder): Promise<ICheckout> => {
return new Promise(async (resolve, reject) => {
// do something
})
}
const withPayExpress (order: IOrder): Promise<ICheckout> => {
return new Promise(async (resolve, reject) => {
// do something
})
}
const withPayPal (order: IOrder): Promise<ICheckout> {
return new Promise((resolve, reject) => {
// do something
})
}
// Define strategies with handlers
const getStrategies = (): {processor: string, handler: (order) => void}[] => {
return [
{ processor: 'coinbase', handler: withCoinbase },
{ processor: 'paypal', handler: withPayPal },
{ processor: 'payexpress', handler: withPayExpress }
]
}
// Simpler usage in main
const main = async (processor): Promise<ICheckout> => {
const strategy = getStrategies().find(strategy => strategy.processor === processor)
if (!strategy) {
throw new Error({ message: `Processor '${processor}' is unknown to checkout method.` })
}
return strategy.handler(order)
}
Another example: security policies
const userAgentChanged = ({ userAgent, token }) => ({
test: () => {
return userAgent && token.user_agent !== userAgent
},
execute: () => {
// should not be possible so deny access
console.log('user agent changed from %o to %o', token.user_agent, userAgent)
throw new AuthenticationException({
message: `We're detecting awkward account activity. Resetting your current session for security concern.`,
})
}
})
const userIpChanged = ({ ip, token }) => ({
test: () => {
return ip && token.ip !== ip
},
execute: () => {
// IP can change because of network change
console.log('[security] IP changed from %o to %o', token.ip, ip)
}
})
const userIpAndUserAgentChanged = ({ ip, userAgent, token }) => ({
test: () => {
return userAgent && ip && token.user_agent !== userAgent && token.ip !== ip
},
execute: () => {
// TODO: should lock. Same as when velocity change is too big
console.log('[security] IP & user agent changed')
}
})
const userVelocityCheck = ({ ip, token }): SecurityStrategy => ({
test: () => {
// if velocity > 300km in the last 1h then flag deny access
// TODO
return false
},
execute: () => {
// TODO: calculate velocity = delta(currentLocation - previousLocation)
}
})
const securityChecks = (options, strategies) => {
const {token, ip, userAgent} = options
for (const strategy of strategies) {
if (strategy.test()) {
strategy.execute()
}
}
}
Tweet
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>✨✨✨ Strategy pattern ✨✨✨
— Optimus Debugger✨ (@babacarcissedia) May 18, 2020
- can make your code look nicer ✅
- allows you to focus on abstraction ✅
Comprehension is key to turn lines of code 💻into business results 💰!
Full source code below
👇🏾👇🏾👇🏾 pic.twitter.com/2bdaXd8oPm