
Join the Conversation!
Subscribing gives you access to the comments so you can share your ideas, ask questions, and connect with others.
Mastering JavaScript patterns will give you a deep understanding of coding principles and problem-solving abilities. Learning these patterns will set you apart as an advanced developer and open up opportunities to become a consultant or mentor. The workshop covers a wide range of patterns, including object-creational, factory, singleton, prototype, decorator, adapter, composite, proxy, observer, strategy, and command. It also discusses the importance of architectural patterns and provides further resources for learning and practice.
How do I remove the blur effect from my CSS?
I removed but the blur is still there. Any ideas?
filter: blur(5px);
Does work for removing blur from modals?
backdrop-filter: none;
Subscribing gives you access to the comments so you can share your ideas, ask questions, and connect with others.
How did you manage to remove the blur property and reach here?
Upgrading gives you access to quizzes so you can test your knowledge, track progress, and improve your skills.
By logging in, you'll unlock full access to this and other free tutorials on JSM Pro.
Why? Logging in lets us personalize your learning experience, track your progress, and keep you in the loop with new workshops, coding tips, and platform updates.
You'll also be the first to know about upcoming launches, events, and exclusive discounts.
No spam—just helpful content to level up your skills.
If that sounds fair, go ahead and log in to continue →
Enter your name and email to get instant access
##Looks like we found a thief monkey By the way, I liked the trick how you reached till here. You have a good sense of humor. You will improve a lot if you join our course with this passion.
var
(function-scoped, outdated)let
(block-scoped, modern and recommended)const
(block-scoped, cannot be reassigned)_
, or $
let let = 5;
is invalid)myVar
and myvar
are different)string
, number
, boolean
, null
, undefined
, bigint
, symbol
Objects
, Arrays
, Functions
Subscribing gives you access to a brief, insightful summary of each lecture to stay on track.
00:00:17 Hello, hello, mastering JavaScript patterns.
00:00:21 Let's take it just one step further.
00:00:24 If there was one takeaway that you want to get from this conversation or from this workshop, the one thing that you want to learn or implement after,
00:00:30 I mean, there must be a reason why you're here.
00:00:34 Refresh memory, code more clearly and efficiently.
00:00:37 I'm guessing better code architecture, patterns to work faster, looking to learn more JS patterns, how to apply them to daily work,
00:00:45 catch up to other team members.
00:00:46 Okay.
00:00:47 I think we're going to cover all of these.
00:00:50 Actually, you might wonder what are going to be some of the things that we will be talking about.
00:00:55 And a lot of these will make a lot of sense.
00:00:59 So let's talk about the workshop specifically.
00:01:02 The world won't turn upside down if you decide not to learn them.
00:01:08 JavaScript patterns, of course.
00:01:09 But your world might.
00:01:12 because learning principles or patterns are what sets a normal developer apart from a skilled developer.
00:01:21 So for that reason, by the end of this workshop, you'll have a deep understanding of the coding principles and problem-solving abilities which are highly
00:01:32 valued in the tech industry.
00:01:34 knowledge of when and how to apply different patterns, helping you make better decisions about code architecture and design,
00:01:45 lead role in the tech industry.
00:01:47 I already saw some tech leads with us right now, but a lot of mids and seniors that might want to have a better understanding of coding architecture.
00:01:57 Also, better efficiency implementing these well-tested solutions resulting in fewer bugs.
00:02:06 Also, a greater sense of accomplishment, job satisfaction, and less stress about production breaking because we'll write some high-quality code.
00:02:16 And of course, the potential to become a consultant or mentor with better skills and deeper understanding of software principles.
00:02:28 I think we covered a lot of the things that you might have sent in the chat right now of what you want to get from it.
00:02:34 I'll try to deliver on that.
00:02:37 Hi there.
00:02:38 I'm Adrian.
00:02:39 I'm the founder and CEO of JavaScript Mastery, where I teach millions of developers, believe it or not so far, how to code and improve their skills.
00:02:48 I'm also a global speaker at different events and we do a lot of socials as well.
00:02:53 We primarily focus on YouTube where we teach project-based development.
00:02:58 And also, another thing is that I'm a GitHub star.
00:03:03 And this has been something that not a lot of people talk about.
00:03:06 It's not a well-recognized term, but it is a recognition given by GitHub to developers that basically do a lot of open source and education work.
00:03:18 So I'm glad that I was able to be recognized by them for open source.
00:03:23 And I hope today to share some of that knowledge with you.
00:03:27 So I want to welcome you and set and change the way that you think about the code and how we approach it for a bit.
00:03:35 So we'll cover an introduction to JavaScript patterns, the importance of learning JavaScript patterns, the main categories of JavaScript patterns,
00:03:45 including object, creational patterns, structural patterns, behavioral patterns, and architectural patterns.
00:03:54 We'll also cover detailed explanations and code examples of various subtypes, including factory, singleton.
00:04:03 There's going to be a lot of these different things that we'll cover.
00:04:07 There's going to be, I think, decorators, composites, proxies, observers, strategies.
00:04:13 These terms might mean nothing, but they also might mean something.
00:04:18 You might have heard of them.
00:04:19 But again, if you don't know what a decorator pattern is, that's totally OK.
00:04:24 We'll explore all of them today.
00:04:27 So, think of patterns like blueprints for writing code in a structured and efficient way.
00:04:37 There are common approaches or strategies to help developers solve recurring problems in their code.
00:04:46 So as we code, and as people have coded many, many years and decades before us, we started figuring out some patterns.
00:04:55 Patterns on how to organize code, manage complexity, achieve specific goals like improving performance, or just making code more reusable.
00:05:06 So let me just remind you that these approaches are not commonly known or used in everyday applications.
00:05:14 However, you will encounter them in enterprise level projects, frameworks, or libraries in generally more complex environments.
00:05:25 So learning these is important if you want to stand out as an advanced developer or maybe build a library or a framework of your own.
00:05:35 So you might think, what proof do I have?
00:05:39 Show me some code.
00:05:42 And I checked out a couple of open source code bases to find some patterns that are actually there.
00:05:50 And let me try to see if I can pull it up right here and share it right now to give you some real tangible things that are right now live on GitHub.
00:06:01 So first of all, here I have Vercell, Next.js' thing.
00:06:06 So this is coming from directly from the Next.js framework within their code base, where they use a factory pattern to create a dictionary service in one
00:06:17 of their code examples.
00:06:19 So here we go.
00:06:20 A JavaScript pattern in the wild.
00:06:23 Also, if you dig deeper into the Next.js code, you'll also find another example, and you'll see that one of their server features uses the adapter pattern
00:06:36 right here.
00:06:37 So yeah, they're actually there.
00:06:39 And if you might not understand what that is, this code might be a bit hard to read and understand, but they are there.
00:06:48 And it's not only Next.js.
00:06:51 For example, Node.js uses the decorator pattern to connect, upgrade, and handle data events.
00:07:00 And they also use the proxy pattern, which you can see right here, for many different reasons.
00:07:08 In this case, to create HTTP agents.
00:07:13 And if you search for performance observer like here or in different files, you'll find that the observer pattern is used a lot to track the performance.
00:07:22 Angular as a framework also uses a lot of these architectural patterns like the MVC.
00:07:29 Some of you might know what MVC stands for.
00:07:31 It's model or model view controller.
00:07:34 There's also MVVM, right?
00:07:37 Which helps us structure the application.
00:07:40 So they are everywhere, right?
00:07:43 And the pattern can be anything.
00:07:45 For example, If you are a React developer and you have been using any kind of hooks, which I bet 99% of you who use React do,
00:07:56 you're basically following the hooks pattern that allows us to write stateful logic without writing a class.
00:08:04 Or you might have used the higher order component pattern to reuse component logic by passing down the props.
00:08:13 But again, it's not just React.
00:08:16 You may have unknowingly used the data provider pattern in Vue, which manages and shares data across multiple components.
00:08:25 It promotes the separation of concerns by centralizing data logic and in a dedicated component, making it easier to manage and then share data across different
00:08:37 components within an application.
00:08:39 your app doesn't have to care where the data is being fetched for.
00:08:43 That's exactly what Vue does using this pattern.
00:08:47 Do you get it?
00:08:48 It's not only Vue as well, and it's not only React.
00:08:52 A lot of these are reused across languages, across frameworks, across libraries.
00:09:00 These basically are some fundamental code principles that are not even like syntax or code or a framework or a library.
00:09:13 They're basically abstract concepts we have figured repeat in time and can be very helpful if you use them properly.
00:09:24 So I want to make that clear, right?
00:09:27 Patterns are principles more so than anything else, ideas, if you will.
00:09:34 So You get the idea, right?
00:09:37 So, and if you've been working for three to five, maybe even 10 years without knowing a single pattern, that's totally okay.
00:09:46 A lot of developers are like that.
00:09:49 But then you might ask, why should I spend time learning them?
00:09:53 Why care?
00:09:55 Well, let's talk about that for a second.
00:09:59 If you only learn how to use an axe for chopping down trees, well, that's all you know.
00:10:05 But if you take some time to really understand it, you'll see it can be helpful in other emergencies too.
00:10:10 And coding patterns are like that.
00:10:13 There are ways of writing code that have been tried and tested by the community to make it better.
00:10:20 And learning these patterns can help you solve all sorts of problems using certain design rules.
00:10:27 And it even helps you enhance your problem-solving skills beyond the boundaries of frameworks.
00:10:35 So once you've mastered these concepts, you'll essentially be the Sherlock Holmes of coding saying things like, that's too elementary.
00:10:43 I suggest using a factory pattern to your peers.
00:10:47 So yeah, you'll be able to have a deeper understanding of code, not just code, but the principles behind why you're writing that code to begin with.
00:10:58 So let's talk about some widely used JavaScript patterns that you may have even used unknowingly before.
00:11:08 The first category we'll cover today are object creational patterns.
00:11:16 These patterns focus on object creation mechanisms, providing flexibility and reusability when it comes to creating objects.
00:11:27 The most popular example of a creational pattern is a factory.
00:11:34 As you can see by this illustration here, you have a single factory class most often, which then creates multiple smaller objects.
00:11:45 It's basically you define an interface that looks something like this for creating an object, but then you define and let subclasses decide which class
00:11:57 to initialize.
00:11:59 Essentially, the factory pattern is about defining a method for creating objects in a superclass, but allowing subclasses to alter the type of objects
00:12:10 that will be created.
00:12:12 I hope I didn't lose you there.
00:12:15 But that's the idea.
00:12:16 You create a function, then spits out additional objects.
00:12:20 Essentially, the factory pattern is about defining things, creating new objects, and this can be used in many different cases,
00:12:29 such as with sending notifications via different channels like email or SMS.
00:12:36 The underlying logic behind sending any kind of notification is the same, but the channel is different.
00:12:44 Email, SMS, phone, or anything else, right?
00:12:49 Next, we have different use cases such as covering multiple payment gateways.
00:12:55 Once again, the underlying principle is the same, but there are different gateways that have specific code.
00:13:02 And then finally, theme management, where you can also cover different use cases on front end.
00:13:08 And basically there are so many examples where you can use this pattern, but think of it like this.
00:13:15 I'm going to go back for a second.
00:13:17 Like whenever your child objects have to follow specific principles, but then be just a tiny bit different from the factory.
00:13:26 I mean, a car factory is a great example, right?
00:13:29 You have a car factory which creates cars, but each car can have a different color, but still it has four wheels and it goes.
00:13:41 That's the idea.
00:13:42 So let's see what else we can do with this.
00:13:46 or not what else we can do, let's actually go ahead and code it out.
00:13:51 Can I get some yeses or nos on who has downloaded the Starter repo?
00:13:57 It should look something like this if you have downloaded it.
00:14:01 And I can see a lot of people have downloaded it already, which is great.
00:14:06 And you'll notice that there are two different files in most of these.
00:14:10 A problem file, a solution file, and then a readme.
00:14:15 Solutions files for most situations will be empty.
00:14:21 In some cases where there's more code, I put them already there for you so you don't have to type it out.
00:14:26 But in most cases, they will just be empty.
00:14:30 So we can code them all together.
00:14:32 But the problem is not really kind of a typical algorithm problem where it's just a question that you have to solve.
00:14:41 Rather, it is an already done solution.
00:14:44 Like if you try to navigate over to this repo, like we can cd into zero one factory and run or yeah, run npm run problem.js Let's see if I have it,
00:15:01 problem.js.
00:15:04 Oh, no, let me see.
00:15:05 Yeah, we'll look into that later on, but basically this code will run.
00:15:13 I'll try to figure out, I might be doing a lot of React and Next.js, so I'm actually mistaking how to run a basic node script,
00:15:20 but that should be good.
00:15:22 Yeah, I think we can do node solution.js or problem.js, that should be good.
00:15:26 Yeah, that's good, that's good.
00:15:28 So basically, you can see if you try to run it, it already runs and it gives me a proper solution, right?
00:15:36 So it's not that this is a question and this is an answer to that question.
00:15:41 This is the code.
00:15:43 without a code pattern implemented that already works, how you would typically write it.
00:15:48 And then here together, we will solve the same problem, but now using a code pattern.
00:15:56 And then also there's a readme where in case I fail to mention something, we can go over it together, or you can also go through this repo by yourself
00:16:05 and then just check it out if you want to learn about it later on.
00:16:10 Also, let me try to remember what is the preview.
00:16:13 I think here we can open up a preview for each one of these READMEs.
00:16:17 If anybody knows a quick shortcut to open up a preview of a README, that would be great.
00:16:24 Just let me know.
00:16:24 Okay, I can see it's Command-Shift-V.
00:16:29 This is the one that I didn't necessarily know about.
00:16:32 Let me try to open it.
00:16:34 Maybe here.
00:16:35 Oh, there we go.
00:16:35 You can also press there, Show Source, and then we have a preview right here.
00:16:39 Cool.
00:16:40 Great work on the shortcuts there.
00:16:42 But yeah, we can basically read through the preview later on.
00:16:45 For now, let's actually code it out together.
00:16:47 You understand the idea.
00:16:48 There's going to be a lot of these coding patterns we'll go through.
00:16:51 We'll learn how to write each one.
00:16:53 So let's get started with the factory pattern.
00:17:00 Here?
00:17:01 We have a problem and the problem is that we want to basically process some payments.
00:17:06 This is a very real example, right?
00:17:09 We have a function that processes the order and you process it through PayPal, through Stripe, or maybe give an error if it's not right.
00:17:18 Now keep in mind, to really process a payment, it typically takes for smaller applications like from 20 to even 50 or a few hundred lines,
00:17:28 right?
00:17:29 So this is right here a huge code base.
00:17:31 You would have a lot of lines.
00:17:34 right here just to make that happen, right?
00:17:36 So that will look something like this.
00:17:38 You have a lot of PayPal specific code and then you also have a lot of Stripe specific code.
00:17:43 And you can easily get lost, like what is PayPal and what is Stripe code and then start messing with different functionalities,
00:17:49 right?
00:17:50 So, and then of course you can call each specific one and they should work.
00:17:54 But can we somehow split that logic up and make it a bit more debuggable and a bit more testable?
00:18:04 Let's try it out.
00:18:06 We'll use a class-based example for this one.
00:18:11 And I want to ask you, since most of you have said that you are JavaScript or TypeScript developers primarily, which programming paradigm do you use more often?
00:18:23 Object programming or class-based programming or functional programming in JavaScript?
00:18:30 Since JavaScript is capable of doing both, Great.
00:18:34 That's what I like to see.
00:18:35 I see that 100% of you uses functional programming more.
00:18:39 And that's great.
00:18:41 I wanted, I really wanted to create this entire workshop and to show all of these patterns using a functional approach.
00:18:48 Like I am, I do a lot of React, a lot of Next.js.
00:18:52 It's solely functional right now.
00:18:55 But we have to keep in mind that JavaScript allows you to do class-based programming as well.
00:19:01 And since many of these paradigms come from different programming languages, many of them include classes.
00:19:10 So alongside, I got you to do something here that you might not have expected.
00:19:14 I will give you a refresher on class-based JavaScript coding because to really show the full power of some of these patterns,
00:19:24 it might be necessary to write it as a class-based component or just as a class.
00:19:31 All of you on board with that?
00:19:33 And why does that not technically matter?
00:19:37 It's because as I told you, all of these are just ideas.
00:19:41 They're abstractions and how we put them to code doesn't matter too much.
00:19:46 It's just that it's an idea that we have to somehow put to work.
00:19:50 So let's explore it.
00:19:51 And for some, you'll see that I provided two solutions, both functional and class-based.
00:19:55 I try to do that as much as possible so we can kind of mix and match.
00:19:58 Okay.
00:20:00 Let's make it happen.
00:20:02 So first, I will create a class called PayPal Gateway.
00:20:09 And this will be a class that will only work for that specific gateway.
00:20:13 So only work for PayPal.
00:20:17 Um, classes allow us to create a constructor and a constructor is a special method that is called when a new instance of that class is created.
00:20:29 So as soon as we create this class, PayPal gateway, it will give it a name of PayPal.
00:20:36 And we can also give it a method called process payment that accepts an amount And we can simply return a console log saying processing amount through PayPal.
00:20:53 Or we can even do something like this.name.
00:20:57 I haven't wrote this, that, something in like five years since React transitioned over to functional coding.
00:21:03 So this is a big reminder for me here.
00:21:07 But yeah, it just shows you how some of these patterns will have to be coded in a class-based style.
00:21:15 Okay, great.
00:21:16 So this is now just a PayPal gateway.
00:21:18 And I will duplicate that one right here, and also create one for Stripe.
00:21:24 Okay, so same thing.
00:21:26 We just have to change the name right here.
00:21:29 Now even though they're the same right now, besides the name, I think you can get the idea.
00:21:34 The actual function to process the payment will be different for each one of these.
00:21:39 And now each one of these codes is completely separated within their own class.
00:21:47 And now we will actually create our function called or not a function but a pattern, old class, paymentFactory.
00:21:59 And this paymentFactory can have specific methods.
00:22:04 And we can give it a first method, which will be of a static type.
00:22:10 And when a method on a class is static, that means that it is defined as a static one and it belongs to the class itself rather than a specific instance
00:22:21 of that class.
00:22:25 This is the refresher on the class-based coding, if you don't mind.
00:22:29 So if it's static, you cannot call it on an instance of a factory that you create.
00:22:35 You can only call it on the class itself.
00:22:38 Good.
00:22:39 So let's call it create payment gateway and pass the type of the gateway which we want to create.
00:22:48 Next, we can have a switch statement as smartly suggested right here by my copilot.
00:22:54 And based on the type that we pass into the payment factory, we will create either a PayPal gateway or a Stripe gateway or return an error.
00:23:08 And now we want to process the payments.
00:23:11 So right here, we can say process order, and we can call PayPal to process maybe a hundred bucks.
00:23:18 We can do Stripe 200 bucks, and we can also try to process an unsupported payment gateway.
00:23:26 So now if I go right here and I run node solution.js.
00:23:33 Oh, let's see what happens here.
00:23:35 Process order is not defined.
00:23:37 Oh yeah, I completely missed the actual function on the class to process the order.
00:23:44 So let me do that right away.
00:23:48 If I go here, we can define the actual function to process the order, which takes in the payment type and the amount.
00:23:57 And here we can open up a try and catch block just to ensure that our error handling is done well.
00:24:05 And we can console that error.
00:24:08 the actual error message if there is one, but if there is no error, since we have created this create payment gateway type as a static method,
00:24:19 we can call it directly on the payment factory class.
00:24:23 And that would look something like this.
00:24:25 Cost payment gateway is equal to payment factory dot create payment gateway to which we pass the payment type.
00:24:40 And here, remember that first image that I've showed you when it came to the factory pattern?
00:24:48 You saw that we had one factory in the middle, which is a big car factory, and then many differently colored cars around it.
00:24:55 This right here is the factory, okay?
00:24:58 And then the payment type will be the actual type or the color of the car, which we want to create out of the factory.
00:25:05 And then we can call the process payment of the gateway with the given amount.
00:25:11 So that's going to be payment gateway dot process payment.
00:25:15 And then we will give it the actual amount.
00:25:20 Now we can actually test it out.
00:25:22 I do believe that we're good.
00:25:25 Again, just to remind you, the process payment functionality is hidden, not hidden, I just collapsed it, but is contained within a specific gateway we're
00:25:36 trying to process the money with.
00:25:38 The factory returns the right gateway, and then now we have a process order function which creates a specific gateway depending on what and when we want
00:25:49 to process the money with.
00:25:51 And then we can call it.
00:25:52 So I believe now with this, we should be able to clear it and run node solution.js.
00:25:59 And I just realized that some of you might be typing the code with me, so collapsing these things might not be a good idea.
00:26:06 But as you can see, we got the same response that we have gotten before.
00:26:14 Now, what are some of the concerns with this approach right here?
00:26:19 My first concern is that it is significantly more code than before, and I totally get that, right?
00:26:26 But you have to think about the fact that this was just a demo example and that by itself, this would already be a few hundred lines if you want to have
00:26:34 proper error handling on processing each one of these payments.
00:26:38 So it's not that much more, but it gives you the scalability and code reusability to simply add additional gateways later on if you want to.
00:26:50 So if you want to process payments through something else, you just add it here, you add it here as well, and you have completely separated pieces of code
00:27:00 logic that are completely individualized and only depend on the parameter that you pass right here.
00:27:10 Now, let's see.
00:27:12 Let's also go over some of the pros and cons of this.
00:27:17 Approach using normal functions is simple and direct.
00:27:22 There's no overhead and you just immediately call it and process it.
00:27:27 But it has limited scalability.
00:27:30 Adding new payment gateways would require modifying the process order function potentially leading to code duplication or just increased complexity.
00:27:41 Also, some of the logic will be hard-coded as the logic is coupled with process order, making it less flexible for the future.
00:27:52 It's also less reusable, as we discussed.
00:27:55 With the factory, the cons are already discussed, like increased complexity, potential overhead of initializing those classes,
00:28:03 and a learning curve.
00:28:05 You cannot give this code to junior developers because they're going to have a tough time figuring out their way around it.
00:28:14 I think that the pros outweigh the cons.
00:28:18 Your code will become more modular and extensible, and also it allows for encapsulation, right?
00:28:26 So now all of your specific payment gateway implementations are encapsulated within their respective classes, which results in cleaner code and separations
00:28:38 of concerns, okay?
00:28:41 Also, much better error handling because you can define specific error handling for each one of these functions without losing yourself around the code.
00:28:55 I see a note by Andrea asking if we can decrease the font size.
00:29:02 And I accidentally closed my entire Visual Studio code.
00:29:04 So let's open it up and let me increase the font size.
00:29:08 Of course, only if all of you say that you can clearly see what's happening right here.
00:29:14 I'm going to open up this one right here.
00:29:18 And I think we should be good.
00:29:20 So if I decrease it by one step, is this looking a bit better?
00:29:25 More code fits?
00:29:28 Okay, that's great.
00:29:29 I typically like to have it very zoomed in just so you can see it, but I do understand how if you're following along, it might be a bit easier to do it
00:29:36 like this.
00:29:38 Perfect.
00:29:39 Great.
00:29:40 So I hope this gave you a bit of a better understanding of how a factory pattern looks like.
00:29:49 But I just want to go back to this picture a bit.
00:29:52 And I hope that now you can tie this illustration with the code implementation as well.
00:29:59 So let's continue with the next pattern right here.
00:30:02 And that is a singleton pattern.
00:30:06 This singleton pattern is a bit different from a factory pattern, as you can see it here, in a way that you have a single singleton and then you create
00:30:16 multiple instances that belong to that singleton.
00:30:20 Let's explain it a bit further.
00:30:23 Having a single singleton is like living in a house with only one key.
00:30:31 No matter how many people live in the house, they all share the same key to enter.
00:30:39 This pattern is commonly used in scenarios requiring a single shared resource or centralized management, such as logging utilities or database connection pools,
00:30:54 configuration management, caching mechanisms, thread pooling, user sessions, and many other.
00:31:02 And there are a couple of reasons why we use it.
00:31:04 We use it to maintain consistency, manage resources efficiently, and also initialize things in a lazy way and enhance performance by avoiding the overhead
00:31:19 associated with creating and destroying multiple instances.
00:31:23 So we all access it through a single shared resource.
00:31:28 In this case, a singleton.
00:31:31 So, let's check out the singleton out in code.
00:31:36 I'm going to go back right here and open up our second example called singleton.
00:31:43 You'll see that for this one specifically, each problem and solution also has a folder within it.
00:31:49 That's because I wanted to make this code a bit more extensible.
00:31:53 In this case, these are actual express servers that we can run and that simulate the saving process of saving a login session.
00:32:06 But in this case, we'll just go over it in code.
00:32:08 If you want to run it, feel free to do so.
00:32:11 I can explain how it all works.
00:32:16 We have, let's analyze the code right here.
00:32:19 We have an express server with an express session, and we have two different users with different passwords.
00:32:26 We use the simplest way to store our session by using a memory store, which stores the session data in memory.
00:32:33 And we create a session middleware by creating a new session with some secrets and typical stuff that sessions contain, like cookies with a max age and
00:32:43 so on.
00:32:45 Next, we have the isAuthenticated middleware.
00:32:49 What is a middleware?
00:32:50 Well, middleware typically happens in between our different holes of our API.
00:32:56 So if you try to call the app.getDashboard, we first want to check whether we are authenticated.
00:33:02 So we check this isAuthenticatedMiddleware.
00:33:07 If a session contains a user ID, we're good.
00:33:10 Let's move forward.
00:33:12 Else, we'll just say unauthorized 401, right?
00:33:18 Cool.
00:33:18 That makes complete sense.
00:33:20 Now, if we try to log in, we simply check for the username and the password.
00:33:25 All of this is just dummy data for now.
00:33:28 And we add the user ID equal to the username to the session, essentially mocking the successful authentication.
00:33:36 Also, on logout, we destroy it.
00:33:39 So this is how you would typically do it without using a singleton pattern.
00:33:44 But a problem here is that whenever this code run, JavaScript run linearly, right?
00:33:50 Line by line.
00:33:51 So whenever you run this code, a new session will be created right here.
00:33:56 That will then be passed into the middleware.
00:33:59 And that's not good.
00:34:00 When you create one instance of a session, you want to keep it active until it dies, basically.
00:34:08 So singletop pattern will help us have a single source of truth right here.
00:34:13 So let's move over to the solution.
00:34:16 As I said, a lot of this code is very similar to the problem.
00:34:20 Just a couple of things will change.
00:34:21 So I kept the solution here for you so we don't have to type it out together live.
00:34:27 In this case, we again create a class called a SessionManager.
00:34:34 We create a new store in this SessionManager by creating a new session.
00:34:40 Next, we create a static class or static method, getInstance, where we try to get an instance of a SessionManager.
00:34:49 And if it doesn't exist, we just for the first time create a new instance.
00:34:54 And we also have a method called getSessionMiddleware where we finally return that session.
00:35:03 Now in this case, we create a new instance of a session manager like this, and then we get a new session from that middleware by saying sessionManager.getSessionMiddleware.
00:35:17 And we simply call it here.
00:35:20 Now, everything else should be very similar.
00:35:24 If we go, we have a function isAuthenticated, which again checks the session, and also here in the login, we also add it to the session.
00:35:34 We call the middleware function, we log out and remove it.
00:35:37 Everything is the same, but the fact that we are now only one checking for the instance of the session manager, and only if it doesn't yet exist,
00:35:47 we return a new one or we create one, and then we get a session.
00:35:52 This way, we can only get one single session, one single source of truth.
00:35:58 And let me even explain that even further by going to our README right here and expanding it.
00:36:05 As you can see, with the first approach of using functions and global variables, the implementation is once again, simple,
00:36:13 easy to understand.
00:36:15 And yeah, just immediately write it and use it.
00:36:18 No additional setup.
00:36:21 Scalability is a problem right here because if you want to add additional functionality such as a custom session management,
00:36:28 you might need to require modification and that will increase complexity and also remove scalability.
00:36:37 Also, you might have a lot of code duplication.
00:36:42 It's just going to happen.
00:36:44 Also, your specific functions and logic of session management and authentication logic is not encapsulated, meaning it is just in one place,
00:36:54 leading to less maintainable code.
00:36:57 Also, eager initialization.
00:36:59 You'll see right here that the store will be initialized as soon as this module is loaded It won't try to get the previous session and only if it doesn't exist,
00:37:09 create a new one.
00:37:11 And also, you are going to suffer from multiple instances of the session because in large applications, different modules might,
00:37:21 just not on purpose, create multiple instances if they don't share the same module correctly.
00:37:27 With a singleton pattern, we already discussed the cons, I think we can skip it every single time for now, because it's always almost the same.
00:37:35 Increased complexity, learning curve, and overhead, but the benefits of every single different code pattern are Well, in a way,
00:37:46 we could skip those as well, but let me explain them.
00:37:49 They're always the same.
00:37:50 The code will be better encapsulated, meaning that specific code segments will be within their own space, which will lead to a cleaner code and a separation
00:38:02 of concerns.
00:38:03 It's much more scalable by simply modifying the SessionManager class methods.
00:38:08 It's more modular and it will not try to immediately create a new session.
00:38:16 It will only create a session store when needed, which ensures that the instance of the session manager is created only when it's first accessed.
00:38:28 This is different from directly exporting an object, which creates the object as soon as the module is loaded.
00:38:38 So going back to the illustration right here, I'm going to move a few slides back to show this one to you.
00:38:49 Every single instance will try to access the session from the same singleton without trying to eagerly generate a new session as soon as possible,
00:39:01 which can lead to having multiple sessions which weren't supposed to be there.
00:39:06 This way, we have a single source of truth and it just works.
00:39:12 It's great in these cases where there's multiple people or multiple users trying to access a specific thing.
00:39:20 They all have the same key, but they can all access it.
00:39:25 Moving forward, let's go over right here, and we have something like this.
00:39:35 No, I think we have a few more slides just to explain this further.
00:39:39 You can see that this offers significant resource sharing.
00:39:43 You can see that we have different classes on the main class, which is a singleton.
00:39:49 Like here, we have an even simpler example than authentication.
00:39:54 Maybe we should have led with this one.
00:39:56 But you can see that we have a counter example, where you have a couple of methods like getInstance, getCount, increment,
00:40:04 or decrement.
00:40:05 And you can create a counter one and counter two, which are two different instances of that counter.
00:40:12 And this has multiple advantages.
00:40:15 It looks something like this.
00:40:17 Let me see.
00:40:18 So you create a new counter instance, and then you have a red button and a blue button.js, I think it says right here.
00:40:26 And all of this leads to the improved management of the global state.
00:40:32 And if you do it like this, Then you have two different instances that don't rely on the same thing, which leads to a couple of things like the global state.
00:40:49 making debugging harder, and also it can be challenging to test the subclasses, and it violates the single responsibility principle,
00:40:59 which means that a class should only have one reason to change, which potentially leads to concurrency issues in the example we have explored with the
00:41:10 session storage.
00:41:11 And like any other pattern, this pattern also has some trade-offs.
00:41:18 So it's very important to consider what those trade-offs are.
00:41:24 In most cases, it will be just increased overhead and complexity of the code.
00:41:29 But in general, when you have a singleton, it's quite simple to manage the state in a global way and it will all be concurrent.
00:41:39 That's the idea of a singleton.
00:41:43 Next, we have a prototype pattern.
00:41:48 A prototype pattern, simply put, instead of creating new objects from scratch, clones an existing object or a prototype and it modifies it as needed.
00:42:01 So let's say you're working on a document editing application.
00:42:06 Let's say you're editing any kind of document like Microsoft Word or even Google Docs.
00:42:13 Users here frequently change or create new documents based on specific templates, such as a business report or a meeting agenda or even a project proposal.
00:42:27 And instead of creating each new document from scratch, you can use the prototype pattern to clone these templates, which saves time and ensures consistency.
00:42:39 An example of this in code would look something like this.
00:42:43 You have a document template prototype, which clones a specific document and returns it.
00:42:51 And you can create a function to create a new document template.
00:42:57 So you define the template and create it.
00:42:59 And then you define some documents templates using the factory function.
00:43:04 Oh, again, we have some small hints of a factory pattern here as well where we create it.
00:43:12 And now we have a report template and an agenda template.
00:43:16 And now we can create new documents simply using those templates by cloning them.
00:43:21 And if you want to, we can, of course, modify each one of these templates as well, which is what we typically do.
00:43:29 This is an example of a functional programming, but you can do a similar thing in a class-based approach.
00:43:36 Let's see if we have an example for this as well.
00:43:38 If I go back.
00:43:41 I think for this, we don't basically need an additional example, as you can see how this would look like.
00:43:49 It combines some examples of a factory pattern, and basically a prototype allows you to extend specific pieces, not only based on the main class,
00:44:00 but also based on some other prototypes that have been created after it.
00:44:05 That's the idea.
00:44:07 Much simpler than the last few.
00:44:10 But now we can move on to a completely different set of category patterns, which are called structural patterns.
00:44:17 So before we had object creation patterns, and now we have structural patterns.
00:44:23 These are all about organizing and composing objects to form larger structures.
00:44:31 And examples here include a decorator pattern.
00:44:36 So that's the first of the structural patterns that we will explore.
00:44:41 Imagine you have a basic object or a function and you want to add some additional features to it without modifying its primary structure.
00:44:51 That's where decorator pattern comes in handy.
00:44:55 So in simple terms, it is like adding a layer of wrapping paper around the gift.
00:45:02 Each layer adds something new and makes the gift look better or more personalized.
00:45:10 But the core of the gift remains the same.
00:45:15 It allows you to wrap an existing object or function with new behavior without altering its structure, and you use it to dynamically modify different things,
00:45:27 like toppings on a pizza, let's say.
00:45:31 So the main part is the same, and then you use a decorator pattern to decorate that initial object.
00:45:40 So let's move over to the code and let's explore it in action.
00:45:45 0-3 decorator pattern.
00:45:48 We can see a base example right here where we have different constants for pizza toppings and pizza types, like the basic pizza price,
00:45:58 the cheese topping price, and the pepperoni topping price as well.
00:46:02 We define a function to create these pizza with toppings.
00:46:08 So basically, that would look something like this.
00:46:11 Define the price of a basic pizza.
00:46:15 And then based on the topics we pass, we're going to increase the price.
00:46:20 Only cheese, only pepperoni, or cheese and pepperoni.
00:46:26 This should actually be a bit cheaper.
00:46:28 Let's do it like this since we're taking both.
00:46:29 So something like two, three, four.
00:46:31 Okay.
00:46:32 And the basic is default pizza with no toppings.
00:46:35 Now, here are a couple of examples.
00:46:39 If you want to create a basic pizza with no topics or no toppings, you don't like cheese, you don't like pepperoni, you don't like anything,
00:46:48 you're lactose intolerant, it's going to be a basic pizza for you.
00:46:52 Basically bread.
00:46:54 You're going to get it and you can then extract a price out of it and it's going to cost Next, if you want to get a pizza with cheese,
00:47:05 you call a function and you pass a specific parameter that you want on it.
00:47:11 Cheese increasing the price by two, resulting in a price of 12. You get the idea, right?
00:47:17 Let me show you how to do it with a decorator pattern.
00:47:22 So moving forward to solution, I'm actually going to choose a solution number two, which will be a function-based approach.
00:47:32 So now I don't want to do more classes.
00:47:34 If you want that, it's going to be in the final solution.
00:47:38 I can just go ahead and paste it here as well.
00:47:40 That's going to look something like this.
00:47:42 Again, a lot of classes and decorators and stuff like that.
00:47:47 But in this one, I really like the solution with a functional approach.
00:47:52 So let's code it together.
00:47:55 First, we're going to create a function called createPizza, which will simply return an object with a description.
00:48:08 of plain pizza, and it will also include a rice equal to 10. So this is a function that creates a base pizza.
00:48:21 Next, we can create a higher order function.
00:48:25 This one is also higher order.
00:48:27 You'll see why that matters later on.
00:48:30 But a higher order function that adds topics.
00:48:33 So function, add topping, And we add the topping to it.
00:48:41 Why it will be a higher order function?
00:48:43 Because we can return another function as a result.
00:48:47 Okay.
00:48:48 So return function that accepts the existing pizza, whether that's a base pizza or it has all the other toppings on it.
00:48:58 We don't care, but it accepts a pizza and then we can choose what we want to do on that pizza.
00:49:04 So we're going to form a new description of that pizza.
00:49:07 So const new description.
00:49:10 is equal to, we can just use a regular JavaScript template strings and take the current pizza description, but append a topping to it.
00:49:23 That's going to look something like this.
00:49:26 I believe that a pizza has a description and also the topping will be an object, I think.
00:49:33 So we can say topping that name.
00:49:36 We can also declare a new price.
00:49:40 And the price will be pizza.price, the base pizza, plus the topping we just added.
00:49:46 And then what do we do?
00:49:48 Well, we return a description of new description and return a price equal to new price.
00:49:57 Make sense?
00:49:58 Well, not yet because we haven't yet called them.
00:50:00 But as soon as we call them, I'll show you how we can do all of these together.
00:50:05 Let's just first define the example of some toppings, like before.
00:50:11 So const cheese topping is equal to an object with a name and a price of that topping, like cheese2 and pepperoni is 3. And now we can create our custom pizza.
00:50:28 Let's see.
00:50:29 I'm going to go right here into the chat and let's see some people that have sent some messages.
00:50:36 Oh, I can see Mattia right here.
00:50:38 What I'm going to do is going to say const Mattia's pizza.
00:50:43 And we want to create a personalized pizza for Mattia.
00:50:48 Let's say that Mattia wants to get a pepperoni and cheese as well.
00:50:54 We would create it like this, add topping, and to it, we can pass the pepperoni topping.
00:51:05 That would look something like this.
00:51:07 Next, we know that the function add topping returns another function as a result.
00:51:16 This is a bit harder to wrap your head around, but how many of you are aware of higher order functions right here, or at least of a concept?
00:51:28 You can say yes in the chat if you know how higher order functions work or no if you haven't used it before.
00:51:35 Yeah, I can see a lot of yeses, maps, filters.
00:51:38 Yeah, those are great examples.
00:51:39 I can also see some nos.
00:51:40 That's totally okay.
00:51:42 So a higher order function is a function that returns another function as a result.
00:51:48 And when you first see the syntax of how it's being used or called, it looks a bit weird.
00:51:54 It looks like this.
00:51:57 So you call a function and then you open up a new poll as a result of that function.
00:52:04 And keep in mind, what is the parameter that you pass or an argument that you pass into the other function?
00:52:11 It's also a pizza.
00:52:14 So what we can do is here, if you want to add more toppings, we can call the add topping one more time and we can also add the cheese topping,
00:52:25 and the situation is the same.
00:52:27 We can call this function one more time right here, and then we can just pass the create pizza, which will create the base layer.
00:52:39 Even though it seems like it's the other way around, once the functions get called, that will actually work.
00:52:44 So we first create a pizza, then we add a topping, and then we add another topping.
00:52:50 And this will result in the pizza that Mattia requested.
00:52:55 I can see that some people also requested pineapple.
00:52:58 If you want to, we can add that.
00:53:00 But for now, I don't want to scare Italians if there are any here.
00:53:04 So we will be keeping the pineapple off the pizza for now.
00:53:08 Um, and now if you want to get the actual custom pizza, we can console log it by just doing something like this.
00:53:17 Custom pizza, or we can say Mattias pizza will include that pizza dot description.
00:53:23 So let's do custom pizza dot description.
00:53:29 And we can also add a new line and then do something like the price to see the actual price.
00:53:34 That'll be equal to pizza dot price.
00:53:38 Let's go ahead and run it by doing a CD into 03 decorator.
00:53:45 And then we can run node solution to that JS.
00:53:50 And we are left with a plain pizza with cheese with pepperoni.
00:53:56 I think we could have called this just pizza.
00:53:58 That would make a bit more sense.
00:54:00 So now we get a pizza with cheese and pepperoni with a price of 15. Let's check if that works.
00:54:08 Yep.
00:54:08 10 plus 2 plus 3 is 15. And we can also see name, cheese and pepperoni were added to the description.
00:54:16 We basically constructed it right here.
00:54:20 And yeah, I can see a question by Kerman.
00:54:24 The solution one with a class-based approach was indeed left empty.
00:54:31 I will provide a complete code base with all the solutions or the pre-done in case you weren't following along.
00:54:40 later on after this course, or I think maybe Sujata can even drop it in there as well right now, but maybe better not.
00:54:47 Let's just keep it like this.
00:54:48 You'll be able to check out the solutions at the end of this workshop, so you don't have to worry about that at all.
00:54:54 But just to quickly go over the class-based solution, you create a constructor for the pizza, you create a topping decorator,
00:55:03 which gets the price and the description.
00:55:06 And then you create a new class that extends Decorator that then adds to the price and adds to the description, basically modifying the initial object.
00:55:19 This is also quite lengthy.
00:55:23 And it's not very...
00:55:27 Let me put it this way.
00:55:29 It's very unforgiving to hold a workshop on code patterns because I only have the time to show them to you in a very simplified form.
00:55:43 An example of adding pizza toppings.
00:55:47 When you put it like this, it seems like there's so much more code and complexity added basically for nothing, for no reason,
00:55:57 right?
00:55:58 We're just figuring things out as we go.
00:56:01 But on larger example and larger code bases, it actually makes so much more sense to add the decorators and different factories and patterns as they make
00:56:13 your code more robust, more scalable, and it doesn't significantly add the overhead of the solution that we provide.
00:56:22 Rather, it just adds to the scalability.
00:56:26 Looking at the readme right here, we can see again the differences of the approach without it.
00:56:31 Again, simple, easy to understand, straightforward, but limited flexibility and extensibility.
00:56:39 Rather with using classes and decorators, it's object oriented.
00:56:43 If you like it, you can go for it.
00:56:45 And with functional, again, we have that compressibility.
00:56:48 We have separation of concerns.
00:56:51 We have immutability, which are all concepts we should strive to achieve.
00:56:59 Which is better?
00:57:00 Well, the approach using higher order functions is better overall, at least in my opinion, as JavaScript is shifting more towards functional approaches.
00:57:07 And I think it has good readability, flexibility, and extensibility.
00:57:15 Also, Muhammad had a great question about higher order function execution.
00:57:23 That is, how is this pizza getting executed?
00:57:28 From left to right or from right to left?
00:57:31 And yeah, I think Jakub answered that question great.
00:57:35 The answer is right to left.
00:57:36 As you can see, we're first creating this pizza and then passing that pizza as an argument to another function at topping.
00:57:45 We add the cheese topping, we add the pepperoni.
00:57:48 So it gets executed from right to left, which is a bit unintuitive, for sure.
00:57:55 But that's how it works with this functional approach.
00:58:00 Okay, great.
00:58:01 I hope this makes sense.
00:58:04 And as I said, I'll provide the solution for the class-based approach later on.
00:58:08 Hopefully now this illustration makes just a bit more sense.
00:58:15 Now, after the decorator pattern, we can see that you don't necessarily have to follow object-based object programming for every single one.
00:58:27 It's going to work for functional programming as well.
00:58:30 We can make it work, right?
00:58:32 Sometimes.
00:58:32 Maybe it's even better than object-oriented programming for JavaScript.
00:58:37 I still believe that.
00:58:38 But again, just to showcase how some of these patterns work, I wanted to use some classes as well.
00:58:43 Next, we have an adapter pattern.
00:58:47 And here you can see a nice illustration as well where we have a power plug, which is the original form of request, which we want to plug into an outlet,
00:58:57 which is an external and it can be incompatible.
00:59:01 So we need an adapter which converts the requests to be compatible.
00:59:06 Okay?
00:59:08 That's where the adapter pattern comes in.
00:59:11 Can anybody think of maybe an example even before of how an adapter might be used?
00:59:19 Like in code, what would an adapter be?
00:59:25 Version compatibility.
00:59:26 Okay.
00:59:27 Yeah.
00:59:28 GitHub OAuth adapter.
00:59:30 Okay.
00:59:30 So different auth adapters for logging users in.
00:59:33 Okay.
00:59:34 Boulder.
00:59:35 Great.
00:59:35 Back in the response to view model.
00:59:37 Yeah, that works as well.
00:59:40 Yeah.
00:59:40 These are great.
00:59:42 You guys should be here giving this workshop.
00:59:45 That's great.
00:59:47 You're entirely right.
00:59:49 It's like having a middleman that helps with two different things to talk to each other like this, even if they're not originally designed to.
01:00:01 Imagine you have two friends who speak different languages and want to communicate.
01:00:06 You act as an adapter, translating between them so they can understand each other.
01:00:11 In a programming example, let's say you want to send notifications through different platforms like email, Slack, SMS.
01:00:20 And here it acts as a common interface for sending those notifications.
01:00:27 So let's explore it in code.
01:00:32 Moving forward to 04 adapter pattern.
01:00:39 Let's start with the problem.
01:00:42 This is an actual example here where we can use Twilio to process SMS and other things, and also NodeMailer for email.
01:00:52 And then we also have a Slack web API.
01:00:56 We have a function to send notifications via email, which uses the node mailer.
01:01:02 We have a function to send notifications via SMS using Twilio.
01:01:06 And then we have a function to send notifications via Slack.
01:01:10 And again, these are very minimal examples, but I wanted to provide you with as real examples as possible.
01:01:20 So now we have these three different functions.
01:01:22 And then we have a main function that sends out these notifications.
01:01:29 As you can see, we have a if-else statement or a switch statement.
01:01:34 And depending on the service, we can then send either an email, an SMS, or a Slack message.
01:01:42 And that would look something like this.
01:01:44 A function accepts a service, a message, and a recipient.
01:01:48 And then based on that, we can send a notification.
01:01:51 Makes sense, right?
01:01:54 So how would this look like using the adapter pattern?
01:01:59 Let's check it out in action together.
01:02:02 We can move over to solution two.
01:02:04 I would, I think, prefer a functional approach for this as well.
01:02:09 And yeah, there's going to be a lot of code for this one.
01:02:13 So I don't necessarily feel like typing all of it out together.
01:02:17 It might take us just like half an hour to get this done.
01:02:20 I think we could just copy the problem and then paste it over to the solution.
01:02:26 Let's try it that way.
01:02:28 So basically we'll create different functions for each one of these, like send email, send SMS, and send Slack, but we will turn them into adapters.
01:02:42 So what we have here, create email adapter, we'll just rename them.
01:02:47 Send email will be renamed to create email adapter.
01:02:53 And let's properly spell that out.
01:02:57 I'm going to collapse it.
01:02:59 We'll then rename this to create SMS adapter.
01:03:05 And we can rename the last one to create Slack adapter.
01:03:10 Okay, just like that.
01:03:15 But now it will be a bit different.
01:03:19 We're going to use another function right here.
01:03:23 You can think of it almost as middleware in our previous example.
01:03:28 I will also convert these functions into ES6 plus arrow functions.
01:03:33 It might be a bit easier to check it out that way.
01:03:36 So instead of just async function regular, I will say const and then declare it like this, and then add an arrow at the end right here.
01:03:45 I think it might be just a bit easier to see what we will do with this approach.
01:03:51 Or it will be a bit less verbose because arrow functions allow us to do something cool here.
01:03:58 And that something is to call a function beforehand.
01:04:03 That's going to look something like this.
01:04:06 We can call an additional function to which we'll pass a config, almost like we are doing a middleware, adding a middleware thing right there.
01:04:15 Okay.
01:04:16 And that config will contain the config that we need to pass right here for the node mailer transport in this case.
01:04:26 So node mailer.createTransport, we can pass this config right here.
01:04:31 That will act as the adapter.
01:04:34 And then we send the email.
01:04:36 Next, let's move forward.
01:04:38 We're gonna do a similar thing for the create SMS.
01:04:41 And sorry, I'm not gonna collapse it just so you can see it as well.
01:04:45 For the SMS, we also need to know some information like the Twilio account SID and Twilio auth token as well.
01:04:53 So we wanna pass them into this function.
01:04:56 before we call it, and that will be account SID and auth token.
01:05:07 And now here we can pass those two as they're real variables, account SID and auth token, and then we can send out the SMS.
01:05:17 And we want to do a similar thing for Slack.
01:05:22 where we're going to pass in a token and a channel.
01:05:26 These are the two things that we need before we call this second async function.
01:05:32 We can simply pass the token right here and we can pass the channel right here.
01:05:40 Great.
01:05:41 So now we have these three adapters.
01:05:44 And finally, we have the general send notification function right here.
01:05:52 But this will look quite different now.
01:05:56 Instead of having a service right here and then having to call these different things, instead of a service, I will accept an argument or parameter known
01:06:10 as an adapter, and I will remove everything from the try and catch, at least this entire if and else statement.
01:06:18 The only thing we want to do is say await and we want to call the adapter, like a function, and to it, we want to pass the message and the recipient.
01:06:32 Do you get the difference?
01:06:33 Instead of doing this and checking for the service and then based on that calling different functions, now we only call one function called an adapter
01:06:47 to which we pass the message and the recipient.
01:06:52 And then we already have all the information that we need to send over to these different functions.
01:06:59 Like for Slack, it's the token and the channel.
01:07:03 For SMS, it's the account SAD and the odd token.
01:07:08 And for email, it's the entire email config.
01:07:12 So how will this work?
01:07:13 How can we actually know what we have to pass?
01:07:16 Well, let me show you.
01:07:18 We need to configure the adapters and we can configure them like this.
01:07:23 By saying const email adapter is equal to create email adapter, and to it, we can provide the service equal to Gmail, and we can also provide an auth equal
01:07:43 to whatever we need to define here, right?
01:07:46 This is the email adapter coming from a function called create email adapter.
01:07:52 We can also duplicate this right here and create adapters for different kinds of things.
01:07:58 Like in this case, we can create an adapter for SMS.
01:08:02 So that's going to be an SMS adapter by calling a create SMS adapter.
01:08:10 And to it, of course, we will pass some different configuration options, such as the Twilio account SID.
01:08:19 And also, no, this won't be an object.
01:08:21 This right here can just be two different sets of strings.
01:08:25 I think that's how we have declared it.
01:08:27 So we can just pass a account SID as well as the auth token.
01:08:33 That's how it will look like.
01:08:35 And finally, we have the Slack adapter.
01:08:38 which is similar to the SMS adapter, which we can call create.
01:08:45 Slack adapter.
01:08:47 And to it, we can pass the Slack token, I believe.
01:08:50 Let's see it.
01:08:51 Yeah.
01:08:51 Token and the channel.
01:08:52 Okay.
01:08:54 So let's do it like that and pass the Slack token and the Slack channel.
01:09:00 I can also just put this in one line so we can see it more easily.
01:09:04 And this config could have been also two different things.
01:09:08 Like just to keep it consistent, I will put it as service and auth, right?
01:09:12 So now we can pass two different strings.
01:09:15 like service and different options as well, if you want to.
01:09:22 So hopefully now this makes a bit more sense.
01:09:25 Like let's start with Slack so we can both see these things.
01:09:28 We have create Slack adapter, which accepts a first function once we call it, which calls a token and a channel, which are the adapter,
01:09:37 which is the adapter information needed to be able to process the next thing, which is shared among all the other things,
01:09:45 which is a message, right?
01:09:48 You can think of this part as a translator in the conversation, which needs different languages to be able to convey information.
01:09:58 In these different languages is the specific setup needed for each one of these accounts.
01:10:04 We have the email adapter with a service in Auth, we have an SMS with Twilio, and we have Slack with tokens.
01:10:11 We pass it, and then only then, if we have that individual information, we can pass the message or the recipient whatever we choose.
01:10:20 Great.
01:10:21 So now that we have those adapters, what can we do?
01:10:25 Well, we can simply send notifications, but this time it's not going to be like this, email, SMS, and Slack.
01:10:32 Rather, we'll feed it an actual adapter.
01:10:35 So email adapter, SMS adapter, and also a Slack adapter.
01:10:46 Let's make sure I spelled it properly.
01:10:48 Oh yeah, adapter.
01:10:50 There we go.
01:10:54 Perfect.
01:10:55 So you get the difference, right?
01:10:57 Again, it's not a big change from the original solution, but you can see how each one of these, we are keeping all the tokens right here within that specific one,
01:11:07 which already, again, is not that bad because each one of these is encapsulated within its own function.
01:11:16 But here we just go a step further.
01:11:19 and completely create one function that sends the notification but has a function to which we pass additional configuration for each one of these services.
01:11:32 I hope this makes sense.
01:11:35 Great.
01:11:36 So we can also quickly check out the solution for class-based thing.
01:11:41 Again, I will paste it for now and it will be shared with all of you later on.
01:11:46 It looks something like this.
01:11:48 It's a bit longer.
01:11:49 As you can see, we have a notification service with a method that sends the notification.
01:11:56 and then we have different adapters.
01:11:59 In this case, you automatically define the config.
01:12:04 You allow yourself to pass the config into the constructor of the adapter.
01:12:10 So when you create an instance of an email adapter, you can pass that config right in, and the situation is repeated for all of these different classes.
01:12:20 So we're waiting to create an instance of that class.
01:12:23 And when we create it, we then pass that information to it.
01:12:28 And then once we have all of those adapters, what do we do?
01:12:31 Well, we simply call a function adapter that's a notification, and we do the same thing as before.
01:12:38 Once again, the underlying principles are more important than the code paradigm that we choose to use.
01:12:47 As before, without using any patterns, they're wrecked, straightforward, simple, no overhead.
01:12:52 But the SendNotification function is tightly coupled to specific notification services, making it harder to extend.
01:13:01 So if we go here and look at SendNotifications, you can see that it's harder to extend it right here.
01:13:06 You have a lot of repeated code.
01:13:09 And imagine even that all of your functions Even this, as I told you already, is okay, but imagine if you kept all of the code right here.
01:13:19 If you didn't have an additional function, whereas the SMS is here, the Slack is also within here, it's all within a same function,
01:13:29 and it would be just a mess to try to debug it and to figure out what is happening across different services.
01:13:37 It will be so tough to manage in this.
01:13:41 I can see a note right here, a comment from Christopher.
01:13:44 I like this functional adapter approach.
01:13:46 I do too.
01:13:47 It's quite useful.
01:13:49 And once again, I will keep repeating it most likely until the end because I love simple code.
01:13:57 I love it.
01:13:58 I love code that is self-explainable, that doesn't need a lot of comments, that is simple and not lengthy.
01:14:07 I love it.
01:14:10 As we dive into more complicated code bases, you have to account for scalability.
01:14:17 You have to account for security.
01:14:18 You have to account for encapsulation of different services.
01:14:24 And this is where the patterns do add additional overhead to your code, but make it that much more extendable and scalable.
01:14:35 So I'm glad that you like this approach, Christopher.
01:14:38 And if there's only one takeaway, only one pattern that's going to...
01:14:44 stay in your mind after this workshop, that's going to be good.
01:14:48 But even more so, if you're going to have a bit of a better general understanding of what patterns in general are, even if you don't end up using them,
01:14:57 or even if you end up just finding them online and then putting into your code to use, I'm totally okay with that as well.
01:15:05 I just want you to understand that these things exist, and that there are specific situations in the code when you might need those and when you might
01:15:16 not need them.
01:15:17 If you're developing a very simple project, you don't need complex patterns.
01:15:23 That was the adapter pattern.
01:15:26 We have a composite pattern and you can see a tree right here, right?
01:15:31 We'll explain this illustration later on, but it basically lets you treat individual objects and compositions of objects uniformly.
01:15:41 That needs some additional explanation.
01:15:42 So in simple terms, It lets you build a tree structure of objects where individual objects and groups of objects, called composite objects,
01:16:01 are treated the same way.
01:16:04 Let me repeat it.
01:16:07 It lets you build a tree structure of objects where individual objects and groups of objects called composite objects are treated the same way.
01:16:23 Let's dive into the code to explore it further.
01:16:27 That is 05 composite pattern.
01:16:30 I tried to find a good example that we can use to explain this, so I used an employee role system of permissions.
01:16:41 In this case, we have different employees with different permissions.
01:16:45 Employees can read documents, managers can approve documents.
01:16:50 I'm guessing they should be able to read them too.
01:16:52 So we can basically add this right here, read and approve documents.
01:16:57 And oh, no, I did it in the next thing right here where we inherit all of the other permissions from an employee as well.
01:17:08 Okay, so we're inheriting the property from an employee.
01:17:11 And in this case, administrator can also delete the documents and then inherit the properties of a manager, or sorry, yeah,
01:17:19 inherits the manager, which can approve the documents.
01:17:24 So we have a function that gets all the permissions of a specific role.
01:17:29 And they do that by collecting those permissions, by getting the role name.
01:17:34 And then we map over them and we add specific permissions.
01:17:38 and we check what does it inherit, and we also map through each inherited role, and then we, again, call this function.
01:17:46 This is essentially a function that, again, calls itself in a way.
01:17:55 You can see collect permissions here, collect permissions here.
01:17:58 It's basically recursion, right?
01:18:00 It keeps calling itself while we are going through the code, okay?
01:18:06 Then we can call it once for the first role right here, and it will keep calling itself to collect all the permissions needed based on all the different roles.
01:18:16 One more time, the administrator can delete document, but can also inherit the manager which can approve document, but also inherence the employee which
01:18:26 can read documents.
01:18:28 It is a recursive structure.
01:18:32 And then we can display a role hierarchy if we want to play with that a bit, where we can console lock the permissions of different roles and display the hierarchy.
01:18:40 So let's actually try to display a role hierarchy of an administrator.
01:18:46 I'm going to do clear CD, CD into zero five composite and run node problem.js.
01:18:57 And as you can see, here we have printed out the administrator role hierarchy, role administrator, permissions delete document,
01:19:04 and then it inherits the role manager who can approve and it inherits the employee who can read.
01:19:11 And then we have the final permissions right here to each one of these people or roles.
01:19:20 So now let's explore how we can use a composite pattern to improve this code base.
01:19:26 Want to do solution one or solution two?
01:19:28 Have you been doing a lot of functional?
01:19:30 Do you want to stick with functional or do you want to check out class-based?
01:19:37 I'll wait for a couple of ones.
01:19:38 Let's do one or two, depending on whether you want to do object for one or functional for two.
01:19:48 I do see some 1s, but I see many more 2s.
01:19:56 Although, yeah, more 1s are coming up.
01:20:00 Let's do 2, and then we can briefly go over the class-based approach as well.
01:20:06 Going over Composite Solution 2, which will be a functional approach, This code is not something I would like to type live as there is a lot of possibilities
01:20:21 to break it.
01:20:23 And I would not like you to type it live as well.
01:20:27 I will pass it into the chat right here so you can copy and paste it if you want to.
01:20:33 No, it doesn't allow me to paste it, but I can have Sujata paste the solution link right here just so we can go over it together.
01:20:41 You can see it's quite lengthy and also has a lot of possibilities of breaking it.
01:20:48 Let's go over it together.
01:20:50 You can also drop in the functional Sujata as well for this one for composites.
01:20:56 Then we should be able to all be on the same track.
01:21:02 Looking at this, we have a function that creates a role with a specific name.
01:21:10 So here we return a name and we set the permissions and the sub roles to be empty at the start.
01:21:17 Pretty straightforward, right?
01:21:19 Then we have a new function that adds a permission to a specific role.
01:21:24 The two parameters being a role and a permission to add.
01:21:29 Then we spread all the roles and then append an additional permissions, initial permission to the list, to existing list of permissions.
01:21:41 We can also add a specific role and if we add a role, we spread all the properties of that existing role and add the sub roles.
01:21:50 Again, spreading all the previous sub roles and then adding that new one, which we want to add.
01:21:58 To get the permissions, I think we can use the function that we have used before, like doing a recursive loop and then accessing each one,
01:22:08 and we can also use a reduce function.
01:22:11 So roll.subrolls.reduce, and here we get the specific account and a subroll, and we keep going over it to finally create a new set of role permissions.
01:22:26 Again, it can be a bit of a tougher piece of code to get at first, which is totally okay.
01:22:33 But it does a similar thing like what it did before.
01:22:37 It gets all the permissions based on all sub roles and roles.
01:22:42 And then it creates an array based off of all of those permissions.
01:22:48 We also have a function to display a role and now we can start defining them.
01:22:52 And you can see how the benefits of all of this once you see how we can start calling those functions and hopefully it will start making more sense.
01:23:00 So here we can define different roles by saying employee role is equal to add permission.
01:23:09 We create the role for the first time called employee and then they can read the documents.
01:23:17 Then we create a manager role where we can add role and we pass add permission, the same function we called above for creating a role of the employee,
01:23:29 but this time we pass the create role of manager and approve documents, but we add an employee role to it so that the manager has all permissions of an
01:23:42 employee as well.
01:23:44 Similarly, if we create an admin, we make it inherit the manager role, and then we add additional permissions by creating a role of administrator that
01:23:56 can delete documents.
01:23:58 And then same as before, we can display that specific role and show the hierarchy.
01:24:05 So now if I go here and I do node solution2.js, We can see that it looks something like this, same thing as before, but now we have used a composite pattern.
01:24:24 Let's go over the readme just a tiny bit to try to understand the pros and cons.
01:24:31 I'll completely skip this as, again, it's similar every time.
01:24:35 Simplicity, declarative thing, and yeah, I see in the chat, Pavel understands it.
01:24:44 Even though this looks tough right here, it's not as close to as being tough as how some additional permission hierarchy is a nightmare.
01:24:55 This is a way to mitigate that and to make it just a tiny bit easier to understand.
01:25:02 But yeah, looking at this, it's very limited with what we can do with the object literal right here.
01:25:09 It lacks additional separation of concerns because logic and data are mixed, making it harder to extend.
01:25:17 The role is also mutable, which might lead to unintended changes.
01:25:21 And we have recursive functions, potentially leading to stack overflows, error, and deeply nested role hierarchies, which are just a tough to manage.
01:25:30 Pavel knows what I'm talking about.
01:25:33 We have then different benefits of class-based or functional-based programming.
01:25:39 Let's look into functional-based in this case.
01:25:42 Immutability, this is huge.
01:25:44 All the functions we wrote are pure, avoiding mutation of state and reducing that unexpected behavior.
01:25:54 It's completely modular, we have separated all the concerns, and it is more readable because we have more smaller functions that are focused and concise.
01:26:04 Once again, as before, it's a bit tougher to understand for beginners.
01:26:10 Each approach has its own benefits.
01:26:14 We have to define what is the most important for us.
01:26:20 I think for a project where simplicity and flexibility are the key, functional programming might be great because of immutability and modularity.
01:26:29 But if you really need a strong encapsulation and clear hierarchy, then you might want to go for the class-based approach.
01:26:37 And then for very small projects, you can use it without using any kind of patterns.
01:26:44 Let's quickly explore the solution with a class-based approach.
01:26:51 So you should have a URL somewhere in the chat so you can check it out.
01:26:56 But I will also paste it right here so we can see it.
01:27:01 This is a very good example of a class-based approach.
01:27:06 Where using a class makes a lot of sense with this pattern.
01:27:10 Why?
01:27:12 It just makes more sense.
01:27:14 I mean, check this out.
01:27:15 We have a role, which is a class that allows us to create different instances of that role, right?
01:27:20 We can create an employee role.
01:27:22 We can create a manager role.
01:27:24 And each one will have a name, a permissions, and sub roles.
01:27:29 And then we create methods on this class.
01:27:34 Methods such as add permission, which pushes the permissions, role, which pushes the role, and then get permission, which extracts all of the permissions
01:27:44 from that specific role.
01:27:48 And then we have a display which displays their role.
01:27:51 So this is just a class which we call with different methods and see how simple this is?
01:27:56 We define in your role and we add permissions to that role.
01:28:01 Check this out.
01:28:02 You can just do this.
01:28:02 You can just add read.
01:28:04 You can also do write documents.
01:28:08 or something else.
01:28:09 The manager is a new role and again, we can add additional roles or permissions, but then we can make it extend.
01:28:19 Maybe a better thing to say here role would be to extend the role of an employee as well to extend their permissions.
01:28:27 This is one of the rare situations where I think that a class-based approach makes more sense than a functional-based approach.
01:28:38 I think many of you can agree with me in this specific situation.
01:28:43 It just shows you that we should maybe not hold a grudge against class-based JavaScript, which is something I'm to blame for as well a bit,
01:28:52 as I do a lot of functional.
01:28:55 It shows us that syntax is not what matters.
01:28:59 The underlying principles are, in this case, a composite pattern.
01:29:09 So yeah, it's super helpful in a lot of situations.
01:29:14 Like when you're dealing with any kind of stuff that might seem recursive, like trees or folders within folders, that's a hierarchical data structure.
01:29:29 Or when you're making computer screens with buttons, text boxes, and windows, such as a graphical user interface, because there's a lot of pop-ups that
01:29:38 appear on top of pop-ups.
01:29:40 Organizing files on your computer, that's file systems.
01:29:45 figuring out who's in charge at work, like an example that we had right here, that is organizational structure.
01:29:52 Putting together documents with pictures and text and all of that stuff, that's document processing.
01:29:58 Here you can also use composites.
01:30:00 And add drawing cool shapes, even in computer programs, that is graphic application.
01:30:07 So there are a lot of these applications where composites make a lot of sense.
01:30:15 Okay, and maybe for this example, I would dare to say, I'm not sure how I can even say this, but for this case, using a pattern maybe is even more simpler
01:30:27 than the first solution that we have seen without a pattern.
01:30:30 You have seen that class-based approach.
01:30:32 It makes quite a lot of sense naturally.
01:30:34 It seems intuitive.
01:30:36 Maybe a lot of these patterns would seem intuitive the more you do them, right?
01:30:42 And now we come to the next one and you can see that the more we have, there's a lot of these exciting patterns that we can go over.
01:30:52 I can see there was one question by Muhammad asking, how do you choose the patterns?
01:30:58 Is there a guideline?
01:31:00 Well, yeah, that's a good question.
01:31:03 First of all, multiple patterns can be used to solve a single problem or even multiple can be used at the same time for a single problem if it's a big
01:31:13 one to optimize it further.
01:31:15 But there is not a specific guideline, unfortunately.
01:31:18 This is one of those cases where I'm going to give you an analogy from a React standpoint.
01:31:25 Some of you might be doing some front-end stuff on React.
01:31:29 People often ask me, how do I learn when to split a component into multiple smaller components?
01:31:38 There's no precise answer.
01:31:41 The more you work with different things, the more you understand that if you have a form, typically it should be its own component.
01:31:48 If a button starts changing shapes and colors, even the button should be its own component.
01:31:54 And the more granular the things get, the more specific they can be and they can become their own components.
01:32:03 I call it pattern recognition.
01:32:07 The more you code and the more you learn how to write code and to write actual production-ready code, the more problems you encounter,
01:32:18 the more you will start noticing patterns on when you can implement specific patterns right here.
01:32:30 Start noticing patterns of implementing patterns.
01:32:35 I'm just talking too much here, but I think you get the idea.
01:32:38 Unfortunately, it's more so an art than science.
01:32:44 Although with experience, it's going to become pretty clear when you should use any one of these patterns.
01:32:53 hope that answers the question.
01:32:54 I know it might not be the answer you expected.
01:32:57 There's not a bullet point list of problems and then specific patterns for them, but it is what it is.
01:33:05 It comes with experience.
01:33:08 Let's talk about another structural pattern called a proxy It provides a placeholder for another object to control its access.
01:33:23 So if you have an object, you create a proxy and then through additional events, you can manage that object.
01:33:33 Essentially, it acts as an intermediary for another object between the client and the database in this case, or the target object controlling the access
01:33:45 to that object.
01:33:47 That is a proxy.
01:33:48 Here we have an example with different clients trying to access a database.
01:33:54 In this case, they're doing that through a proxy.
01:33:58 A bit of a simpler example, imagine you are a personal assistant to a celebrity and your job is to filter and control who gets access to that celebrity.
01:34:11 And instead of letting everyone interact directly with the celebrity, you act as a proxy.
01:34:18 Okay.
01:34:20 So let's explore it in action.
01:34:23 I'm going to go back to the code and go to 06. proxy problem.
01:34:30 This one fits in one screen with less code than before.
01:34:34 Let's see how to implement it.
01:34:36 Let's say we're working with cache and we try to fetch data with cache, some specific piece of info stored in the cache.
01:34:46 We pass a key and then we simply return the cache data for that key.
01:34:52 And then we also have a console log saying fetching data for this key.
01:34:57 And we store that key.
01:34:59 We store that piece of data within that key.
01:35:03 If we don't, if we don't already have it there and then return the whole piece of data.
01:35:07 So there's a couple of situations here.
01:35:09 We have fetch data with cache or to which we pass the key one.
01:35:14 Okay.
01:35:15 And we're expecting it to say fetching data for key one.
01:35:19 Similarly, we're saying fetching data with cache with key two, we're expecting key two.
01:35:25 And then if we call it again, we expect it to get the data with cache from cache.
01:35:32 It's the same expected response here.
01:35:34 First, it will fetch it, but the second time we're expecting for it to just return that data.
01:35:40 And this works because here we have a global object acting as cache.
01:35:44 So for the first time, which is here, it will actually set the data and return it.
01:35:50 And in the next case, it will just get it from the cache and show it.
01:35:55 This is actually a great minimal example of how cache in browsers work.
01:36:02 And with key three, it will just try to fetch it without returning it.
01:36:05 So let's try to actually run this by going right here, CDing into CD06 proxy.
01:36:14 And we can simply run node.
01:36:17 problem.js.
01:36:19 And we can see that first, we fetch the data, and then we get the data right here for key one fetching, then we get the data for key one.
01:36:30 And then for the second time we call this, we just get the data from key one, returning it without fetching it in the first place,
01:36:36 because it's already stored there.
01:36:38 So let's see how we can use a proxy pattern in this case.
01:36:43 Let's go to, once again, do we want to do two or one?
01:36:47 I think that in this case, maybe a, let me think about it.
01:36:55 Even a, yeah, I think in this case, a functional approach might make a lot of sense in this case, since we're going to create a cache proxy.
01:37:03 So let's try to do it like that.
01:37:05 I'm going to create a simple function const fetch data is equal to, and then here we can choose which key we want to fetch.
01:37:15 And this will look the same as before, where we simply console.log fetching data with key right here.
01:37:24 And then we can return data with key like this.
01:37:31 Okay, pretty simple, right?
01:37:33 We're fetching that data and we return that data.
01:37:36 But now we can create a cache proxy, okay?
01:37:41 Const create cache proxy is equal to, and here we can get a data service, okay?
01:37:51 Pretty cool.
01:37:51 So we're getting the data service, which is just a parameter name, not a special JavaScript keyword.
01:37:57 We're getting that service and let me show you how we'll use it.
01:38:01 We can define a cache same as before as an empty object.
01:38:07 And then we can return an object that will have a property of fetch data.
01:38:15 which will be a function that accepts a key, and here we'll check if this key already exists in this cache.
01:38:26 If so, we will simply console log cache hit for key this, and then we will return that cached piece of information.
01:38:36 But if we don't hit a cache, then we can say const data is equal to data service dot, we're not going to fetch it, we're just going to call it as a function
01:38:49 and pass the key which we want to get.
01:38:52 Once we get it, we can update the cache and then we can also return it right here.
01:38:56 Okay.
01:38:59 But now let's see how we'll actually put that to use.
01:39:03 What is this data service anyway?
01:39:06 Well, const data service, in this case, will basically be equal to the fetch data function.
01:39:16 So it's the same thing.
01:39:17 We could have just called it like that, right?
01:39:19 But in this case, we are extending or passing that data service as a prop.
01:39:24 So I think this makes sense.
01:39:26 And then when we call it const cache proxy, is equal to create cache proxy.
01:39:34 And then we pass the actual data service to it, which as we have already figured out is just the fetch data function.
01:39:40 So I think even doing just that is fine.
01:39:43 We can just pass it like this, create cache proxy, or this should be, it should definitely be cache proxy, not catch proxy.
01:39:52 So create cache proxy and we pass the data service, which is just the fetch data function right here.
01:40:01 Once we do that, we can test it out.
01:40:04 This would be pretty crazy.
01:40:05 So if we do a console log and we call a cache proxy dot fetch data for key one, what are we expecting to hit?
01:40:15 Let's see.
01:40:17 Cache proxy, which is right here, dot fetch data, which is this function right here.
01:40:25 we're passing a key, which is key one, which doesn't exist on the global cache just yet.
01:40:32 So this will exit, and then simply we will return the data service, which will console log fetching data with key for key one.
01:40:43 And then the next time we call this same thing, like fetch data for key one, next time it will go into this cache proxy,
01:40:54 which we have already created, which consists of this cache, which is now updated because now it contains a key one there.
01:41:01 It will hit here and it will hit a cache and automatically return the cache key.
01:41:07 So let's test it out.
01:41:10 If I go here and run node solution2.js, you can see fetching data with key one, and we get the data with key one, cache hit for key one,
01:41:22 and then we get the data with key for key one.
01:41:25 So exactly as I explained, this is how the create cache proxy works.
01:41:34 Hope that makes sense.
01:41:35 And there's also a class-based solution for this one as well, which looks something like this.
01:41:44 You have a solution in the chat as well.
01:41:46 But if you want to check it out, we basically create a data service, which can then fetch the data.
01:41:53 And we have a cache proxy which takes in the data service and has a function for fetching the data.
01:42:00 And it's basically the same thing as before, just in a class-based form where we create a new instance of the cache proxy with that data service.
01:42:11 We can also explore the benefits and drawbacks of it a bit.
01:42:16 Again, same thing as before with the simple approach, but using the class-based approach, we have encapsulation because cache logic is encapsulated within
01:42:26 cache proxy, promoting modularity and separation of concerns.
01:42:31 Again, separation of responsibilities because cache proxy handles caching while data service focuses on fetching data.
01:42:40 Okay, that's the thing.
01:42:42 Before we didn't have that in the original problem.
01:42:45 And then of course, flexibility.
01:42:48 And it's the similar thing with the functional approach.
01:42:50 We have encapsulation and cleaner separation of concerns.
01:42:55 Great.
01:42:57 Once again, if you're going for something simple, then you might not even need a data pattern here.
01:43:03 And if you prefer object or functional, it's completely up to you which one you might want to choose from.
01:43:11 Great.
01:43:11 So if we go back right here, this pattern is useful for a lot of different things, exactly like I showed you, caching, lazy initialization,
01:43:23 access control, logging, monitoring, and even more stuff.
01:43:30 That is the proxy.
01:43:33 And now we dive into a completely different set of patterns called behavioral patterns.
01:43:43 And these are all about how objects interact and communicate with other objects.
01:43:51 So without behavioral patterns, they almost have no communication.
01:43:56 They don't know about each other.
01:43:58 But here we can form some kind of a relationship between these two objects.
01:44:03 And you might have noticed me talking about object this, object that all the time for all of these pattern categories.
01:44:13 Well, you know, everything in JavaScript is an object.
01:44:18 And a couple of examples of behavioral patterns include an observer pattern, which looks something like this.
01:44:27 Again, these illustrations are getting complex as we go, but it's all about having a single subject, also known as a publisher,
01:44:37 and then having multiple observers, also called subscribers, that get notified when the subject state changes, like subscribing to a newsletter.
01:44:49 Imagine you have a subject, like a newspaper publisher, and you have subscribers who want to know when the new newspaper is out.
01:44:58 The observer pattern is like that.
01:45:01 The subject or the publisher maintains a list of subscribers or observers.
01:45:08 And when something interesting happens, like when a new issue is being published, it notifies all the subscribers so they can act accordingly.
01:45:18 So that is the publisher and the observer pattern.
01:45:22 Great.
01:45:23 Let's check it out in the code base as well by moving over to 07 observer.
01:45:30 And here I have just a very quick example for you.
01:45:34 As you can see in 07, let's check out how this works.
01:45:41 We have an event emitter, which requires a base CommonJS module called events.
01:45:51 And it basically allows you to stream and emit specific events.
01:45:56 Here, we can see that we have a class that extends the event emitter module.
01:46:04 where it has a new method called publishedNews with a parameter of news and it just simply says console.logPublishingNews and it emits a new event called newsPublished.
01:46:19 Now, we can create a new newsPublisher instance like this and we want to subscribe the subscribers to the newsPublished events.
01:46:30 So it looks something like this, news publisher dot on news published.
01:46:37 Okay.
01:46:38 So when the new event is emitted, then we have a callback function that allows us to subscribe that subscriber to the news.
01:46:49 In this case, it's just a console log saying you've been subscribed.
01:46:54 We can also have a new news publisher, or rather it's the same instance of a news publisher, but we can call a new on once again and we can subscribe a
01:47:05 different person.
01:47:05 Like we can subscribe as many people as we want whenever an event is emitted.
01:47:10 And we can listen to multiple events as well.
01:47:13 So what happens now?
01:47:15 Well, when we publish news, news publisher that publish news, breaking new discovery in science.
01:47:23 What can we do here?
01:47:24 Well, we're causal logging that we're publishing something, but we don't simply want to yell it out without nobody to come and nobody to get it.
01:47:33 So what we're hoping will happen that these callback functions will be executed so that the people that have already subscribed to this news publisher
01:47:44 will receive those news immediately.
01:47:47 And that's exactly the point behind the observer pattern.
01:47:52 So let's try it out.
01:47:53 If I cd into 07 and run a node, example.js, we can see that publishing news and then subscriber 1 and subscriber 2 both received that notification as part
01:48:10 of the callback function.
01:48:13 And that's more or less all there is to it.
01:48:15 In this case, we don't have a without pattern example because this is it.
01:48:19 This is the pattern.
01:48:20 This is how we can emit events and then subscribe to those events as well.
01:48:27 And we can see just a couple of benefits of doing this.
01:48:30 Like if I expand this right here, we see a concept known as loose coupling.
01:48:35 And this observer pattern promotes loose coupling between objects.
01:48:40 Subjects or publishers and observers, subscribers don't need to know much about each other.
01:48:46 They only need to implement a common interface, allowing for flexibility in how they interact.
01:48:54 Okay, and because of that, the design is very modular.
01:48:57 We have an event-driven architecture, which is great.
01:49:00 We completely separate the concerns and it is quite flexible.
01:49:06 Also, in this case, it actually reduces complexity because we're breaking everything down into smaller concepts.
01:49:14 This is the answer to Muhammad's question, I guess.
01:49:18 And that is when to use this pattern.
01:49:21 And you can use it when you need to implement a one-to-many dependency between objects.
01:49:27 In this case, we have one news publisher and multiple subscribers.
01:49:31 And if changes to one object require changes to multiple other objects, then this is a good pattern to use because it allows you to notify all dependent
01:49:41 objects automatically whenever the state of the subject changes.
01:49:45 when you want to decouple components in your system, meaning minimize the dependencies between them.
01:49:50 If you think about it, the solution to this problem could have been implemented by using other patterns we've learned about so far.
01:50:00 Which are some of the patterns that come to mind?
01:50:11 that we might have used to solve something like this, where we would have to combine the object of the subscriber and the object of the sender as well.
01:50:22 I mean, the example of a JavaScript pattern we have seen before where we could use this.
01:50:28 Yeah, Hector composite pattern could have been used here.
01:50:31 That is right.
01:50:32 Also, a factory pattern could have been used as well, if you think about it.
01:50:37 Like you create a news publisher, and then each subscriber that you create comes out of a factory of subscribers that by default can have specific properties
01:50:48 such as being subscribed to this thing, right?
01:50:51 But if they're not subscribed by default, then you have to add, you have to call them and you have to add a subscription.
01:50:57 But in this case, like it all works so flawlessly.
01:51:00 It just shows that you have different use cases for different patterns.
01:51:03 This one is great for one-to-many dependencies, decoupling, and when you want to implement event handling or notification mechanisms,
01:51:12 and when you want to support multiple observers with different behaviors.
01:51:17 It's just great to be able to do something like this.
01:51:21 There's also a valid question about an event emitter being exclusive to node environment.
01:51:27 It is certainly not.
01:51:28 Different programming languages and different frameworks as well have their own event emitters.
01:51:35 I'm pretty certain that something like this could be achieved through any kind of event emitting.
01:51:41 It can even be done through through the live functionalities of any kind of browser, through sockets specifically.
01:51:51 Think of it as emitting a message through a group chat.
01:51:57 This is great.
01:51:57 This is a phenomenal example.
01:52:00 If you have a group chat, multiple people are subscribed to the group chat, and that would be done not through node event emitters,
01:52:09 but rather through sockets.
01:52:13 Does that make sense?
01:52:15 That was Alexander, I believe.
01:52:18 No, that was Christopher.
01:52:20 Christopher, does that make sense?
01:52:21 Who would emit a socket IO thing to let everybody in that specific group know that a message was sent?
01:52:32 Great.
01:52:32 Perfect.
01:52:33 That makes sense.
01:52:34 Again, I'm glad that you asked that question because it once again points to the fact that it's not only the syntax or the code or the specific situation
01:52:46 that is happening right now, but this is rather an underlying principle that can be used in different examples with different code bases,
01:52:54 different programming languages, and more.
01:52:59 And even Jakub pointed out that it's not very difficult to implement an event emitter on your own if you want to use this pattern in the browser,
01:53:06 for example.
01:53:07 Completely true.
01:53:08 I'm sure there are libraries that already do it, but if you want to do it on your own, completely possible.
01:53:14 Great.
01:53:17 Let's see what else we have here.
01:53:22 So we have a strategy pattern in this case.
01:53:25 And yeah, maybe these illustrations are getting much more abstract now.
01:53:30 But you can see that with a strategy pattern, we have an airport, we have a bicycle, we have a bus, and we have a cab.
01:53:40 Basically, these are different options of how we can get from location A to location B.
01:53:48 with different strategies, with different parameters.
01:53:52 With a bike, if you have a bike already, at least, it's going to be zero bucks, but it's going to take more time.
01:53:58 But if you want to go with a bus, it's a medium cost, but also it's going to take some time.
01:54:03 The bus has to stop every now and then.
01:54:05 And with a cab, it's just you, you're on your way, right?
01:54:09 Highest cost, lowest time.
01:54:11 So sometimes it's important to choose a strategy.
01:54:14 So we have to choose which algorithm makes most sense in the specific situation.
01:54:21 It's like having a different strategy to tackle a problem and being able to switch between them easily.
01:54:26 Imagine you're playing a game and you're using different weapons to defeat enemies.
01:54:32 For every enemy, you're maybe going to use a different weapon.
01:54:37 That is basically a strategy pattern.
01:54:39 So let's explore it in code.
01:54:43 If I go back here and if we go to a strategy pattern, we can see a problem.
01:54:49 And what problem are we faced with right here?
01:54:53 it's different compression functions.
01:54:56 So we can compress with zip, we can compress with gzip, we can compress with, what is this?
01:55:02 This is decompression with zip and this is decompression with gzip.
01:55:08 And we can choose an example data which we want to compress, although this is just like 15 characters.
01:55:15 Sure, let's try to do it.
01:55:18 We have the compressed data, decompressed data, and then we can select the compression algorithm.
01:55:25 Based on a specific situation, whether it's an image, video, or MP4, or MOV extension, we can choose an appropriate strategy.
01:55:38 So in this case, let's say that we want to use a gzip and we can do that.
01:55:44 And for decompression, we can choose zip.
01:55:46 It's totally okay.
01:55:47 So here, this is an example of how we can choose different strategies.
01:55:51 Now let's see how we can implement that in code.
01:55:57 Once again, I provided two different solutions.
01:56:00 There's a class-based solution as well as a functional one.
01:56:05 I think I'm going to stick with a functional one for now.
01:56:09 In this case, we can define different compression strategies.
01:56:13 And I do think that I can just take these over from above.
01:56:18 I'm going to do gzip and also zip.
01:56:22 So I'm going to copy them.
01:56:23 You can do that too.
01:56:25 and paste them right here, and I'm going to call them basically...
01:56:32 These are not going to do the compression automatically, or maybe they...
01:56:36 Let's see.
01:56:37 Yeah, they will do the implementation here, but I will just rename them for the sake of it.
01:56:42 It will be zip-compression-strategy, and also gzip-compression-strategy.
01:56:51 So they do something, and then they return the compressed file.
01:56:56 Now we're going to create a context function that sets the strategy and compresses the data.
01:57:04 It looks something like this.
01:57:06 Const compression context, okay, is equal to strategy that we pass in.
01:57:15 And then we create a new function called set strategy to which we pass the new strategy, which we want to use.
01:57:27 and we set the strategy passed into the original function, into the compression context, equal to new strategy.
01:57:34 Then we can call a compress function.
01:57:38 So const compress, we get the data which we want to compress, and we return a compression with a strategy.
01:57:46 So we simply pass the data which we want to compress into the strategy which we want to compress.
01:57:54 And then we can return an object containing both the set strategy and the compress functions, like this.
01:58:04 So let me just now hold these functions and then I will explain how this works.
01:58:09 We get the data, const data hello world, which I want to compress.
01:58:14 And we have two different strategies, which is a zip strategy and gzip strategy.
01:58:20 We need to create a compression context.
01:58:22 So const context is equal to compression context to which we pass the compression strategy.
01:58:29 Okay.
01:58:31 That is the context under which we want to compress.
01:58:35 So now if I run a console log of something like context.compressData, you can see that here, I don't really know.
01:58:44 Am I compressing it with zip or gsep?
01:58:47 It's abstracted from me.
01:58:50 And I don't need to know.
01:58:51 I just want to compress it.
01:58:52 But the underlying principles are there so that it uses the best situation given the context.
01:59:01 So in this function, we can figure out, or before we declared the context, we can figure out which situation is the best one to use.
01:59:11 And that's why there is this context right here to which we pass the desired strategy.
01:59:17 And then we simply call it compress, and it's going to use the strategy that we passed right here.
01:59:23 So if we pass something, if we call it again, it's going to again compress it with this one.
01:59:29 But if we create a new context, or if for some reason the strategy changes, this is the main point, context.setStrategy,
01:59:38 we can now set it to be gzip compression strategy.
01:59:43 I think that's how we call it.
01:59:44 Yep.
01:59:46 So if for whatever reason the strategy changes, we change the context and then it's going to the same call context that compress will now use a different strategy.
01:59:58 I hope this makes sense.
01:59:59 I also want to check out a solution with a class-based approach.
02:00:03 It should be somewhere in the chat as well, but we can quickly go over it.
02:00:08 I think this is the one where it also makes a lot of sense with this.
02:00:13 We first define the compression strategy interface that just has a method that compress, that takes in the data, but it throws an error saying that the
02:00:24 compressed method must be implemented.
02:00:26 So then we have different classes that define different compression strategies using zip or using gzip.
02:00:34 And we have a compression context.
02:00:38 that has the constructor that chooses strategy.
02:00:40 We have the set strategy to that strategy.
02:00:43 And finally, we call this that strategy that compress.
02:00:46 So the situation is the same.
02:00:49 We get the data.
02:00:51 We create different strategies.
02:00:54 We create the context dependent on which we want to modify the strategy.
02:00:59 And then we call the context that compress and we use that data.
02:01:05 This one is compressed using zip and this one is compressed using gzip.
02:01:11 And we can quickly explore the readme and see the pros and cons of all of this.
02:01:18 Even the normal approach is simple and straightforward, but not scalable.
02:01:22 And it has tight coupling between the client code and specific strategies.
02:01:28 And it's harder to manage it or extract or extend with new strategies.
02:01:33 Object-oriented as always gives us encapsulation.
02:01:36 We also have some polymorphism happening here because we can easily swap existing strategies without modifying the client code.
02:01:44 You can also check that right here.
02:01:46 No code is being modified anywhere by choosing this strategy.
02:01:52 We just define it.
02:01:54 And based on the context, we just do it.
02:01:57 We didn't modify anything.
02:01:58 We just chose a different class based on which we can then, in this case, compress it.
02:02:07 More scalable, more reusable, as always.
02:02:10 And similar things go for the functional approach, but it's a bit more concise, a bit more simple, flexible, and lightweight.
02:02:17 Okay?
02:02:18 That's it.
02:02:21 I hope this makes sense as well.
02:02:24 And we can move over to the next example right here, which is the command pattern.
02:02:32 There we go.
02:02:34 You should be able to see it right now.
02:02:37 This command pattern, again, you can see that these get a bit more abstract as we go.
02:02:45 And the command pattern, in this case, looks like you press a specific button, like you save it, and then it gets catapulted over to the receiver.
02:02:55 So taking a quick look, it might be similar to the observer pattern where we emit specific objects or specific events, but this one is a behavioral pattern
02:03:07 that turns a request into a standalone object that contains all the information about that request.
02:03:16 Let me explain.
02:03:18 Once you transform it this way, It lets you parameterize different methods with different requests or maybe different delays or queues of different request
02:03:34 executions and support different undoable operations.
02:03:37 That's a lot, right?
02:03:39 So in simpler terms, think of the command pattern as a way to package actions or commands as objects so they can be passed around,
02:03:53 stored, and executed later.
02:03:55 Okay, so we package it nicely.
02:03:58 You package the commands into the actual thing so you can then call them later on from that thing if you need to.
02:04:07 And let's quickly check that in the code as well.
02:04:12 So if we go here, we have the command pattern.
02:04:15 And yeah, as you can see, we have different commands such as deduct funds, credit funds, execute transaction, rollback transaction,
02:04:27 and also undo a command.
02:04:30 And then we have different commands, which is just like a list of different commands, which want to do one after another,
02:04:37 like deduct funds first from this account of an amount of 100, and then credit funds into the account of amount 100 as well.
02:04:48 And then we call executeTransaction with different commands.
02:04:53 I hope this makes sense.
02:04:56 It's not super important what these are.
02:04:59 Well, these are not doing anything right now, basically.
02:05:01 It's just cons and logs that says deducting, that says crediting, and then the transaction maps over all of each or does a for each and then executes all
02:05:12 the transactions.
02:05:13 Let's quickly run it as well to check it out by cd-ing into 09 command and then running node under the problem.js.
02:05:23 So you can see that we start the transaction, we deduct 100, we credit 100, and then the transaction has been committed successfully.
02:05:33 So now let's explore how to do it with a functional approach using the command pattern.
02:05:41 In this case, I believe the functional one will be the preferable solution.
02:05:45 Let's check it out together.
02:05:49 In this case, again, there is a lot of code connected to this, as the example was a bit lengthy as well, if we see it here.
02:06:00 We could take all of the code and then paste it there, but I don't want for us to break something and then to be stuck on fixing that.
02:06:12 I'd rather want us to focus on the actual actual, as I said, underlying logic and thinking behind that.
02:06:21 So let's quickly go to the chat and there should be a solution two for this one, as well as a solution one for this specific exercise.
02:06:33 So let me just take it and move it over to solution two.
02:06:38 I think Sujata will be able to add our own solution there.
02:06:44 If not, we can go over it right now and it will be provided to you at the end of the workshop.
02:06:55 It's very similar to the problem, but it uses the actual pattern.
02:07:00 Let me show you how it works.
02:07:02 All of these things are the same.
02:07:06 As you can see, it actually has a create deduct funds command.
02:07:11 which basically calls the create command, which in this case acts as a higher order function.
02:07:22 So see, we have createDeductFunds command and we basically encapsulate it right here.
02:07:28 So createDeductFunds, we need to know which account and we need to know which amount, okay?
02:07:35 And then we call a create command that returns the execute and undo.
02:07:42 In this case, to it, we pass the execute and we provide undo.
02:07:47 That way, it is much simpler for us to execute and undo because each command already has these two actions.
02:07:57 Then we repeat the process.
02:07:59 Even for crediting, in this case, we credit and then we undo credit.
02:08:04 And then we have something known as a transaction manager, which keeps track of all of these commands.
02:08:11 So we have an add command, which pushes the command to the end of the list of commands.
02:08:16 And then we have an execute transaction, which maps over all of them and then calls the .execute on them.
02:08:24 Or if you want to roll it back, we can reverse it and then for each, just call the undo.
02:08:32 And yeah, we basically generate a client transaction manager like this.
02:08:38 And then by calling the create transaction manager, this is like initializing a class.
02:08:43 We'll see it later on in a class-based approach as well.
02:08:46 We add a command to a transaction manager.
02:08:51 Think of this like an ATM or something.
02:08:53 It's going to go one by one.
02:08:55 And in this case, we want to deduct or we want to credit and it will finally execute the transaction.
02:09:02 So it'll work in the same way as before by running node solution two.
02:09:07 There we go, solution2.js.
02:09:11 You can see that it will deduct credit and then all of the transactions have been committed successfully.
02:09:17 We can also explore how to do it with a class-based approach.
02:09:22 And I think I might have been wrong.
02:09:24 This could be one of those examples where classes are a much better way of handling it, although it's significantly longer.
02:09:33 But think of it essentially as creating your own calculator.
02:09:37 So we have a db command.
02:09:40 This is a database of different commands that we have that the calculator has to keep track of.
02:09:48 And then we have options of executing or undoing these commands.
02:09:53 We have a class to deduct funds, which extends this super class right here.
02:10:00 And it figures out which amount and which account do we want to deduct from.
02:10:04 And then it also has its own execute and undo that in this case are deducting.
02:10:12 Similarly for crediting, they are crediting and we can undo the credits right here.
02:10:18 And then the transaction manager itself gets access to this command.
02:10:22 We push all the commands and we execute all of them here.
02:10:26 And the rest is the same as in the functional approach.
02:10:30 We create the transaction manager by initializing a new transaction class.
02:10:35 And then we can basically add different commands to be executed.
02:10:41 And there are many benefits for using a command pattern.
02:10:46 Let's check them out right here.
02:10:48 Basically, once again, encapsulation, polymorphism and readability.
02:10:52 And with the functional, again, it's a bit more flexible, but the most important thing is that each command is encapsulated within functions,
02:11:01 which has better code organization and still allows for dynamic construction.
02:11:06 It's also more readable and there's less boilerplate code.
02:11:10 As for classes, there's quite a lot of code we need to write just to make it work.
02:11:14 And similarly, all approaches have their pros and cons, but yeah, this was the command pattern.
02:11:25 Great.
02:11:26 Let's check this out.
02:11:28 And believe it or not, all of these patterns I've showed you right now, were the most widely known design patterns or JavaScript patterns in our case.
02:11:39 And there is another on top of this, like these aren't the best ones, the most popular ones, the ones that are used the most.
02:11:47 But on top of it, there's another category of patterns called architectural patterns.
02:11:56 And these patterns are like blueprints for building a house.
02:12:02 They provide a structured way to solve common problems in software development, like blueprints that help architects to build houses.
02:12:12 With it, we have different patterns that focus on different aspects of software design.
02:12:17 we have the MVC or the Model View Controller pattern.
02:12:23 And many of our popular frameworks and libraries that we use, use these patterns behind the scenes.
02:12:31 And yeah, don't confuse that with MVP.
02:12:34 This is MVC, Model View Controller.
02:12:39 It is a widely used design approach in software development and it's used for building user interfaces for web applications,
02:12:47 even desktops and mobile apps.
02:12:50 MVC divides an application into three interconnected components, each with its own responsibility.
02:12:58 A model, which represents the data and business logic of the application, Then we have, it also manages the application state and data persistence,
02:13:09 and it performs all the operations like data validation, calculations, and even database interactions.
02:13:15 The view itself is not worried about any of that.
02:13:20 It simply presents the data from the model to the user interface.
02:13:24 It displays information to the user in a visually appealing format, and it captures user input and sends it to the controller for processing.
02:13:34 Once again, Controller has nothing to do with that either, but it basically acts as an intermediary between the model and the view.
02:13:43 It handles the user input and triggers all of the appropriate actions on the model, and it updates the view of the results based on the model operations.
02:13:55 And there are many benefits of using an MVC infrastructure or architecture, modularity, reusability, testability, scalability.
02:14:05 And these are basically all of the benefits of the existing patterns we talked about.
02:14:11 But this one is just, you can see how different frameworks are already using it.
02:14:15 There's also the MVVM model view view model pattern.
02:14:21 And it is a widely used client-side application development pattern used in frameworks like Angular, Vue, Knockout, and even a couple more that I haven't
02:14:34 used personally.
02:14:35 But it builds on the concept of the model view controller with the addition of the view model layer.
02:14:43 It divides the application into three main components.
02:14:47 The model, which has all of the same functionalities.
02:14:51 The view, which is again very similar to MVC model.
02:14:55 And then we have the view model, which acts as an intermediary between the two.
02:15:00 exposes the data to the view and prepares it for display, but also handles user interaction from the view and translate them into the actions on the model,
02:15:11 providing data binding mechanisms to synchronize the view of the model, the view model, and vice versa.
02:15:22 So, as you can see, different use cases, different frameworks use different patterns.
02:15:28 And based on that, they choose how they want to work and behave.
02:15:32 There are many benefits to using MVVM.
02:15:36 There's a separation of concerns, reusability, data binding in this case.
02:15:42 This is a very similar one to before, but again, simplifies the way that we bind the data between the view and the view model,
02:15:51 allowing us to change, allowing the changes to automatically reflect in the other without us having to manually intervene.
02:16:00 It's also quite testable and it uses a declarative functional approach.
02:16:07 And there are many, many more patterns.
02:16:10 So let's explore some of them.
02:16:13 The microservices pattern is a design approach.
02:16:17 And this is one that you, this is a very popular one nowadays.
02:16:22 I think that Netflix popularized that before, but it's used, it's a pattern where you have a collection of small independent services.
02:16:36 each focused on a specific business capability.
02:16:40 These services are loosely coupled, they're not connected, they're independently deployable, which is the primary thing,
02:16:47 and they communicate well with each other via well-defined APIs.
02:16:54 These are used in cloud-native applications like for scalability, resilience, in cloud platforms like AWS or Google Cloud,
02:17:05 distributed systems, and also enterprise applications, allowing them to decompose and enhance their basically their cloud functionalities as well.
02:17:20 Yeah, it's all about clouds in this case because we can decompose the applications into smaller pieces which are typically easier to serve to the users.
02:17:31 It's more manageable and increases the agility in the way that we share or send the content to the user and also time to market is faster because you don't
02:17:43 have to manage a huge monolithic server.
02:17:47 There are also other patterns like layered architecture, EDA, event-driven architecture, and a repository pattern.
02:17:56 But there's a lot of them, right?
02:17:58 And every architectural pattern has its own advantages and disadvantages.
02:18:05 As developers, we need to know how to use them depending on what we're building.
02:18:11 If we need to connect two different systems smoothly, we might use the adapter pattern.
02:18:16 But if we want to create a lot of similar things without making the code messy, we might use the factory pattern.
02:18:24 Sometimes, we might choose between writing code in a way that focuses on actions or data.
02:18:31 That way, we can use the event-driven path.
02:18:34 The key is to find the right balance and pick the best approach for each situation.
02:18:39 It's not just about sticking to just one way of doing things, but knowing when to use which method to do the job well.
02:18:51 Before we finish, I want to remind you that I've also included a couple of exercises for you to practice and just implement things that you've learned
02:19:03 today about different patterns.
02:19:07 Give them a try experimenting with both object-oriented and functional approaches.
02:19:13 And they're right here in this repo that you already downloaded.
02:19:17 So if you go here, you'll notice that there's a 0, 10 exercises.
02:19:21 And there are nine different exercises corresponding to nine different patterns.
02:19:28 But as we've discussed before, you don't have to use one.
02:19:31 You can use multiple or you can mix and match.
02:19:34 You can just use one, right?
02:19:35 So we have different patterns right here.
02:19:38 different problems on which you can try to apply these patterns too.
02:19:44 Thankfully, I made your job a bit easier so that technically each one of these problems is specifically crafted to make use of a pattern,
02:19:55 right?
02:19:56 So in real world, sometimes it's not going to be as obvious, but here, after you maybe are realist in this workshop, you should be able to try to implement
02:20:06 some of these patterns on your own.
02:20:10 So again, the more you practice and explore these patterns and the more you code, the better you'll be able to make informed decisions on which patterns
02:20:21 you should put where.
02:20:23 So this is it.
02:20:24 This is the end of the talk.
02:20:26 It was a bit longer one, definitely longer than two hours, but hopefully you were able to extract something useful out of it.
02:20:36 I know that it's not possible to get everything to remember all the patterns or even more to use them within the code tomorrow.
02:20:47 But I hope that this just at least opened up your mind to how we can optimize your code bases.
02:20:53 And if you can use some of these patterns, maybe within your company with the existing code base, Like if every single one of you could maybe try to make
02:21:06 a mental connection to maybe think about the code base that you're currently using, maybe it's a huge code base for your work project,
02:21:19 and implement at least one of these patterns within the platform you're working on, that would be amazing.
02:21:27 And maybe some of you even can make that connection.
02:21:30 Maybe you've been doing something the other day and you thought, oh, I didn't even know about this.
02:21:34 Maybe I can use it.
02:21:36 So it will make your code better.
02:21:38 It will just in general make it that much more scalable.
02:21:46 And even though many of you don't work for enterprise level companies, many of us don't do that.
02:21:52 So it might not be super noticeable there, but trust me, it will be noticeable in code cleanliness, code readability, or just in general,
02:22:03 making your peers respect you more, making your superiors also respect you more as you implemented something that saves time,
02:22:10 saves developer energies, reduces bugs.
02:22:13 You're positioning yourself as the expert in the industry here by implementing something that only true engineers can do.
02:22:23 So yeah, I'm going through the comments right now or the chat.
02:22:27 Thank you guys so much.
02:22:28 I'm so glad that you found a lot of value in this workshop.
02:22:33 It was a pleasure just being here and sharing all the things.
02:22:38 Yeah, we managed to prepare a lot of stuff.
02:22:41 Sujata here as well was very helpful with all the examples and everything.
02:22:44 So yeah, huge, huge thanks to her.
02:22:48 But yeah, it's a pleasure being here in this workshop on Git Nation with you.
02:22:55 And I'm excited that all of you actually took the time to be here.
02:23:01 It's a Friday even, right?
02:23:03 You're here on a Friday afternoon, Central European time, and you took two and a half hours of your day to learn about JavaScript code patterns.
02:23:13 I mean, that's just good for you.
02:23:16 It's just amazing.
02:23:18 Um, thank you so much.
02:23:20 Have a great rest of the day.
02:23:22 And yeah, just to let you know, if, if you want to reach out or if you want to get in touch, feel free to, um, contact me on LinkedIn YouTube works as well.
02:23:32 Right.
02:23:33 Um, we are, we have, we post a lot of these projects on, on YouTube.
02:23:38 They're more so intermediate, right?
02:23:39 Not necessarily.
02:23:41 Um, um.
02:23:43 not necessarily for senior developers, but if you're an intermediate developer or if you're a senior developer that hasn't necessarily worked with React
02:23:55 or the latest and greatest of what Next.js has to offer, then you definitely might want to explore what we do on YouTube.
02:24:04 So once again, if you want to reach out on LinkedIn, you can do so right here.
02:24:08 It's Adrian Hadgdon.
02:24:11 You can add me right there.
02:24:13 And on YouTube, that's going to be JavaScript Mastery YouTube channel as well.
02:24:19 Just wanted to quickly share that with you in case you want to get in touch.
02:24:24 We also have a JS Mastery Pro website where you have a couple of courses.
02:24:29 This is not going to be a good fit for most of you.
02:24:32 But if you want to learn a bit more about Next.js, we have done a great course specifically on Next.js.
02:24:40 So it teaches you how to use many of these patterns.
02:24:44 Well, you make use of many of the patterns that Next.js team has put into the framework, then this is for you.
02:24:50 And also, just in the next couple of days, we're starting with the masterclass as well, which is our own version of a bootcamp for specifically for mid
02:25:01 to senior developers, not for juniors and sorry, not for people that don't yet have experience specifically crafted to get further in the career with additional mentoring.
02:25:14 So that's jsmastery.pro.
02:25:17 It was a pleasure once again, being here.
02:25:20 Reach out.
02:25:20 If you have any questions, reach out to on LinkedIn, YouTube.
02:25:24 I'll see you there.
02:25:25 Thank you so much and have a great rest of the day.
02:25:28 Bye bye.