I came across an article about LSP https://stackify.com/solid-design-liskov-substitution-principle/ . It seems that a strategy pattern could be implemented here but I cannot see how to implement it.
My idea is to let the brewCoffee be the strategy method, in which i state what kind of coffee should be brewed. The following is an overview over the design,
Here’s my solution so far:
BasicCoffeeMachine should only be able to brew filter coffee, while PremiumCoffeeMachine should be able to brew both filter and espresso. But as of yet, both machines can do both. Should i introduce another interface, one being for BasicCoffeeMachine and one for PremiumCoffeeMachine?
Any feedback overall?
How would you implement the strategy pattern in this case?
In your second diagram, the
PremiumCoffeeMachine and the
BasicCoffeMachine both inherit the composite aggregation of their generalization
The way you modelled the diagram is without any specific constraint. So any
IStrategy could be used with any
CoffeeMachine. If this is too flexible, you have several choices:
- Introduce a distinction between different families of
IStrategyand associate them only with the appropriate family of coffee machines.
- Redefine/Subset the association of specific coffee machine with specific strategies.
- Simply express the constraint in regarding the associated types. This is more flexible in the model.
Whatever your choice, the fact that a subclass contraints the usable strategies, i.e. strengthen the pre-conditions, the design will not be LSP compliant.
Other unrelated remarks: there is no need to repeat the composite aggregation of
CoffeeMachine for all its specializations: the specialized machines inherit the associations from their ancestors (by the way, the arrow head of inheritance should be hollow, i.e a large white triangle).
You can have a property “canMakeExpresso” which each class implements. And the method “makeExpresso” throws an error / exception if you call it for a class that doesn’t implement it.
You decide if you want a property “canMakeRegularCoffee” which returns true for all existing classes – and a method “makeRegularCoffee” which is always implemented.
Now these two properties are properties of the coffee machine that you don’t expect to change while the coffee machine is plugged in. You could also check if the coffee machine has water, has the right ground coffee or coffee beans etc. which you would assume to change at runtime. I’d keep those separate. So “canMakeExpresso” would return true even if the machine has no water.