Say I have a bakery and an inventory of ingredients:
enum Ingredient {
case flower = 1
case sugar = 2
case yeast = 3
case eggs = 4
case milk = 5
case almonds = 6
case chocolate = 7
case salt = 8
}
A case's rawValue
represents the inventory number.
Then I have two recipes:
Chocolate Cake:
- 500g flower
- 300g sugar
- 3 eggs
- 200ml milk
- 200g chocolate
Almond Cake:
- 300g flower
- 200g sugar
- 20g yeast
- 200g almonds
- 5 eggs
- 2g salt
Now I define a function
func bake(with ingredients: [Ingredient]) -> Cake
Of course I trust my employees, but I still want to make sure they only use the right ingredients to bake a cake.
An Alternative Approach: Using an Option Set Type
Another approach is letting your
Ingredient
be anOptionSet
type (a type conforming to the protocolOptionsSet
):E.g.
Applied to your
bake(with:)
example, where the employee/dev attempts to implement the baking of a chocolate cake in the body ofbake(with:)
:Along with a call to
bake(with:)
using the given available chocolate cake ingredients:You could do something like this in Swift:
In this example i am using the enum
Ingredients
as a namespace for all my ingedients. This also helps with code completion.Then, create a protocol for each Recipe and conform the ingredients that go in that recipe to that protocol.
While this should solve your question, I am not sure that you should do this. This (and also your pseudo-code) will enforce that no one can pass a ingredient that does not belong into a chocolate cake when baking one. It will, however, not prohibit anyone to try and call
bake(with ingredients:)
with an empty array or something similar. Because of that, you will not actually gain any safety by your design.I don't believe it is possible to perform a check like this at compile time. Here is one way to structure your code to do this at runtime:
The
bake
method looks like this:Now I can say:
... and be sure that only valid ingredients were used.
Static Solution:
If the recipe quantities are always the same, you can use a function in the enum:
Usage:
Dynamic Solution:
If the recipe quantities are changeable -and that's what I assume-, I cheated a little bit by using a separated model class :)
It will be as the following:
Usage:
Even if those are not the optimal solution for your case, I hope it helped.
I think you should list ingredients for specific recipes as:
and then just check if that list contains the required ingredient.