This is an example.
Assume the following constraints:
- A shop can only have 5 products.
- A food shop can only add Food products.
- A clothes shop can only add Clothes products.
- A mixed shop can add both products.
I have the following classes:
AbstractShop
- FoodShop
- ClothesShop
- MixedShop
AbstractProduct
- Clothing
- Food
functionality:
AbstractShop>>addProduct: aProduct
(products size < 5)
ifTrue:[^ products add:aProduct]
ifFalse:[ self error: 'A shop can only have five products' ]
FoodShop>>addProduct: aProduct
(aProduct isMemberOf: Food)
ifTrue:[^ super addProduct: aProduct]
ifFalse:[ self error: 'You can only add food products to a foodshop' ]
ClothesShop>>addProduct: aProduct
(aProduct isMemberOf: Clothing)
ifTrue:[^ super addProduct: aProduct]
ifFalse:[ self error: 'You can only add clothing products to a clothes shop' ]
"ClothesShop doesn't override the addProduct, because it can add them both."
How can I avoid checking the type of the product, to see if it can be added to the shop? I want to avoid this, because it is a bad smell. I tried figuring it out with Double Dispatch, but it seems to make the code more difficult to maintain.
This is a pattern we have used several times. The class declares which things (or instances of which classes) can handle.
And you should not redefine addProduct.
But if smells say something, the magic constant 5 in the capacity check should also be extracted, and you should have the validation of the product separated from the add.
I never liked to call a class AbstractSomething, that is also a bad smell. In your case, using just Shop and Product is abstract enough.