Last active
October 19, 2022 17:08
Strategy and factory patterns in TypeScript
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// see article with examples in JAVA here: https://dzone.com/articles/design-patterns-the-strategy-and-factory-patterns | |
// example for educational purposes shownig close and mature syntax of modern TypeScript | |
enum AccountTypes {CURRENT, SAVINGS, HIGH_ROLLER_MONEY_MARKET, STANDARD_MONEY_MARKET} | |
//////////////////////////////////////// | |
/// the interface that is used by the strategy | |
//////////////////////////////////////// | |
interface InterestCalculationStrategy { | |
calculateInterest(accountBalance:Number):Number; | |
} | |
//////////////////////////////////////// | |
/// Null object implementation and 4 account type related calculation stategies | |
//////////////////////////////////////// | |
class NoInterestCalculation implements InterestCalculationStrategy { | |
calculateInterest(accountBalance:Number):Number { | |
return 0; | |
} | |
} | |
class CurrentAccountInterestCalculation implements InterestCalculationStrategy { | |
calculateInterest(accountBalance:Number):Number { | |
return +accountBalance * (0.02 / 12); | |
} | |
} | |
class SavingsAccountInterestCalculation implements InterestCalculationStrategy { | |
calculateInterest(accountBalance:Number):Number { | |
return +accountBalance * (0.04 / 12); | |
} | |
} | |
class MoneyMarketInterestCalculation implements InterestCalculationStrategy { | |
calculateInterest(accountBalance:Number):Number { | |
return +accountBalance * (0.06/12); | |
} | |
} | |
class HighRollerMoneyMarketInterestCalculation implements InterestCalculationStrategy { | |
calculateInterest(accountBalance:Number):Number { | |
return accountBalance < 100000.00 ? 0 : (+accountBalance) * (0.075/12) | |
} | |
} | |
//////////////////////////////////////// | |
/// select and apply the correct strategy | |
//////////////////////////////////////// | |
class InterestCalculationStrategyFactory { | |
//Strategies for calculating interest. | |
private currentAccountInterestCalculationStrategy:InterestCalculationStrategy = new CurrentAccountInterestCalculation(); | |
private savingsAccountInterestCalculationStrategy:InterestCalculationStrategy = new SavingsAccountInterestCalculation(); | |
private moneyMarketAccountInterestCalculationStrategy:InterestCalculationStrategy = new MoneyMarketInterestCalculation(); | |
private highRollerMoneyMarketAccountInterestCalculationStrategy:InterestCalculationStrategy = new HighRollerMoneyMarketInterestCalculation(); | |
private noInterestCalculationStrategy:InterestCalculationStrategy = new NoInterestCalculation(); | |
public getInterestCalculationStrategy(accountType:AccountTypes):InterestCalculationStrategy { | |
switch (accountType) { | |
case AccountTypes.CURRENT: return this.currentAccountInterestCalculationStrategy; | |
case AccountTypes.SAVINGS: return this.savingsAccountInterestCalculationStrategy; | |
case AccountTypes.STANDARD_MONEY_MARKET: return this.moneyMarketAccountInterestCalculationStrategy; | |
case AccountTypes.HIGH_ROLLER_MONEY_MARKET: return this.highRollerMoneyMarketAccountInterestCalculationStrategy; | |
default: return this.noInterestCalculationStrategy; | |
} | |
} | |
} | |
getInterestCalc(type) : void {
eval("new InterestStrat"+type+"()");
}
class InteresStratTypeA implements Interface {}
class InteresStratTypeB implements Interface {}
Im working out another solution due to eval deprecation.
Did you ever figure out a better solution? I tried using an Enum and creating an object mapping each value to functions, but I could not get it to work in TS.
interface InterestCalculationStrategy {
calculateInterest(accountBalance:Number):Number;
test(type: AccountTypes): boolean;
}
// and then you implement test function for every strategy
class SavingsAccountInterestCalculation implements InterestCalculationStrategy {
test(type: AccountType) {
return type === AccountType.SAVING
}
calculateInterest(accountBalance:Number):Number {
return +accountBalance * (0.04 / 12);
}
}
class InterestCalculationStrategyFactory {
strategies = [
new SavingsAccountInterestCalculation(),
// ...all other strategies
]
getInterestCalculationStrategy(accountType:AccountTypes):InterestCalculationStrategy {
return this.strategies.find(strategy => strategy.test(accountType));
}
}
it could be like that too...
interface InterestCalculationStrategy {
calculateInterest(accountBalance:Number):Number;
}
// and then you implement test function for every strategy
class SavingsAccountInterestCalculation implements InterestCalculationStrategy {
calculateInterest(accountBalance:Number):Number {
return +accountBalance * (0.04 / 12);
}
}
class InterestCalculationStrategyFactory {
private static strategies = {
[AccountTypes.SAVING]: new SavingsAccountInterestCalculation(),
// ...all other strategies
}
static getInterestCalculationStrategy(accountType:AccountTypes):InterestCalculationStrategy {
return InterestCalculationStrategyFactory.strategies[accountType];
}
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
What solution would be better to get rid of that switch block?