We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.
Feedback

Design Patterns for .NET Programmers

00:00

Formale Metadaten

Titel
Design Patterns for .NET Programmers
Serientitel
Anzahl der Teile
110
Autor
Lizenz
CC-Namensnennung - keine kommerzielle Nutzung - Weitergabe unter gleichen Bedingungen 3.0 Unported:
Sie dürfen das Werk bzw. den Inhalt zu jedem legalen und nicht-kommerziellen Zweck nutzen, verändern und in unveränderter oder veränderter Form vervielfältigen, verbreiten und öffentlich zugänglich machen, sofern Sie den Namen des Autors/Rechteinhabers in der von ihm festgelegten Weise nennen und das Werk bzw. diesen Inhalt auch in veränderter Form nur unter den Bedingungen dieser Lizenz weitergeben
Identifikatoren
Herausgeber
Erscheinungsjahr
Sprache

Inhaltliche Metadaten

Fachgebiet
Genre
Abstract
Between lambda expressions in C# and functional programming in F#, we can now make use of patterns well beyond those popularized by the so called GOF. In this presentation we will learn the benefits of some useful patterns and how to implement them using C# and F#.
23
63
77
Gewicht <Ausgleichsrechnung>E-MailSoftwareentwicklerFaktor <Algebra>Twitter <Softwareplattform>MusterspracheAusnahmebehandlungSoftwaretestMusterspracheMereologieCodeVererbungshierarchieSchnittmengeGüte der AnpassungBitMultiplikationsoperatorProgrammierumgebungRechter WinkelWhiteboardTelekommunikationProgrammiergerätSoftwareSoftwareentwicklerPhysikalisches SystemEinsWort <Informatik>ProgrammierspracheEntwurfsmusterTabelleArithmetischer AusdruckPi <Zahl>EinflussgrößeWellenlehreLambda-KalkülGewicht <Ausgleichsrechnung>Computeranimation
Physikalisches SystemGenerizitätSoftwareentwicklerZeichenketteWeb-SeiteDateiformatStichprobeHydrostatikOvalSpielkonsoleObjekt <Kategorie>InstantiierungDatentypVolumenSystemaufrufBitProgrammierspracheCodeRechter WinkelMultiplikationsoperatorMusterspracheInstantiierungKategorie <Mathematik>KonfigurationsraumEntwurfsmusterMathematikHochdruckTypentheorieObjekt <Kategorie>Web-SeiteFaktor <Algebra>ZeichenketteC sharpKlasse <Mathematik>Web-ApplikationHydrostatikLesezeichen <Internet>ParametersystemCompilerStichprobenumfangGewicht <Ausgleichsrechnung>FehlermeldungPunktSpiegelung <Mathematik>BenutzerbeteiligungSpezifisches VolumenData DictionaryRahmenproblemMomentenproblemAppletSchlüsselverwaltungProdukt <Mathematik>RelationentheorieMultiplikationMathematische LogikAutomatische IndexierungStützpunkt <Mathematik>ProgrammierungCASE <Informatik>BildschirmmaskeFramework <Informatik>VariableDienst <Informatik>Sampler <Musikinstrument>BestimmtheitsmaßKartesische KoordinatenProzess <Informatik>PRINCE2MereologieHierarchische StrukturNatürliche ZahlWasserdampftafel
E-MailGewicht <Ausgleichsrechnung>Twitter <Softwareplattform>Faktor <Algebra>MusterspracheAusnahmebehandlungSoftwaretestSoftwareentwicklerPhysikalisches SystemGenerizitätSpielkonsoleStichprobeHydrostatikOvalCOMCodeKlasse <Mathematik>GruppenoperationMusterspracheComputerspielCodeKlasse <Mathematik>Hierarchische StrukturParametersystemAdressraumSystemaufrufVererbungshierarchieFunktionale ProgrammierspracheLambda-KalkülGruppenoperationObjekt <Kategorie>p-BlockMessage-PassingDifferenteZweiE-MailMaschinenspracheMultiplikationsoperatorLesezeichen <Internet>ProgrammbibliothekMathematikInterface <Schaltung>Inverser LimesArithmetischer AusdruckOvalMereologieBildschirmmaskePunktSoftwareentwicklerFächer <Mathematik>BitKontextbezogenes SystemEndliche ModelltheorieCOMCASE <Informatik>Rechter WinkelPhasenumwandlungResultantePhysikalisches SystemRelationentheorieGraphiktablettWort <Informatik>Vollständiger VerbandNichtlinearer OperatorKartesische KoordinatenMaßerweiterungProgrammierungFunktionale ProgrammierungComputeranimation
Physikalisches SystemGenerizitätSoftwareentwicklerStichprobeHydrostatikSpielkonsoleInnerer PunktOvalTotal <Mathematik>SeidelCodeBitMultiplikationsoperatorKartesische KoordinatenInnerer PunktNichtlinearer OperatorSchlussregelVollständiger VerbandTotal <Mathematik>ComputerspielNeuroinformatikMailing-ListeFunktionale ProgrammierungMathematikArithmetisches MittelSystemaufrufObjekt <Kategorie>MereologieGrundsätze ordnungsmäßiger DatenverarbeitungRechter WinkelBetragsflächeTeilbarkeitSchreiben <Datenverarbeitung>SoftwareBefehl <Informatik>ParametersystemTVD-VerfahrenComputeranimation
StichprobeHydrostatikTotal <Mathematik>Physikalisches SystemInnerer PunktGenerizitätSpielkonsoleSoftwareentwicklerTwitter <Softwareplattform>Gewicht <Ausgleichsrechnung>E-MailFaktor <Algebra>MusterspracheSoftwaretestAusnahmebehandlungCodeLambda-KalkülRefactoringParametrische ErregungTemplateProgrammierspracheTypentheorieStrategisches SpielFunktionale ProgrammierungProgrammierungKomponententestMusterspracheParametersystemGewicht <Ausgleichsrechnung>Fächer <Mathematik>PunktMultiplikationsoperatorDelisches ProblemFokalpunktEntscheidungstheorieCASE <Informatik>Objekt <Kategorie>Total <Mathematik>SoftwareentwicklerZahlensystemDifferenteProjektive EbeneMixed RealityTVD-VerfahrenBoolesche AlgebraGanze ZahlInnerer PunktVollständiger VerbandMultiplikationC sharpZellularer AutomatVererbungshierarchieArithmetischer AusdruckGruppenoperationBitRechenwerkQuick-SortTeilbarkeit
Physikalisches SystemGenerizitätSoftwareentwicklerSpielkonsoleOvalStichprobeHydrostatikSCI <Informatik>Objekt <Kategorie>Datenbankp-BlockHalbleiterspeicherRechter WinkelParametersystemProgrammierungCodeMehrwertnetzEinfach zusammenhängender RaumSchreiben <Datenverarbeitung>AusnahmebehandlungMultiplikationsoperatorHilfesystemWhiteboardDatenverwaltungKonstruktor <Informatik>Arithmetischer AusdruckFunktionale ProgrammierungGrundsätze ordnungsmäßiger DatenverarbeitungDatensatzNichtlinearer OperatorKartesische KoordinatenFigurierte ZahlCASE <Informatik>LoopZahlenbereichSystemaufrufInhalt <Mathematik>Schreib-Lese-KopfNeuroinformatikEinfache GenauigkeitGeradeSpeicherbereinigungFunktion <Mathematik>Projektive EbeneClientMathematikDifferenteWort <Informatik>StichprobenumfangMusterspracheKlasse <Mathematik>ProgrammiergerätAbgeschlossene MengeWeb logProgrammfehlerMomentenproblemGrenzschichtablösungC sharpLambda-KalkülEigentliche AbbildungComputeranimation
StichprobeHydrostatikOvalSpielkonsoleSoftwareentwicklerFehlermeldungZurücksetzung <Transaktion>TransaktionGenerizitätPhysikalisches SystemAusdruck <Logik>p-BlockE-MailTwitter <Softwareplattform>Gewicht <Ausgleichsrechnung>Faktor <Algebra>MusterspracheAusnahmebehandlungSoftwaretestCodeNichtlinearer OperatorDifferenteZurücksetzung <Transaktion>FehlermeldungDateiformatAusnahmebehandlungObjekt <Kategorie>Klasse <Mathematik>Gruppenoperationp-BlockKartesische KoordinatenDienst <Informatik>MusterspracheHydrostatikTransaktionProgrammiergerätZweiAbgeschlossene MengeKonstruktor <Informatik>ParametersystemGeheimnisprinzipWort <Informatik>RandwertRechter WinkelSystemaufrufKontextbezogenes SystemTextbausteinZahlenbereichFigurierte ZahlMulti-Tier-ArchitekturLambda-KalkülFunktionale ProgrammierungKomponententestMailing-ListeGamecontrollerArithmetisches MittelKonditionszahlProgrammierumgebungGemeinsamer SpeicherArithmetischer AusdruckLokales MinimumProgrammierungCASE <Informatik>COMQuellcodeSpeicherabzugMathematische LogikGleitendes MittelHilfesystemFächer <Mathematik>Computeranimation
Physikalisches SystemGenerizitätSoftwareentwicklerSoftwaretestAusnahmebehandlungOvalFehlermeldungSchreiben <Datenverarbeitung>AusnahmebehandlungCodeErwartungswertp-BlockMultiplikationsoperatorNatürliche ZahlAttributierte GrammatikSoftwaretestFächer <Mathematik>AppletOvalRechenwerkTypentheorieNegative ZahlOrtsoperatorRechter WinkelKomponententestObjekt <Kategorie>SoftwareentwicklerInstantiierungLambda-KalkülGeradeCheat <Computerspiel>ImplementierungFramework <Informatik>BitTermKraftWort <Informatik>ForcingResultanteWeb SiteWasserdampftafelProzess <Informatik>CASE <Informatik>MinimumBasis <Mathematik>Arithmetischer AusdruckGewicht <Ausgleichsrechnung>System FQuaderKonfiguration <Informatik>Schreib-Lese-KopfComputeranimation
Physikalisches SystemGenerizitätSoftwareentwicklerSoftwaretestDatentypAusnahmebehandlungHydrostatikGruppenoperationMusterspracheGewicht <Ausgleichsrechnung>E-MailTwitter <Softwareplattform>Faktor <Algebra>SoftwaretestSchnittmengeHilfesystemExakte Sequenzp-BlockFramework <Informatik>CASE <Informatik>Rechter WinkelGeradeMultiplikationsoperatorAusnahmebehandlungMusterspracheTelekommunikationObjekt <Kategorie>Arithmetischer AusdruckMultiplikationTypentheorieGruppenoperationFokalpunktCodeMAPParametersystemMessage-PassingArithmetisches MittelComputerspielCompilerMereologieSummierbarkeitPunktCOMSchreib-Lese-KopfSystem FVererbungshierarchieClientDifferenteRechenwerkSampler <Musikinstrument>Endliche ModelltheorieComputeranimation
Transkript: Englisch(automatisch erzeugt)
Good morning. Welcome to the session on design patterns for .NET programmers. My name is Venkat Subramanyam. We're going to talk about a few patterns here. Let me quickly just say a few words about what we're going to do here. I'm not going to use any slides. I'm going to write some code, play with examples.
I hope the session will be interactive, so please do ask questions, make comments along the way. I'm going to talk about a few handful of patterns. Clearly, the first one is from the Ganga 4. The remaining ones really are not directly. Most of the patterns I'm going to talk about here are going to stem from lambda expressions,
so we'll talk a little bit about why they are and get into those details. Best time to ask a question or make a comment is when you have it, so please don't wait until the very end. Just about any time is a good time for questions and comments. If you do have a question or a comment, I'd definitely love to hear from you, but if you feel that I'm ignoring you, and that's because I'm blinded by these lights here,
I can't see you very clearly, wave at me, start speaking, draw my attention, and I'll immediately yield to you and get your question or comment. So let's get started. The first thing I want to start out by saying in this session is I really hate patterns.
There you go. So that was very motivating to start the session, isn't it? And I'll explain to you why I hate patterns. And the reason I hate patterns is any time somebody in the development team asks me the question, I wonder what pattern we could use. It's going to be a very long day. And the reason for that is because that's a very poor way to develop software, in my opinion.
Patterns are excellent tool for communication purpose, but I don't believe they are excellent tools for really innovating the design in a way. And the reason simply is the following. If you really think about how do we fit a system into a set of patterns,
that's incredibly hard to really do that and effectively reach a good solution. Now think about this for a minute. You know, hands down, grandma makes the best pie, right? And you know grandma makes the best pie, you've enjoyed it so much. And you're with your colleagues and you say so much about grandma's pie.
And everybody says, really, if your grandma's pie is so good, why don't you make it for us? And they tell you that they're going to show up at your place on Saturday and they want you to make grandma's pie. And you're nervous, obviously. So you call grandma and say, grandma, I want to make the pie you always made for me. I've told it to my friends and they all are coming over on Saturday.
I'm going to make this pie for them. And tell me, how do you make it? And grandma says, oh, that's simple. No, no, no, grandma, tell me exact steps to follow. And you have sat down with a paper and pencil and grandma says, take some flour. And you say, no, no, no, no, tell me how much. And then grandma says, add some sugar. You say, here you go again, grandma, tell me how much.
Two, two teaspoon or tablespoon. You are very meticulous in all the details. You write them down and then you tell grandma, let me go over this one more time. And then you say, grandma, what are the best practices, right? And then on Saturday morning, you wake up early and you make the pie. What happened? It was a disaster.
Last time I tried it, I couldn't get the spoon out. Something is telling me wrong, right? And with the burnt kitchen, I cover up everything, go to the store, buy something, make it up. Then I go to grandma and say, grandma, this was a disaster, right? What went wrong? And grandma says, oh, this is very simple. Let me show you how to make it. And you walk into grandma's kitchen and grandma takes two teaspoons of sugar, measures it accurately and puts it down, isn't it?
Not really. Grandma is putting some flour and takes sugar, puts it and then does that. You say, what was that? That was sugar. How much did you put? She says, enough.
And you're like, well, with all that recipe you gave me and I wrote down, what was that about? Well, kid, you asked for it. Well, unfortunately, design patterns are those recipes and the guys who wrote it are the grandmas and grandpas of our industry. And we try to take that recipe, try to cook with it and expect great results.
And we end up with disasters. No wonder that's happening. So I think that design patterns really are great tools for communication, but not to sit down and say, let me see which pattern I can fit into this code is a very poor approach in my opinion. Having said that, patterns are a great tool for communication and I want to know how we could effectively use it.
And we're going to take, of course, the first Ganga4 pattern, but then we will move on to other patterns that are very valuable and useful given today's developing environment and language itself. Like I said, I'm going to write quite a bit of code along the way and look at examples. So imagine for a minute, when I was programming in C++ years ago, design patterns concepts were not popular.
And I called the C Sharp language as the post pattern language. Right about 1994 time frame is when the design patterns book was created and about the
time Java was released and of course, .NET and C Sharp followed a few years later. Now, one of the aha moments I had with Java and C Sharp was, wow, I don't have to put that much effort to implement those patterns that used to be so hard to implement in C++. So one of them, for example, is abstract factory.
Let's talk about that for a minute. Imagine you're creating a web application and this web application is going to sell different products, you know, books, t-shirts, shoes, what have you. And when you have these products you want to sell, you may quickly notice that a lot of pages are doing redundant work with same type of logic.
You create an object, you set some variables, you set some parameters, and then you do some calls and methods on it. You can remove that much laborious code by simply using, for example, a bit of a pattern here baked into C Sharp already. Let's take a look at an example of a class called book. And I'll throw in another class later on and see how that's going to help.
A book has two properties here. It's got a title, it's got pages as two things here. And I want to run this. Of course, I have a two string method, just prints the details of this itself. I want to use it. So let's create a sample class right here. And what am I going to do in the sample class? I want to first of all create an instance and print it. So let's say create instance, which I don't have yet, and I'm going to create an instance of book.
And I want to send some properties to this. So I'm going to say over here, new dictionary. And the dictionary itself is going to contain a string and object as parameters. So let's say object. And what is this dictionary really going to contain?
Well, one of my favorite books title is going to be Who Moved My Cheese? And I'm going to specify for this the parameter, which is pages. And this is a very small, nice book, 94 pages long. When I run this code, obviously, I get an error that create instance is not available to us yet.
Let's fix that problem. I want to create the create instance method. So how do I create the create instance method? So let's go ahead and write the public static. And it's going to create an object for us. So here's my create instance. The first parameter is going to be the class name I passed to him. The second parameter is going to be the dictionary, which is going to take a string and object as parameters for us.
And we'll call this values. Let's take baby steps towards it. I'm going to simply return a null for now just to make sure things are not complaining anymore. So C Sharp compiler is happy at this point. It says, all right, you got enough code in place. What I want to return here is an instance that I'm going to create. Where do I get that instance from?
This is where the abstract factory really comes in. So I'm going to say type equals type dot get type, and I'm going to specify the type that I want to create. But that comes really from the class name provided to us. And then I'm going to say the instance I want to create over here is going to be simply the activator. This is one of my favorite classes in dot net API.
It is the factory baked into the framework already. And then we can leverage reflection and stuff like that after this to make use of it. So I'm going to say over here, create instance from this and the instance of the type I want to create at this point and return to it. So you can see that in this case, it created the book object for us. But I need to populate this object.
Like in a web application, we probably get the data from a web form, and this is probably in a collection already, and might as well populate it from a collection off the bat. So we could say for each at this point, and we could say, for example, an entry within the values entry. And I'm going to simply say type dot, in this case, I'm going to get the property of the type.
And the property I'm interested in is the entry's key value. And then I'm going to ask him to set the value on the property on the instance we just created. And the value that I want to create is the entry's value itself I want to set because it could take multiple values index based.
I'm going to set the null on it, and we can see that it's able to create the instance for us very dynamically. But the real benefit of such an approach is we can reuse this code quite a bit with putting some conventions over configuration very easily. So, for example, if I were to go back over here and create, let's say, a CD class, and a CD has a
title, maybe I have a volume for a CD rather than pages, and I'm going to specify the volume for this particular CD. I want to create an object of a CD and use it, but we can see how easy it is to create this now. Let me just copy this over here without any change to the rest of the code. We can ask him to create a CD object, and the title could be whatever the title is, let's say Nature over here.
And, of course, the volume is going to be, let's say, the second volume of the CD I want to create. And we can see how easy it is to create some of these. Hey, it's a book because I forgot to change this guy here. So that's going to be the CD that it creates for us. You can see the abstract factory.
Again, a very simple factory, but nevertheless, it really doesn't require the hierarchy of classes to be created like in C++. C-Sharp really makes the life a lot easier, the dotted API makes the life a lot easier, and we can do that. Now, I want to move away from the Gang of Four patterns. There are several other patterns we could implement, but I'm going to focus on something a little different.
And one of them is my favorite called the Cascade pattern. I'm going to show you three different ways to write some piece of code and see how we could benefit from this particular pattern. So let's start with something we are all familiar with. Let's say we have a class called a Mailer. And the Mailer contains a method, maybe it's called to, which takes an address as a parameter.
And I'm going to simply say here, you know, to, to say that it's just printing out the to, to know that we called it. And I have a bunch of other methods. I have a from method. I also have a subject method and maybe one more method, which is the body method. And the body method is going to take a message as a parameter.
Let's call this body. The subject is going to take a subject as a parameter. Let's call it subject so we can, we can know it's called. And the from takes an address, let's say from over here. So we know it's been called. Now it's time for us to call this particular code. So how would I call this code? So to call this code, I'm going to write a little example.
So Mailer equals new Mailer and Mailer dot, let's say to, and it's going to send me a favorite email. It's going to send me a mail to my address. And I want it to send from where? So it's going to be a from my favorite email sender on my system, which is my build machine. So I'm going to say over here, this is the build at agile developer.com.
So let's say builder. And of course it's the message that from subject and a subject is going to say your build is broken. Sorry. And it tells me Mailer dot to our body rather. And the body is going to be your code sucks, right?
And Mailer dot send is the final message to send the email itself. So I'm going to say public void send, and I'm going to simply say here, just send. So we're all familiar with code like this, right? Everybody has written code like this, right?
You all did. How do you feel after writing that code? Dirty, right? You feel dirty. There was nothing exciting about this because you created a Mailer and then you said Mailer, Mailer, Mailer so many times. And that is kind of boring. You would never do this to a human being, right? I mean, they would get angry at you if you're redundant like this.
So that's one problem. The second problem here is what do you do at the very end of this code? Do you reuse the Mailer for another email or you're not supposed to reuse it? How do you know the answer? And somebody says, go read the documentation. We all know how documentations look, right?
Then eventually start debugging the code, trying to figure out what's going on, right? So there's got to be better ways to write this code. This is where an interesting pattern comes in called the cascade pattern. I'm sure you've seen this pattern in other libraries and APIs, but we can use this for our own library very effectively.
So here's what I'm going to do to use the cascade pattern so we can remove some of the concerns that we have here. So notice what I'm going to do. I'm going to first of all ask each of these methods to simply return a Mailer. So a very small change in the code, right? Each of them are returning a Mailer. And as a result, each of these methods now have to return the current object itself when we are done with what they are doing.
So I have only made the change. The code still works. We haven't removed the redundancy yet. But then I'm going to go back to this code and say I no longer need to create a definition of the object there. I can now remove these Mailers from here.
And I can also remove the semicolons from here. And as a result, the code can just follow along. And I can use a cascade pattern to remove that kind of verbosity in code. So this is something we could do by just chaining the object command together. And this becomes more of a fluent interface as we write code.
Like I promised, most of the patterns I'm going to use in this session are going to use Lambda expressions one form or the other. So I'll take this further into a Lambda expression very quickly to address the second point we talked about. And the second point really is I don't know what I can do with the Mailer after the Mailer was sent. Can I continue to call methods on it and reuse it?
That's not very clear from this example. Let's make it very explicit and say no you cannot. Once you send the email, you are really done. In other words, you prepare the email and then we'll take care of the send. So the pattern I'm going to use is going to take three different flavors and I'm going to show you the first flavor here. So I'm going to make this a static method for a second.
And the send method is going to accept as a parameter an action which is going to take a Mailer as a parameter. We'll just say action for now. And this is going to still do the send at the very end. But in the beginning, it's going to create a Mailer object and then it's going to say new Mailer and send the Mailer to the action itself right there.
So now you can notice what I can do is I can simply say here, rather than creating a new Mailer, I say mailer.send. And this takes a Mailer as a parameter and now I have the Lambda expression in place that I'm going to call in. Now one of the limitations of Lambda expression says you can only place a very small piece of code within the Lambda expression.
But it turns out that's really not a big limitation. You can get around it fairly easily in your code. So what I'm going to do here is simply put a little code block, if you will, right here and drop this code within the code block without the send itself.
So I'm going to go ahead and actually in this part of me, in this case, I only have one piece of code. So I really don't need to do that. I'm jumping ahead. So basically, in this case, I'm going to simply say mailer dot and I'm going to simply call that piece of code right there without the send, of course. And you can see that code still works. So in this case, what I have done is used a Lambda expression to wrap it up. So you say mailer dot send, clearly indicating that is your objective is to really send the email.
And then once you send the Mailer, the rest of the code is just to configure the Mailer itself. And then you are saying in this case, I want to configure the to, I want to configure the from, the subject, and the body. And once you have configured these things, when you return from this block of code,
the send will happen back here and it takes care of the sending the message itself. So this is an example of how we are mixing the cascade pattern, but you're also mixing the Lambda expressions with it. And so this gives you an opportunity to create APIs that are a little bit easier to work with and use as well. And that is basically your cascade method pattern and how you can make use of it.
I'm a huge fan of Lambda expressions. Lambda expressions themselves come from functional programming concept. And basically think of a Lambda expression as an anonymous code block, a little code block that you can create and pass it around. And the anonymous code blocks can be passed around into a certain context and then you can execute it within that particular context very easily.
That becomes a lot more easier to work with. But given the Lambda expression, I'm going to show you two other patterns here and an example of how we could use it with a certain model that you can really benefit in writing code. And as a result, rather than having to create classes and anonymous classes or nested classes, this really removes a lot
of burden from your shoulders and really eliminates the need for writing a lot of code and makes it much more easier. The other advantage of this is it also streamlines the code to a certain extent, as I'll show you in a later example as we look at it. Let's talk about another thing here, which is a pluggable behavior and how we could benefit from that.
So imagine for a minute, let's think about an example of where a pluggable behavior could be used. Imagine you're working in an application that's dealing with stock prices and you have to do some arithmetic operations on these stock prices. And it's a very late Friday evening, you really want to go home, you have to really go for an important meeting or a party that you want to go to.
So you really have the urge to leave work. I know how Friday evenings are, right? So you want to really leave. And your colleague says, I want one function from you. And the function the colleague wants is this. Given a list of prices, let's say we'll call it prices, and the colleague says, here are the prices as an example.
I have a new int of prices, let's say one, two, three, four, five and six, and they want you to total the prices. So total prices and the price values are given. So you decide that shouldn't be hard to develop. So you write the code for it.
So here is the code for it. You basically say public static and what is the price you're going to create? So int total prices and takes prices as a parameter, there we go. And we could simply say total equals zero, you want to return the total when you are done. And of course, in this code, you want to go through and total the values.
So we could say for int price in prices, so we'll say total for each price in prices. And let's simply say total plus equals price. So that was very easy, right? So you got this coded, and you really are in a hurry, you want to leave, but your colleague,
the nagging colleague I'm talking about, you know who I'm talking about, right, wants you to write one more function. And your colleague says, I want to total not just the prices, I want to total the even values in the prices. And you say, really, you mean function like this, and they say, yes, that's what I want from you.
And you are in a rush, you look at your watch, it's getting late, you stare at this function. This function is doing almost everything you want, but what you want is slightly different. And you think through this and you tell yourself, there's a reason God created copy and paste, right? So you say total even prices.
A little bit of trouble in your heart, right? Something tells you this was not a good thing. But you look at your watch and you forget all of that. And then you say, if price is even, see, that was not that hard at all, right? And you get that to work.
And you really want to leave, but your colleague knocks on your door one more time and says, please, one favor before you leave, I want total odd prices also. And you look at this code one more time and you know, copying and pasting one more time is an act of criminal negligence.
You know it's morally wrong to do, but such rules don't apply on a Friday night, does it? So you just copy and paste it one more time. You change it to odd prices and the code still works. And what are you going to do here in this code? Oh, this is going to be if, did I change the wrong one?
Sorry. So this is going to be, did I do the right thing? This is the even one, right? Yeah, thank you. So right there, let's fix that. So I'm going to fix this code. So what do I do here? I'm going to say this is going to be the total odd prices and that is going to say if it is not equal to, and you got that to work, and then you leave home and you have a broken heart because you know this is not the way to write software.
This is wrong, morally wrong. So you pull over by a coffee shop, you really want to refactor it, but something really terrible happens. Your friends show up and they ask you what you're doing. Before you could answer, they take a peek at your computer and they realize you do copy and pasting for a living.
How does, how do they feel about it? They just found out your dark side. They leave. They don't return your phone calls anymore. They don't hang out with you for drinks anymore. How life changes, right? Let's put an end to it. We should never make this happen.
No. So let's remove all of this. Before that, one small change. If true, now notice how similar the code pieces are. All the three functions are exactly the same except for what's within this parentheses in the if statement, right?
So that is a candidate for extraction. So we can simply extract that out. That is the pluggable behavior we can remove from this code. So what I'm going to do is to go back to this total prices. Let's remove this part from the code, all these guys, and this time we will use a special object. We'll come back to these two variations in a minute.
And we can simply say here, comma, a func object. This is going to take an integer as a first parameter and a Boolean as a second parameter. And it says selector as a parameter. The way the func object works is it can take multiple template parameters, parametric types.
The last one is special. The last one defines the return type of the function that you are implementing. And the other types that you specify are the parameters to it. So you could say func int comma, double comma, char comma, bool. Then this function takes three parameters and then it returns a bool at the very end.
That is the last one it says. So in this case, I'm going to say if the selector was happy with the price, then I'm going to accept it into the totaling, otherwise I'm going to just ignore it. So what we are doing in this case is we are externalizing that decision into a separate selector. Now in this particular case, I want to pass to him a func object.
Again, you can use a lambda expression notation and you can say price, but I want to total all the prices. So I simply return a true and you can see it printed at 21, which is the total of all the prices in this particular case. So you can see how we simply extracted this behavior out and we are simply passing the selector object.
Now your nagging colleague comes to you and says, hey, I want to total even prices also. You say, don't worry, you can do it yourself. Just simply write the code like this example. You can provide a little unit test for them to work with if they want to. And you can see how it's really totaling the even prices now.
This could be the odd prices in the mix and you can see how that's easy to write. And you can also write one more function where it could say if the price is greater than, let's say, three, then accept for the totaling, otherwise ignore it. And you can see that it's only totaling the four, five and six in this particular case.
And you can see how you could make use of this in your code as well. And this is called the pluggable behavior. Of course, you may notice this and say, wait a second, this does sound a lot like another pattern in the gang of four as well. Remember what that is. That's right, the strategy pattern. But the difference is without lambda expressions, if you want to use a strategy pattern, you would have a corporate design meeting.
Whereas this is so trivial to get done with the lambda expression, you don't have to think twice. You can just quickly write the code and refactor it. So that is exactly what you're doing is the strategy pattern or the pluggable behavior becomes a lot easier to use thanks to the lambda expressions that we have. So that is an example of how you could use a pluggable behavior in the code and benefit from it.
Like I mentioned earlier, I'm a huge fan of lambda expressions. But to be kind of fair about this, I've used this even before lambda expressions were introduced in C Sharp. And the way that I used them was because C Sharp has anonymous delegates. And you could achieve some of these with anonymous delegates as well.
So lambda expression just makes it a lot easier to use them. But anonymous delegates really are helping as well quite a bit. So the point I'm really making in this case, really in this regard is when we learn different languages, we not. So a lot of times when people focus on different languages, they focus on the syntax of these languages.
And I think that's really a wrong way to look at it. And I can't tell you how many times I meet developers who look at a language and they say, oh, gosh, I can't handle the syntax of that language. But that's a very wrong thing to say. What is really important is the idioms the languages provide. And learning these idioms in languages is extremely helpful.
So one of the things I do is I continue to learn different languages. I can comfortably program probably in about nine or 10 different programming languages. And I take pride in doing that because it really is not for reason that I want to go program in these other languages. But really, when I come back to program in the main language as a program in for
my work, I really end up programming very differently with these languages than I used to do before. To give you an example of this, I was programming in Ruby for a while and I was back on a .NET project, a C Sharp project. And when I had to program in C Sharp, one of the colleagues I was working with wanted to implement a certain way to clear up resources on an object.
And as we were pairing up and programming this particular construct, I wrote a piece of code in a certain way using anonymous delegates. And honestly, I have not written an example or a code like that until that day. But it just spawned on me saying, hey, you know what, I could really implement it this way because I know how lambda expressions work in Ruby.
And I kind of related back to saying, oh, here is anonymous delegates and how they work in C Sharp. And I wrote a piece of code. Literally, this guy stands up in the middle of the room and says, wow, I've never seen code like that in C Sharp before. And I said, absolutely, I've never written code like that in C Sharp before either. But this is just a way to experiment a certain idea and really got a handle of how this could work.
And we were able to implement this quite effectively in the code. To give you an example of this, let's talk about where another pattern from the small talk days kind of is very helpful now that we have anonymous delegates in C Sharp and how we can use that. And also how you can use that with lambda expressions in a more elegant way.
So to give you an example of this, let's just imagine for a minute there's a class called resource. And the class resource basically has a constructor, let's say. So here is the resource constructor. And I'm going to simply say here that created over here, that's all I'm going to say. I'm going to write a function here for us to use.
Let's call it as operation one. And the function simply is going to say, let's say, op one. And I have another function in this class, let's say, op two, which is just another operation I want to perform. And of course, at the very end, I have the finalized method called the resource. And here I'm going to simply say, you know, cleaned up.
So that's my code so far. I want to use this in my code. Let's create a sample code to use it. So let's say, in this case, I create a resource object equals, let's say, resource equals new resource. And I'm going to call the op one method. And then I'm going to call the op two method over here in this particular call.
So very simple, run it. And naively, when you look at it, you would say, yeah, that's, oh typo, what happened? Oh, op two. Thank you. Appreciate it. So you could call any number of methods over here. And so you can see how this is going to be called.
But at the first look, it appears like things work properly. But unfortunately, this is not true. For example, if I were to put a loop over here, let's say equal to zero, and I call this 10 times over here, and you can see that within this code, at the very end of this, I'm going to say done over here.
And you can see clearly in this case that all the code loop was running. Let's make it only three times so we can see more output here. So you can see all the cleanup is happening at the very end. And that itself is not guaranteed, right? You don't know when this cleanup is going to call. In other words, depending on finalize is a bad idea.
This brings back memories about a project I was on. The client calls me and says, hey, can you do me a favor? Can you review the code? And while you're reviewing it, maybe you can help us fix a bug that's been haunting us for a while now. So I asked the manager, what is the bug? And he said, go talk to the programmers.
So I went to the programmers, and the programmers, you know, explain it the best always, right? So I went to the programmer and I said, what's the bug? And the programmer said, well, the program works fine most of the time. I said, that's good. Tell me when it doesn't work. He says it doesn't work between 7.15 and 9 in the morning. I'm like, awesome. Every day, he said, only Monday through Friday.
It works fine on Saturday and Sunday. I'm like, wow, I'm intrigued. I'm curious. How is that going to be not working? Well, as it turns out, this was a portal application in a company with 100,000 employees. You kind of figure out what happens between 7.15 and 9 in the morning. Employees come in, log in, they hit the portal. And heavy contention in the morning, then the rest of the day, OK, something related to resources.
I kind of put that in the back of my mind. And I was digging through the code, scratching my head while this gentleman was sitting there quietly browsing through his computer and eating pizza, which is kind of nice, right? So I was sitting there trying to figure things out. And I just wrote a single line of code, hit the compile, and ran the example.
Immediately, he drops his pizza and says, that worked. I'm like, excuse me? He says, that change you made worked. I'm like, how did you know? Well, I'm profiling your code as you're running it, so I didn't know he was actually working. I thought he was happily eating pizza, right? But I'm profiling your code as you're running it. And when you made that one change, it made a ton of difference.
What did I do? Well, all that I did was, in the code, I really had a method I wrote, let's call it public void close. And in the close method, I cleaned up the resource, right? So in other words, what really happened in this case, let's call it cleaned up. So actually, what happened on this particular project was that they had been using a particular resource object.
And in the constructor, they were opening a connection to a database. And in the finalize method, they were closing the connection to the database. You know where this is going. So the code was creating tons of objects.
And the CLR said, hmm, I got a lot of memory. I don't need to call the garbage collector. So it was happy. But all the objects being created on the other side were choking up on the database side. It was throwing exceptions because it couldn't open up any more database connections. Unfortunately, the exception was not being propagated properly. So they had no clue what was happening.
So what I did in that particular example was simply I went over to the code and I simply said, you know, something like resource dot close to clean it up. That's all I did. And now when I ran the code, it had shown an improvement. But of course, we in this room know this solution is only a first step.
It's not adequate. What's wrong with this code? What's missing still? That's correct. Somebody whispered in my ears. Say it louder. Exception, I heard, right? You said exception is not being handled. So you're absolutely right. If op1 or op2 does not throw the exception, does throw the exception, the close will never get called.
So one way to solve the problem, and I heard your word using, I'll come back to that in a minute. One way to solve the problem is to use a try block and put this code within this block. Then you put a finally block and within the finally block you call the close.
This would be a better way to write the code. And of course, we know this is verbose. This is all cumbersome. Who wants to write code like that? Nobody wants to do that, right? And most of the time, I will forget to do this. That's painful. And then I'm going to have a bug in the code. I've got to go back and fix it. I'm saying, I'm sorry, I forgot it.
What a nasty way to do it. Of course, C Sharp has a way to solve this problem. As he rightly pointed out, it is the using. And I hate using. And I'll tell you why I hate using in a minute. But what does using do? Using simply says, rather than doing that, simply say using resource. And what using says is just call that within that block.
And whatever code you want to run, run it within this block of code. Now you no longer have to go through that mess. Now at the very end of this call, we will call the close method for you automatically. In other words, what using does is exactly what we did a minute ago. It puts the code in a try block
and calls the close within a finally block. Of course, if I have to use this, this code will not work at the moment because I have not done the idisposable. So now I have to implement idisposable. And once I put idisposable, then I have to come and write the dispose method.
Let's actually write a dispose method separately. So public void dispose. And within the dispose method, I'm going to call the close method I wrote. But then you say gc.suppress finalize this because you no longer want the finalization to run after this.
So you have to do that. But look at that for a minute and you say, wow, you know what, if you want to really implement the idisposable correctly, you have to read some books or blog entries and scratch your head for a few minutes. Things should not be that difficult to do. And besides that, there is still one problem with this code, right?
If I still forget to call using, what's going to happen? So I still don't have a call to using in this code. So you're back to square one, isn't it? All the cleanup only happens later on. There's no immediate garbage collection when the object is no longer needed.
And that's the reason I don't like using. Using is okay if you remember to put it. Now here's my argument. If this is the way code should be written, why not make sure the programmer has only one way to write the code? I'm all for that, right? Don't blame the programmers and say, oh, you should follow this pattern.
Hey, that's the only way you can write the code. There's no other way. Then there's no messing up. So how do we do that? Here's an idea. Let me show you how we could do this with some lambda expressions on hand. So get rid of all of that for a second. And what I want to do is, first of all, I'm going to say, resource, resource equals new resource.
And I simply call resource dot close for a minute. Now, obviously, this is not a good way to write the code because, first, I have to remember to call close. Second, I have to remember to call the using. Neither one of them I like. So here's an idea. Let's go back to the code.
Get rid of all this dispose stuff for a minute. Keep it if you are interested. But it is not needed for our example purpose right now. And I'm going to go back and remove everything we don't need from this class. So it's bare minimal, as you can see. I'm going to make the close method private. There you go. So now we have an error saying you cannot call the close.
Of course, a brilliant programmer says, okay, no worries. I'm not going to call close. Then you make the next step. You walk up to the class and say, tell you what, I'm going to make the constructor private. Now you're really asking the programmer to think. And the programmer's like, damn, how do I use this class now?
Right? I cannot dispose it, okay? But I can't even create this. There's got to be some logic. How do I use this? So here comes the trick now. So now that you cannot create the object, you cannot dispose it, here's an idea. Let's create a static method called use. And the static method use takes an action
with the resource as a parameter. And let's call it as the block, for lack of better words. And within this method, I'm going to create a resource object, resource. And in this case, I create the resource for you. And I'm going to open up a try and finally block myself. And within the finally block, I call the close method,
which I can because I'm within the class and the encapsulation boundary doesn't stop me, isn't it? So I've created this object within this block. And now I'm going to call within the try safety haven, I'm going to call this method on the block itself and send the resource over to him.
So that is my code right now. So now, as a user of the class, what am I going to do? I'm going to simply say resource.use and I'm going to get a resource block as an object over here. And I'm going to simply say resource.op1 and simply call that one method right there and use that within the context of that object.
So notice now, you're not really blaming the programmer saying you better learn how to use this API. You're providing the one way which is the right way to use this. So that becomes very natural for us to be able to use this API without having to struggle through the API and figure out how to use it rightly.
You don't have to impose the using on them and say, you better start using all these places. Forget about all of that. That's the only way you can use this code. You can kind of streamline this very nicely. But of course, I don't want to call this one method. I want to call multiple methods on it. So this is where I can simply put a block of code and say resource.op1 and resource.op2 over here
and I can call these bunch of methods over here and you can call any number of methods as well within the anonymous code block. This is one of my most favorite patterns is called the execute around method pattern. And the reason why this is called execute around method pattern, this is a pattern from the small talk days,
is that it gives you a handle to do some stuff, but it does some work around it. So before it enters your block of code, in this particular example, the block of code that you have is within this use method itself, so a parameter list itself. So before it enters this block of code,
it does some work. In this particular example, it does the pre-initialization of the object for you. That is the before action. Then it yields control to you and says, if you do whatever you want to do, I don't care. And when you return from it dead or alive, meaning exception or not, I'm going to go back and do the cleanup operation in this case, or whatever operation I want to perform,
it takes it up performing that operation for you. I've used this pattern in a couple of different formats. One I've used it is to do instantaneous cleanup of resources where I have external resources, heavy resources, and I want to make sure I dispose them off really quickly. I've used this pattern. Another place where I've used this very effectively is we have, let's say, for example, an application
where I have to run all the piece of code within a transaction in a service tier. And a very ugly code ensues very quickly where you create a transaction, do your stuff, and then you have to check whether the transaction to be committed or aborted, and then that boilerplate code begins to smell really bad
as you copy and paste it in each of the service tier layers. Imagine how beautifully you can implement this without having to worry about all of this. So I'm going to change this to transaction for a minute, and then I'm going to basically go back over here and write a function called run in transaction.
And this is going to be a transaction object. And now I'm going to say, here's a block of code, and within this code, I'm going to simply say create transaction, and then I can say block and send him a transaction object. So this would be transaction object.
So this could be a transaction, this could be a new transaction, right, with all the other details relevant to your application logic. And then you could run this within a try block, and then you could say, finally, when you're at the very end of this, you could say, you know, check errors
and commit our rollback. And you could even set a little parameter in the transaction object, saying rollback only, or go ahead and commit, or it could be other environments that tell you what to do based on your context, right? So if you imagine this for a minute, how does this help, really?
The way this is going to help is you can easily create a transaction object, as you can see here right now. Let's make sure I have the code written properly here. There we go, thank you, transaction. Thank you very much. So now I'm going to make sure I can use this,
I can say transaction.runInTransaction, and I'm getting a transaction object, let's call it transaction as an object, as a parameter, and now I can run whatever code I want to run within this block of code very easily, right? So we could simply say, here is my block of code, and I'll just say running, you know, whatever I want to run,
I could run within that. So you create the transaction, you're running your stuff within a transaction, and when you finish it, the code can then smell your transaction, and it can either abort it or commit it, depending on certain conditions. And then now you have streamlined the code, you don't have to do the boiler-plated code everywhere in the code.
So we have used this pretty effectively in applications. And at first sight, programmers were like, well, that's kind of unintuitive, but once they get a hang of it, within a week they say, wow, that's removed so much burden from us, we don't have to sit there and write that stupid code and copy and paste it, it becomes a lot easier to work with. So again, an example of using the lambda expressions
to really work with it, as you can see here. So this sometimes is also called as a loan pattern in some context. And the reason why it's called as a loan pattern is an object is created, and it's on loan to you within that context, and when you're done with it,
you're yielding back and then returning what you got on loan, and then you're just giving it back to the object, and that's an example of a loan pattern and how that can be very effective. Where could we use some of this in applications? I'll share with you an example of how this could be used in one other case, which is in unit testing for exceptions.
Let's talk about unit testing for a minute, and then we'll talk about some of the ways we could use this. I'm a huge fan of unit testing, and there are three types of unit tests, maybe fourth type. So there are positive, negative, and exception tests that I normally create, the fourth one being a performance test. But thinking about positive exception and negative test,
a positive test is to say if the code is doing the right thing. A negative test is to ask the code to do something it's not supposed to do and see how the code responds to it. An exception test is to make sure the code is throwing the right kind of exceptions. Now, let's talk about exception tests for a minute. NUnit, for example, was really promoted by
developments on JUnit, which is the Java implementation of XUnit framework that Ken Beck started with. And in terms of JUnit, JUnit really had a way to handle exceptions. So if you are writing an exception test, I'm going to just show you pseudocode here. So in terms of JUnit, you would say something like NUnit,
you would say test and public void some exception. And maybe we will ask an exception on an object. We'll say gorilla. So gorilla, and I'm going to have a gorilla object, and equals new gorilla. And I'm going to feed the gorilla with something.
So gorilla dot, let's say feed, I'm going to give it some bananas. So it's going to, then I'm going to call a gorilla dot gorilla dot provoke. So not something you want to do on a daily basis, right? So provoke the gorilla. And this is going to be, this is a C sharp gorilla,
so it's an F's uppercase, okay? So the way you would write code in JUnit style would be to put a try block over here, and then you would call into a fail method. You would say fail, and then you would say expected gorilla to throw an exception, really.
And then of course, you would say catch, and whatever exception, right, whatever. And you would then say, if you got an exception, I normally like to put a smiley, right? Because empty catch blocks are evil. Or if you don't want to do that,
you could say assert dot is true, and then true to say that you really have an exception block that you're expecting. But as you can see in this particular code, it's very verbose in nature. So as time went on, NUnit created something. In dot net, we have attributes way before Java created annotations,
and so we could use attributes pretty effectively. So JUnit said, here's an idea for you. I'm going to create a test, an expected exception type of, and you can say whatever exception. And then you could say public void some exception to,
and you could simply say, in this case, gorilla equals new gorilla. You're creating the object. And actually, a spelling mistake here, but that's fine. So essentially, we are creating an object of this right here, right? So here's the gorilla object.
We create an instance of it, and then I'm creating another object of that, and I'm asking him to instantiate it. Then I could say gorilla dot feed, and once again, I could then say gorilla dot provoke. So in this case, it's much more concise,
and this was kind of a discovery on the NUnit side. And as soon as the NUnit created this, and as soon as Java introduced annotations, JUnit could not wait. JUnit rushed over and said, ah, we could do the same thing with annotations. So you put an add symbol, expected exception,
and you did that. And I was sitting there shaking my head, you know, this is terrible. I really hate this both in JUnit and NUnit, and it turns out now JUnit, still you can do this, but NUnit deprecated this. And the reason why this is a terrible idea is, as much as the above is verbose,
there is one thing nice about the first thing. It is very clear which line of code is going to throw that exception. The problem with the second example is, it appears to be concise, but I would argue this is terse and not really concise. I blogged about this a while ago,
about being terse versus concise. I want my code to be concise, but I don't want my code to be terse. And the way I define between these two is, a concise code is elegant and does what it's supposed to do. A terse code hides details from you. And as a result, it kind of cheats you a little bit
and makes you error quite easily. So as a result, I would argue, this code is on the side of terse-ness versus being elegant and concise. And the reason is, this code will pass if the provoke method throws an exception called the whatever exception.
Unfortunately, this code will also pass if this code will throw, feed will throw the exception also. And I can tell you how many times I work with teams, and almost every time I mention this, they will say, oh, we got exceptions like this all over the code, we got to go fix it now. Because I never want my unit test to fail for the,
I'm sorry, I never want my unit test to pass for the wrong reasons. And here is an example where a unit test potentially could be passing for the wrong reason. And even if it's going to hide a little bit, maybe hours, I want to eliminate that. So in this particular case, the feed method may be throwing an exception, but we don't have a way to know that the test passed because feed threw the exception
and not the provoke method. And I want to eliminate that possibility. So that is why I still prefer using the top approach rather than the bottom approach even in the end unit. But that recommendation changes. With lambda expressions, we don't have to do that. For example, what I could do is,
I could write a test helper, for example, where a test helper says, I'm going to create a should throw method. So let me actually do this. Let's keep this set of tests for a minute so we can come back and see how we can use that. So I'm going to have a test helper. And the test helper says, I have a method called should throw.
And the should throw method is a generic method. It accepts an exception type as a parameter and accepts a block of code that I want to execute and also expects a method, a message, that is for the exception details itself. And then it says where the exception type is an exception object that I expect to be thrown.
So now in the method I say, within the try block, I'm going to call the action. If the method I call doesn't throw any exception, I'm going to do an assert failure on that exception, meaning I expected an exception, it was not thrown, so I can give a message value for it. If it did throw the expected exception, life is good,
I'm going to just return the exception object to you so you can do something with it. And of course C-Sharp compiler is going to complain to you right now saying, well, after this you need to return something, so I just return a null, hoping that you will never get to this part in the code, right? Because either you get the exception and you return from here, or you blow up on the fail,
so you should never come here. This is just to please the compiler. But how would we use this? The way I would use this now is, I'm going to write the next method, which is the sum exception three method, and notice in this case, I would create the gorilla, I would call the feed method, but then I would simply say, a test helper dot should throw,
and it's going to be whatever exception I expect, and then here is the block of code I'm going to pass to him, and in this case I'm going to say, gorilla.provoke in this particular case, that I'm calling, comma, and then I want to say, you know, expected, whatever exception is, and then I can close that right there.
So that would be a very concise way, please. Right. So the reason I want to, you're absolutely right, but the reason I want to mention this is, when you mix multiple testing frameworks, just because the framework doesn't give you a direct support, you don't have to leave it out.
I've worked with multiple clients, not all of them use nUnit, even though that's my favorite, a lot of them don't, and usually it's like, hey, here's a way to approach it, absolutely, right? So that is the reason you can easily implement that with whatever tool you're using, it doesn't matter what your testing framework is, you can really write a very concise,
so absolutely, thanks for pointing that, but it's important to use, make use of that, and make it very concise, and again, the reason I really enjoy this is, it really brings the focus back to that specific, it's all about level of granularity, and you can bring it down to the level of granularity and say, I want that line of code to be doing this,
and not any other lines of code in the test, and so this becomes very concise, rather than being terse and misleading to us. And so, again, as a matter of point, if you're using the second approach to be writing test cases, it's time to move on to using a lot more concise way also, because that's a lot better.
So to summarize what we talked about here, I look at patterns as a great way to communicate rather than design, so I don't really sit down and say, I wonder what pattern I could use at work today when I go to work. If you ever get up in the morning, and if you think in your head, I wonder what pattern I can use today,
just call in sick, don't go to work, right? It's not worth that day. So it's more of a communication tool rather than anything else, and when you are collaborating with your team, you could say, hey, how do you implement it? Oh, I'm using an executor-on method pattern here. That becomes a nice way to communicate. And as we saw here, Lambda expressions are an extremely powerful tool
to apply in multiple different patterns. I really hope you found that useful. I appreciate your time. Thank you.