Programming magic is one of the reasons I LOVE this field. It's a beautiful thing when a library or piece of software works in a magical way and makes the other developers think about how it was achieved. Puzzles everywhere!
It is amazing how, by using the right patterns and techniques, we can hide the complexity of the domain and show only the "intention revealing expression" (I loved this sentence) to the consumer. Thanks for sharing!
I have to say this pattern was one of the most complex for me to understand and I'm still struggling in knowing when and how to develop it in my own in the correct way. This a good example,complex though.. thanks for sharing
If I came up with a simpler example, then there would possibly be no justification to apply the Builder in the first place. I might also make a video that demonstrates how exactly it comes that we don't need a Builder in simpler cases.
Thank you for the great video! What if we need to guide the users of the builder on which method they can use next? For instance if builder has mandatory steps or one steps can be called only after others? I guess we can introduce different interfaces and return them after each operation. Maybe you can share what could be the better way of implementing such complex builder?
That is a good question, indeed! You may try this article I wrote a few years ago: codinghelmet.com/articles/advances-in-applying-the-builder-design-pattern Not everything in that article would I back today, but it should be about the topic you have mentioned here.
I'm always using a similar builder design for specifying readable test cases. Especially when non-technical people specify cases with IntelliSense auto-complete, it's important that the compiler forces them to use the builder correctly.
Hi Zoran, I've noticed that Pluralsight have marked some of your Tactical Design Patterns courses and your "Making Your C# 6 Code More Object-oriented" course as retired. Would you still recommend watching these courses to somebody who have not gone through these before?
I still recommend those courses until I publish a new version. The problem with older courses is in the use of obsolete syntax, but on the positive side the themes gave not changed, and the way we address the problems is still the same.
This is a very specific builder - it is part of the domain model and it naturally contains domain logic. However, if more requirements came, asking for variation, then the Builder would probably have to receive appropriate strategies or something of that sort.
This is a great example for encapsulating the extremely complex logic to make life easier for a caller. I have a question for this solution though. How would you unit test this? For example, AppendFee, AppendInterest and GetInterest for are complex enough on their own to have unit tests for each of them separately. But since they're private, you can't reach them from the tests (unless you make them internal and make internals visible in test project or public, but that brings up some red flags in design, so I don't think it's a good approach). You can test the builder as a whole, but since you call AppendFee(AppendInterest(deliquency)) in Build method, it would not really be trivial to know why some test failed - is it the fee calculation that's wrong or interest calculation? Or maybe both? Or maybe neither? Thanks in advance.
I would focus testing on the attributes of the product. Builder normally has more than one responsibility, if we assume that each component it manages is a separate responsibility. Each component naturally requires unit testing, and so I envision it would be useful to focus on one component per test and keep all the rest at bare minimum. Then assert the qualities, the consequences caused by that component in the product. I think that adding variation to the builder for the sake of testing would be overengineering. The whole purpose of the builder is to be the single point where the rules of composition are defined, so leave it be that way, that is my opinion.
@@zoran-horvat So, if I understood correctly, in the example of AppendFee(AppendInterest(deliquency)), you'd construct an object via the builder in which the AppendInterest(deliquency) would not change the deliquency at all (or as little as possible), but in that object there's a significant change done by AppendFee method for the result of previous operation and test the result? And another test would be vice versa - interest would "change the result a lot", while fee would make minimal or no difference to the final result?
@@m5s10 You can skip any method that is not mandatory and use simplest possible arguments in those that must be called. Then carefully call the method you are interested in, and assert that the result of that call has caused the expected change in the product.
Yes, of course. It would test all invariants and postconditions from the specification of requests. Since none of the requirements has any variation, there is nothing to inject into the Builder, which makes testing easier.
Seems the builder is capturing domain concepts that belong to the invoice. The invoice itself is anaemic. How about capturing rules in the model itself and then a printer decoration object knows how to produce the output. Thoughts?
There is no problem in having domain rules in the builder if the builder is named after the domain it is building. Its whole purpose is to relieve the domain model from complex construction logic. It is true that the domain model has remained empty (not anemic yet) after the change, but that is only because this is the demo code. I didn't implement any behavior of the invoices other than their construction and UI. You can imagine further requirements being implemented right in the invoices, and then you would appreciate them not being inflated with the construction logic.
I've used the builder method a couple times in the past, but instead of returning "this" I would return an Interface with the appropriate "next" steps exposed.
I am learning from your fluidity with LINQ and your skill in highlighting best use of design patterns. I started writing software in the 80-90's in pursuit of scientific knowledge. That is my only turn-off about your presentations - they represent capitalism, with "customer's requirements" and the need to profit. I may be idealistic, but I would love to see examples from the scientific domain, where the code more often needs to discern patterns, not apply policies.
@@manmohanmundhraa3087 You can rarely choose the size of the builder class. It is doing one task - to build a product object in a series of steps - and it does it all. It is not uncommon to see builders that are a thousand lines long and yet quite easy to develop and even easier to use. It is funny to see how many common rules of OOD do not hold when we talk about builders.
Programming magic is one of the reasons I LOVE this field. It's a beautiful thing when a library or piece of software works in a magical way and makes the other developers think about how it was achieved. Puzzles everywhere!
It is amazing how, by using the right patterns and techniques, we can hide the complexity of the domain and show only the "intention revealing expression" (I loved this sentence) to the consumer. Thanks for sharing!
I have to say this pattern was one of the most complex for me to understand and I'm still struggling in knowing when and how to develop it in my own in the correct way. This a good example,complex though.. thanks for sharing
Great Lesson! I always learn something new with all your video sir. Thanks.
This is a good demonstration for me to understand Builder Pattern. Great lesson! Thank you for making that!
Thank you Zoran for such good , real-world example
I'm glad you liked it!
Keep up the good work. Very nice explanation with real world example.
Excellent video! And yes, this is a very complex example. It fits really great for scenarios like this one.
If I came up with a simpler example, then there would possibly be no justification to apply the Builder in the first place. I might also make a video that demonstrates how exactly it comes that we don't need a Builder in simpler cases.
I understand. I'd be more than happy to see another video with simpler alternatives, but take your time :). Thank you!
Great use case of builder with proxy methods, Before I just used it to create test data for complex objects , now I have some new insights😊
Excellent I would like to see how you do your strategy pattern and adapter pattern
I do have plans to demonstrate Strategies in one of the future videos.
This is great content. Can we have the link to the full course showing how all the implemetations of the BOOK STORE app.
There is no full course at the moment. I might make it in the near future, though.
@@zoran-horvat will be on the lookout then... ☺️
Thank you for the great video! What if we need to guide the users of the builder on which method they can use next? For instance if builder has mandatory steps or one steps can be called only after others? I guess we can introduce different interfaces and return them after each operation. Maybe you can share what could be the better way of implementing such complex builder?
That is a good question, indeed! You may try this article I wrote a few years ago: codinghelmet.com/articles/advances-in-applying-the-builder-design-pattern
Not everything in that article would I back today, but it should be about the topic you have mentioned here.
Thank you so much!
I'm always using a similar builder design for specifying readable test cases.
Especially when non-technical people specify cases with IntelliSense auto-complete, it's important that the compiler forces them to use the builder correctly.
Excellent ❤
Hi Zoran, I've noticed that Pluralsight have marked some of your Tactical Design Patterns courses and your "Making Your C# 6 Code More Object-oriented" course as retired. Would you still recommend watching these courses to somebody who have not gone through these before?
I still recommend those courses until I publish a new version. The problem with older courses is in the use of obsolete syntax, but on the positive side the themes gave not changed, and the way we address the problems is still the same.
@@zoran-horvat Thanks, i will check these out :)
Is it good practice to use business logic in builder method? While unit testing the calculations will it not become diffcult?
This is a very specific builder - it is part of the domain model and it naturally contains domain logic.
However, if more requirements came, asking for variation, then the Builder would probably have to receive appropriate strategies or something of that sort.
@zoran-horvat Isn't this just a "Fluent Interface" implementation and not actual Builder pattern?
It is actually a fluent builder, like StringBuilder, for example.
This is a great example for encapsulating the extremely complex logic to make life easier for a caller. I have a question for this solution though. How would you unit test this? For example, AppendFee, AppendInterest and GetInterest for are complex enough on their own to have unit tests for each of them separately. But since they're private, you can't reach them from the tests (unless you make them internal and make internals visible in test project or public, but that brings up some red flags in design, so I don't think it's a good approach). You can test the builder as a whole, but since you call AppendFee(AppendInterest(deliquency)) in Build method, it would not really be trivial to know why some test failed - is it the fee calculation that's wrong or interest calculation? Or maybe both? Or maybe neither? Thanks in advance.
I would focus testing on the attributes of the product.
Builder normally has more than one responsibility, if we assume that each component it manages is a separate responsibility. Each component naturally requires unit testing, and so I envision it would be useful to focus on one component per test and keep all the rest at bare minimum. Then assert the qualities, the consequences caused by that component in the product.
I think that adding variation to the builder for the sake of testing would be overengineering. The whole purpose of the builder is to be the single point where the rules of composition are defined, so leave it be that way, that is my opinion.
@@zoran-horvat So, if I understood correctly, in the example of AppendFee(AppendInterest(deliquency)), you'd construct an object via the builder in which the AppendInterest(deliquency) would not change the deliquency at all (or as little as possible), but in that object there's a significant change done by AppendFee method for the result of previous operation and test the result? And another test would be vice versa - interest would "change the result a lot", while fee would make minimal or no difference to the final result?
@@m5s10 You can skip any method that is not mandatory and use simplest possible arguments in those that must be called. Then carefully call the method you are interested in, and assert that the result of that call has caused the expected change in the product.
@@zoran-horvat understood, thank you
Can we create an automated unit test for this builder class?
Yes, of course. It would test all invariants and postconditions from the specification of requests. Since none of the requirements has any variation, there is nothing to inject into the Builder, which makes testing easier.
Seems the builder is capturing domain concepts that belong to the invoice. The invoice itself is anaemic. How about capturing rules in the model itself and then a printer decoration object knows how to produce the output. Thoughts?
There is no problem in having domain rules in the builder if the builder is named after the domain it is building. Its whole purpose is to relieve the domain model from complex construction logic.
It is true that the domain model has remained empty (not anemic yet) after the change, but that is only because this is the demo code. I didn't implement any behavior of the invoices other than their construction and UI. You can imagine further requirements being implemented right in the invoices, and then you would appreciate them not being inflated with the construction logic.
I've used the builder method a couple times in the past, but instead of returning "this" I would return an Interface with the appropriate "next" steps exposed.
That is the fluent builder design. It is useful in cases when an unrestricted use of the builder could cause confusion and errors.
@@zoran-horvat Thanks! Great video's by the way.
Are you spying on me? I just used this pattern today.
I can't disclose my sources :)
I am learning from your fluidity with LINQ and your skill in highlighting best use of design patterns. I started writing software in the 80-90's in pursuit of scientific knowledge. That is my only turn-off about your presentations - they represent capitalism, with "customer's requirements" and the need to profit. I may be idealistic, but I would love to see examples from the scientific domain, where the code more often needs to discern patterns, not apply policies.
Is not class is too big ?
@@manmohanmundhraa3087 You can rarely choose the size of the builder class. It is doing one task - to build a product object in a series of steps - and it does it all.
It is not uncommon to see builders that are a thousand lines long and yet quite easy to develop and even easier to use.
It is funny to see how many common rules of OOD do not hold when we talk about builders.