I'm trying to implement strategy design pattern, and want to know if I do it correctly.
Lets say, I have class FormBuilder which uses strategy from list below to build the form:
SimpleFormStrategyExtendedFormStrategyCustomFormStrategySo the questions are:
FormBuilder, and not passing strategy from outside?FormBuilder class.Draft code example
class Form {
// Form data here
}
interface IFormStrategy {
execute(params: object): Form;
}
class SimpleFormStrategy implements IFormStrategy {
public execute(params: object): Form {
// Here comes logics for building simple form
return new Form();
}
}
class ExtendedFormStrategy implements IFormStrategy {
public execute(params: object): Form {
// Here comes logics for building extended form
return new Form();
}
}
class CustomFormStrategy implements IFormStrategy {
public execute(params: object): Form {
// Here comes logics for building custom form
return new Form();
}
}
class FormBuilder {
public build(params: object): Form {
let strategy: IFormStrategy;
// Here comes strategy selection logics based on params
// If it should be simple form (based on params)
strategy = new SimpleFormStrategy();
// If it should be extended form (based on params)
strategy = new ExtendedFormStrategy();
// If it should be custom form (based on params)
strategy = new CustomFormStrategy();
return strategy.execute(params);
}
}
You asked 2 questions that are not directly linked to TypeScript. The code can be directly converted to C# / Java, the usual mainstream OOP languages. It's even more interesting because it's about both Design Patterns and SOLID principles, both pillars of the Object Oriented Programming.
Let's answer it specifically before being more general:
- Is it correct to select strategy inside
FormBuilder, and not passing strategy from outside?
Yes. The opposite leads to a FormFactory wrapper without much interest. Why not calling directly strategy.execute()?
- Doesn't this violates open closed principle? So, if I want to add one more form strategy or to remove an existing one, I have to edit the
FormBuilderclass.
Builders and Factories are tightly couple to the underneath created types by design. It's a local violation of the OCP but with them the client code is decoupled from form creation implementation details.
Misc comments
IFormStrategy is firstly an (abstract) Factory: it creates a Form. A better name should be IFormFactory { create(...): Form; } (or just FormFactory, the "I" prefix being more common in C# than in TypeScript). It's a Strategy for the FormBuilder but not intrinsically. By the way, the term Strategy is rarely used when naming classes, because it's too generic. Better use a more specific/explicit term.formBuilder.withPartA().withPartB().build();. This class selects the appropriate Factory/Strategy based on the input params. It's a Strategy Selector, or a Factory of Factory :D that also call the factory to finally create the Form. Maybe it does too much: just selecting the factory would be enough. Maybe it's appropriate, hiding complexity from the client code.interface/type but without a wrapping object /class that may not bring more value. The client code just have to pass another function which can be a "simple lambda" (fat arrow function).params argument is used by both the Builder and the Factories. It would be probably better to split it, to avoid mixing distinct concerns: strategy selection and form creation.createSimpleForm(simpleFormArgs), createExtendedForm(extendsFormArgs)... Each method will instanciate the associated factory and call it create(formArgs) method. This way, no need for a complex algorithm to select the strategy, based on ifs or switchs which increases the Cyclomatic Complexity. Calling each createXxxForm method will also be simpler, the object argument being more little. In design pattern terms for Strategy, your FormBuilder plays the role of a Context, which holds the reference to the current strategy in use (IFormStragegy). The strategy is passed from outside (using setter) so it is open to extension (OCP). So regarding your questions:
FormBuilder, and not passing strategy from outside?It is not correct implementation of strategy. You should create instances of your strategy and pass it to the context. The strategy therefore can be swapped at run-time.
Yes it does, you cannot make a new strategy known to the FormBuilder without changing it.
You can look here for an example.
FormBuilder context = new FormBuilder();
IFormStrategy simple = new SimpleFormStrategy();
IFormStrategy extended = new ExtendedFormStrategy();
IFormStrategy custom = new CustomFormStrategy();
context.setStrategy(simple);
context.build(/* parameters */)
context.setStrategy(custom);
context.build(/* parameters */)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With