The strategy pattern: write BETTER PYTHON CODE Part 3

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

https://github.com/egges/betterpython/blob/main/3%20-%20strategy%20pattern/strategy-after.py#L68

if len(ticket_list) == 0:
   print("There are no tickets to process. Well done!")
   return

Just a heads up, the standard pythonic way to express this is:

if not ticket_list:
    return
👍︎︎ 3 👤︎︎ u/ElevenPhonons 📅︎︎ Feb 05 2021 🗫︎ replies

Thanks, it was very useful for me.

👍︎︎ 2 👤︎︎ u/my_name_is_karagorn 📅︎︎ Feb 05 2021 🗫︎ replies

Great video, thank you!

I have some feedback:

  • instead of typing the implementation of the strategies, I think it would have been nice to retractor the code from the if-else block into those methods in the strategy classes. I say this because people usually realize they could have used the strategy pattern after the fact, and refactoring would emphasize that it's the same code that's being executed.

  • one of the disadvantages of the functional approach is that you can't pass other parameters into the strategy functions unless you have nested methods (like in java or c#). Not sure if it's possible with Python as it's not my main language. The up side of strategy classes is that you can pass in other information in the constructor, which sets up the factory pattern for one of your videos if you haven't covered it already :)

👍︎︎ 2 👤︎︎ u/NoTieAtBlackstone 📅︎︎ Feb 05 2021 🗫︎ replies

I enjoyed your video, there are two suggestions I would make regarding the code:

In python you should never name a variable the same as a reserved word such as list. This actually overwrites the built in list function. So for example the following code will produce the following results.

list = "this is a string"
a = list()
>>>
Traceback (most recent call last):
  File "<pyshell#1>", line 1, in <module>
    a = list()
TypeError: 'str' object is not callable   

Additionally, the copy methods you are using only return "shallow" copies of the input list. Which means that any changes you make to the copy also happen to the original list.

More info regarding copy can be found here in the python documentation: https://docs.python.org/3/library/copy.html

Keep up the good work!

👍︎︎ 2 👤︎︎ u/bbatwork 📅︎︎ Feb 08 2021 🗫︎ replies
Captions
in this video i'm going to give you an example of the strategy design pattern in python and how you can use it to write better code quite often it happens that you have several options for doing something in your code and depending on the situation you want to do something different for example if you have customer support software you might want to change the way support tickets are processed depending on how busy it is or how many staff you have available on that particular day or what if you're building a vr application and depending on the vr set that's used you want to use a different rendering algorithm without having to change the code these are perfect examples of where you could use a design pattern called the strategy design patterns are generic solutions to problems that you encounter as a developer the original design patterns book is already quite old it's from 1995 but a lot of the patterns are still very useful today even though some of them can be tweaked a bit to better use the latest features of programming languages nowadays the strategy pattern is a good example of a pattern that has stood the test of time it's widely used and any serious software developer should have this pattern in their collection of tools i use it quite a lot throughout my code as it helps me write code that's easier to change and if you're working in a startup like me things change all the time i'm going to show you an example in python where i'll apply the strategy pattern so you can see how it works so if you're ready let's dive in this example deals with handling support tickets we have a support ticket class that has an id it has a name of the customer and a description of the issue and we have a class customer support that maintains a list of tickets it has a method for creating a ticket it also has a method for processing tickets and then finally when it's actually processing the tickets just for testing it's now printing out the ticket information if you look at the process tickets method you see that it has a processing strategy that is that there are various ways that you can process tickets in this case you can use first in first out you can also use a first in last out strategy or you can use a random strategy and this defines the order in which the tickets are handled you can see there's an if else statement here below that depending on the value of this processing strategy that you provide does something else so if it's first in first out it simply goes through all the tickets in the list of tickets and then processes them if it's first in last out it creates a reversed version of that list and handles the tickets in the reverse order and if it's random it creates a copy of the list shuffles the tickets that are in the list and then just handles each of the tickets in that list to test this you simply create a customer support instance then you register a few tickets and then you process the tickets so in this case it's first in last out let's see what that looks like so you can see that john smith was the first person who regis registered the ticket and he was also processed last now if i change this to first in first out we get a different behavior you see that john's ticket is actually processed first the problem in this code obviously lies in this if else statement here because of the if else statement process tickets is quite long if you want to add more strategies it means you need to extend this part and then the process tickets method becomes even longer so currently process tickets has weak cohesion because it's responsible for not only processing the tickets but also for implementing each of these different strategies now the strategy design pattern aims to solve exactly this kind of problem where you have a method that tries to do too many things that has to choose between a strategy of doing something differently in the classic strategy pattern we're creating a class for each of these processing strategies and that class has a one method that does the actual processing that's called from process tickets we define the interface for this class using an abstract base class so let's first import that and then let's create a basic strategy class we have a ticket ordering class that has one abstract method called create ordering and depending on the actual strategy that i use philo fifo random etc create ordering will have a different implementation so create ordering needs a list of tickets and in python we can provide type hints to specify what that looks like and it also returns a list of tickets and obviously in the abstract class the method does nothing because there is no implementation yet so this defines what an ordering strategy looks like it has a create ordering method you put in a list and you get back a list that follows this ordering that you want now what you can do is create subglasses for each of these specific ordering types for example i could create a first in first out ordering strategy not fifi that's a dog fifo first name first out ordering strategy and it's a subclass of ticketing ordering strategy and that means it needs to implement the create ordering method so the first in first out ordering strategy is pretty simple it simply follows the list from left to right so what we can do here is simply return a copy of the list now if we want to create a first and last out ordering strategy let me copy this code to make this a bit quicker first in the last out then the create ordering should not just return a copy of the list but it should actually return the reverse of that so i'm creating a copy i'm reversing the list and then i'm returning that copy here and similarly i can also make a random ordering strategy so now i have three classes fifo ordering filo ordering and random ordering and each of these implements particular strategy for dealing with the list of tickets now what i can do in the customer support class is actually change the process tickets method to no longer get a string like this but actually get a strategy object that it can use so i'm going to change this here this is a processing strategy is not a string but it's a ticket ordering strategy then i can split all this code into two steps first i create an ordered list according to the processing strategy and then i simply process each of the tickets so first create the list and for that i'm going to use the processing strategy and i'm going to call the create ordering method and that needs the list of tickets so this is now my ordered list of tickets according to the processing strategy that i chose now the only thing you need to do is go through the ticket list and process each of the tickets i'm going to copy this code here and then we of course need to go through this list so now i'm processing the tickets and now i can remove the whole if statement that's here so now you see it's very simple process tickets has a ticket ordering strategy it doesn't know what the ordering actually is that's the responsibility for the ticket ordering strategy the only thing it does is it creates the ordered list and then it processes the tickets now one minor cleanup thing is that we might want to create this ordered list before we check if the list is empty because the processing strategy may actually remove things from the list we're not sure so we're going to move this here to the top like this and then i'm going to check for the length of the ordered ticket list so now if you look at the final code we still need to create the actual instances of these classes because process tickers no longer accepts a string so to reproduce this in this example random behavior let's create a random ordering strategy that we give to the process tickets methods and then when we run this code we obviously get an error because that always happens so let's see it says that support ticket is not defined why is support ticket not defined support ticket is not defined because the class is below these classes so i need to put this class up on top and then that problem should be solved so there we go let's try that again and yes there we are so now i'm using the random ticket processing strategy if i want to use another strategy like the first in first out strategy i simply replace the processing strategy instance that i pass to the process ticket method so then you get this now the nice thing here is that process ticket itself doesn't know anything about the ordering strategy that's used it simply creates the ordering and then goes through each of the tickets so that reduce the responsibility of the process tickets method leading to higher cohesion and the nice thing what you can do now is create new strategies and you won't have to change anything at all in the process tickets method for example perhaps i'd like to have a black hole ordering strategy that simply throws away all of the tickets so let's create a black hole strategy like this and now i add here the black hole ticket ordering strategy and then there are no tickets to process great this is the perfect way to deal with customer support so what you see here is a classic example of the strategy pattern that's based on the principles of object oriented design so we're using abstract classes and subclasses of these abstract classes to define the strategies and that's how it works i'm going to show you an example of how to do this in a bit more simple way because python has support for functional programming we can use functions directly instead of using these classes and this is actually something that i generally prefer to do when i'm creating a strategy pattern is to not having to create all these classes but simply use the functions themselves so let's see how that works for this more functional approach to the strategy pattern we're not going to need an abstract class so i'm simply going to remove this class here and then these classes are each going to turn into a simple function i'm actually also going to remove the class definitions here and i'm going to rename these functions so that they specify the strategies that they represent so now you see this is much shorter we simply have a function that gets a list and that returns a list and actually this self is also no longer needed here so these are now just basic python functions for each of the ordering types and instead of expecting here a ticket ordering strategy we're simply getting a process and strategy function so let's call this process strategy function and then i'm simply calling the function here directly and now when i create the application i don't need to create this object anymore but i can simply pass the function name so first in first out ordering and then if i call this method now we're getting the first in first start ordering and if i change this to the random ordering function then we get a random ordering overall using functions in this way is a bit shorter obviously you're not really defining the interface as strictly in this case because arguably anything could be passed here as a parameter we don't know that processing strategy is actually a function there are no type hints here we could add a type in to be more strict i'll just quickly show how that works if you want to add a type in for a function parameter you're going to need the callable keyword and let's use that here to specify what the type of this processing strategy function looks like so we need callable and callable is a function that has one parameter a list of support tickets and one return type and the way you specify this is by using these square brackets and it's a bit counter-intuitive i think a language like typescript is a lot cleaner in how you do this but well this is how you have to do it in python so if callable it has one input parameter which is a list of support tickets and it also has a return type which is also a list of support tickets now i can run it and it's it has exactly the same effect except that we're a bit stricter about what the process and strategy function is and what we expect there so this is a more basic version of the strategy pattern personally i find this a bit cleaner especially in languages like typescript where you can actually provide a neat definition of these kind of function types and you don't need all the classes and the overhead that it generates so it's a bit shorter it's a bit quicker and i generally prefer this kind of approach i've put a link in the description to the original design patterns book as well as a few other books and resources that you might find interesting if you want to learn more about the strategy pattern and design patterns in general there's also a link to a git repository with the code example from this video for you to play around with that's it i hope you enjoyed this give my video a like to support my channel and consider subscribing if you want to watch more of my content thanks for watching take care and see you soon [Music]
Info
Channel: ArjanCodes
Views: 25,585
Rating: 4.9870968 out of 5
Keywords: write better python code, strategy design pattern tutorial, design patterns through python, design pattern in python tutorial, strategy design pattern python, strategy design pattern python example, strategy pattern python tutorial, strategy design pattern, design pattern video tutorial, design patterns, software architecture, visual studio, design patterns tutorial, software architecture patterns, strategy pattern python, Software architecture interview questions
Id: WQ8bNdxREHU
Channel Id: undefined
Length: 15min 30sec (930 seconds)
Published: Fri Feb 05 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.