Devreal

The Cake Pattern in Practice

Event: Peter Potts - the Cake Pattern in Practice

SBTB 2014, Peter Potts: The Cake Pattern in Practice

Recording: SBTB 2014, Peter Potts: The Cake Pattern in Practice

hi everyone we're offne so I'm going to talk about the cake pattern in practice so and I'm working for kicksay so it's a multi-game multi massively multiplayer game so okay so what is the cake pan so you know it's a software design pattern it was first explained by Martin nodeski and there was another paper on the web which is pretty easy to find by you and Espana and it's actually there's actually not that much on the web about it so when you actually want to use it in practice you have to well I've had to kind of make it up as I go along and make discoveries and I'm hoping to share some of those discoveries with you today so it its real purpose is it's really a dependency injection a way of doing dependency injection and also arguably aspect orientated programming but some might dispute that and it uses only Scala constructs so there's no extra magic added into the language and it's truly type safe all the way through and I don't think that's really true for many other di Frameworks so the the basic uh structure of of how you use it it well starting block is a component so we have this um say we've got this type called Foo which is a great type and we want to inject it in something so in the cake pan we set up a container called The Foo container components all right and then I can Define what Foo is inside that trait so it's trait inside a trade so it doesn't have to be like that but it restricts how you use Foo if you kind of force people to use it within the cake pan if you have it defined inside so in this particular case I've I've got a valve so I can only have one instance of food that I want to inject so this is a implementation of this so if I say I want to implement this Foo component and I'm going to call it the bar Foo component and this this is something I discovered along the way so I've actually created a trade for barfoot component and a companion object called barfu component so one of the the biggest complaints about the cake pattern is that it creates boilerplate so unfortunately I'm creating even more boilerplate here but but it has a lot of benefits so we actually Define here the the list of other components that this implementation of the food component actually uses so I've created a type Alias called dependencies so it's pretty clear what it is this is the list of dependencies so baz and quacks or whatever it's cool so and and here I'm actually defining the bathroom component which extends food component and then we have this notion of the the self-type here and this is where we basically Define the dependencies so rather than list them all here I've already defined them up here so there's kind of a connection there and then I've defined the actual implementation of Foo with barfu and just to show that these dependencies can be used I'm just calling the hash function on the two dependencies there and it's also important in this particular case where we're just having a single instance we've got to have it lazy here now the reason for laziness is because the way you use these components is you have to mix them in to make up some super object but the actual order in which those things are mixed in is you don't necessarily you don't want to depend on that because that's going to cause all sorts of problems so by having it lazy here we have all these Tendencies lazily available and then they should pick them up in the right order automatically so so while you've got that particular implementation in your mind I'm going to show you how you can easily mock this so one of the reasons to have a dependency injection is because you can mark all the dependencies so in this case I want to test a bathroom components so I know that it depends on backs and Quacks so I create two mocks for those two dependencies and now when I'm testing it I merely have to call food or hash function now that's going to force the lazy evaluation which is then going to call Basin quacks so I now verify that those two have been called so it shows how easy it is to to mock the dependencies so how do we put this all together into a big application so I've done in two steps in my applications first I have this concept of a module so a module is basically a collection of related components um so you know say you have a database component that you may have a particular postgres or MySQL and then various other transaction components that form a natural grouping so a module is a grouping of components so in this particular case I've got um I'm defining the bar Foo module as being bar Foo de baz and dob cook quacks component all put together and because I've defined these dependencies in terms of their companion classes it's easy for me to just say what the dependencies are for these three components I can basically say it's this dependency with these and with those and you know you may choose to actually collapse that down into something more specific or you can leave it super General like this so if it's super General like this if any one of those implementations has changed then this doesn't have to be changed as well and that's usually one of the problems with the cake pattern without without handling dependencies like this it can be very difficult to you have to copy and paste all those dependencies all the way down the chain and this kind of avoids a lot of that and then once you've defined all these modules defining the way in which you put in all these components together you want a an application context which is basically a collection of all the modules that make the application and so this is an application context in the same sense as you would in spring for example so so at this stage you should be having a class you shouldn't be shouldn't be a trade all right so there shouldn't be any dependencies left at this point this is a fully defined application context and you should be able to run the program at this point and so I just showed in my initial component it just had a vowel now the general pattern is that you have you know Foo component and then inside you define something called Foo now the type of that Foo and how it is set up can vary and that's where you know things can get really interesting so by having a vowel here I'm essentially setting up a Singleton scope uh dependency injection but if I change it to a def which is how you actually initi usually people start with that as a way of explaining the cake pattern but I'm doing it different way so if you do def that means every time you call Foo here you're going to get a different or you could get a different instance of food so this is no scope in this case because you know you're basically throwing them away every time you're getting them and so and how do you get any any any kind of intermediate scope so I came up with this other idea of having a with something component so in this particular case we the idea is you have with food component and now we can kind of have a narrower scope somewhere between Singleton and no scope so we can have width and then because it's with food then we've got to have a with Foo whatever in this case it's a it's a method and it takes a call by name so it takes a block of code which is taking food to something else so that we can use that so in this way we can control that if we can create it and destroy it at the end of this code block and we're controlling the scope of that food so that's one way of doing um sort of a sub a sort of restricted scope another way is using implicit so this what I call the session scope so in this particular case we've got a foo which is a method inside the food component but in this case we're expecting an implicit session parameter perhaps you know and so you can obviously how that would be useful in a web application you know this way every time so we have an implementation as an example so the foo we could have an implication called barfu and then bar food takes an implicit session and basically there's so in this particular case we're getting a new instance of barfu being essentially every time but it may get the same session in multiple different calls and you can obviously optimize that by making this whole thing more complicated but I wanted to keep it simple in this in this talk or simply-ish and oh yeah aop so yeah so this this is kind of extending the width notion so I've got a with transaction component so obviously I need a with transaction in there that I'm injecting so in this case it's a it's similar to the session scope so except in this case it's a block of code which doesn't take any it's just executing some block of code within this with transactions it's a bit like a DSL for transaction but in a cake pattern Style so you know arguably some might say this isn't aop because you know I'm not truly doing an independent point cut outside of the application but you know there is a if you look at the way you can do in Spring for example you can have at transactional which is you know annotation based aop so you can see that there's Apparel in the cake version you would just say I've got my method and I just say with transaction one plus two will be done within the transaction whereas in this case we are just annotating the method with attransactional and expecting something externally to you know to put a wrap around that and make sure it's a transaction so if you were testing this you would obviously want to make say a with no transaction component for example if you just want to test you know the OnePlus to make sure the OnePlus 2 gives you three you don't care that it's in a transaction so you can kind of null out the Implement you can mix in a with no transaction component into this so This actually doesn't do anything in your test uh okay so this is another another use case so time itself is interestingly that's a side effect so in a sense time itself is not really a good thing in functional programming so that really is out of touch with reality really but so obviously you want to be able to inject a clock into your application you probably want to trying to be able to control it otherwise it's very difficult to have tests which you've got different times every time so I've got a clock component here and in this case I might want to have an implicit vowel clock so if any if if I've injected this or mixed in this clock component into some part of the application then I can automatically pick up the clock and read the current time and I can obviously create mock implementations of this but I've got a system clock implementation of this which extends the clock component in this case the clock is equal to system clock and in this case I haven't put lazy on because it's not actually depending on anything else so there is no possibility or race condition here now this is a kind of a special component that I have in my systems and it's not really a component but I mix it in with all the other components anyway so this is a way in which I can have set up and tear down because sometimes when you want to inject something there needs to be something done at setup and something done at tear down for that apparently like the actor system for example you might want to shut it down at the end for example so I usually mix in this or into the components that require some kind of setup and set down and then they can call a setup hook and a tear down hook which gets essentially added to the queue of things that need to be set up and torn down and then there's actors so actors throw in okay A little bit of a confusion into this whole thing um so say I've got a foo actor so I Define an actor and so I've done this kind of cake pattern in a slightly different style words not really cake pan but I'm taking the actual cake pan context the whole application context and I can throw it into the actor into the actual Constructor and I just call it injector and then its dependencies are through active dependencies so I can say the foo actor depends on bar component and so I'll move on swiftly because we're running out of time so the actor props component so this would be there are different ways in which you instantiate the actors so you know we have this notion of props so one of the funny things about actors is that they're really great in many ways but one of the ways which kind of puzzles me a little bit is there's actually quite a lot of type safety kind of lost in the active system so you see here props and it could be anything um you know but I'm notionally thinking that this is a full actor being in in this props and then when you actually instantiate the props um we've got two different ways well I was the recommended way which is to do class of Foo actor and then pass in here which this could be anything it doesn't actually have to the full actor doesn't have to have a Constructor this is basically type unsafe or not time safe whereas if you do it this way which is now considered deprecated it is typesafe so I don't know I think there's definitely a solution required there then there's the test kit so I'm just showing how we can test the actor using the actor test kit so I think I'll have to skip through that but it'll be on my slides which you can look at on the on the website afterwards and then there's the actor component so maybe you want to instantiate an actor so obviously to instantiate an actor you need to have other Tendencies such as you know an active Factory and set up tear down so this is an example of how I'm using setup teardown so you can see here I've setting up the actor because this actor is lazy I've had a lazy vowel so that means when you start up the system this actor isn't actually running so you do a setup hook which just calls can call any method here could be two string just the fact you're referencing it forces it to become not lazy anymore at this point and actually evaluated so the actual start running and last slide um yeah so kickseye massively multiplayer online games you can join our team of scarlet server developers if you want my name is Peter Parts I'm principal architect and that's my email address any questions uh yes so is there a difference between the cake pattern and the thin cake pattern and the weight thin cake I don't haven't heard of the thin cake pan I'll have to look that one up I don't know that one well actually in in many he's asking what the use case for a Singleton pan I mean there are a lot of use cases a single pattern I mean starting up a web server you're going to have one HTTP server right you mean why would you want to have a single as opposed to having an object for example you mean then you wouldn't be able to inject in mock dependencies so you want a way of having only one instance of something in runtime but you want to win which you can inject other things into it in a test is that what you meant well you well if it's death for example instead of vowel then you potentially instantiating a new one every time so which maybe maybe you want that in the tests and maybe you don't want it in so you could Define as a Def and then in the actual implementation make it stricter so you could have it deaf in the actual uh like in like in Heavens you could have deaf Foo and but then actually in the implementation you could still have a vowel because that's that's just making it reducing the scope reducing the possibilities so you could do that but you can't do it the other way so it's really just how tight you want to make you know if you know that any use of that component is only going to need one so like a clock you're unlikely to have an application which got more than one clock or active system it's unlikely to have more than one act system or web server or even database well you might then you can always have a deaf and you can have a function and in passing a string so you can then pull different like different named versions of that component for example like you would induce you know like named okay yep finished all right thank you