Building Complex Objects in a Simple Way with C#

SdĂ­let
VloĆŸit
  • čas pƙidĂĄn 24. 06. 2024
  • In this video, I will show you how to implement the Fluent Builder Pattern in C#.
    💎 Be a Patreon to get the source code: / gsferreira
    🚹 KEY LINKS
    đŸ€ Support me on Patreon (and get access to source code) here: / gsferreira
    💌 Free Developer Insights: guiferreira.me/newsletter
    📘 Courses: guiferreira.me/courses/
    đŸ‘šâ€đŸ« Workshops: guiferreira.me/workshops/
    🔗 GET IN TOUCH
    LinkedIn: / gferreira
    Twitter: / gsferreira
    GitHub: github.com/gsferreira
    Get my newsletters: guiferreira.me/newsletter
    Visit my blog: guiferreira.me
    👋 WHO AM I
    Hey! If you're new to my Channel, I'm Guilherme. Call me Gui. I'm a Minimalist Software Craftsman. What do I do here? I share tips on how to simplify your life as a Developer.
    đŸŽ” MUSIC CREDITS
    Consciousness / StreamBeats / Lofi
    #csharp #dotnet

Komentáƙe • 42

  • @br3nto
    @br3nto Pƙed 11 dny

    Nicely presented! Some nice patterns to consider 😃

  • @argon9113
    @argon9113 Pƙed měsĂ­cem +11

    As an alternative solution, we can probably consider not creating N classes with separate steps, but having the builder return an interface with its own method (which is implemented through the interface). Thus , you can do with 1 builder and N interfaces .

    • @diegofaria8187
      @diegofaria8187 Pƙed měsĂ­cem

      I would suggest the same approach. I implemented it on a recent project to receive a bunch of files from different sources (base64, stream, byte array), and save it on disk or compress everything into a zip file before save on disk.

    • @focl2003
      @focl2003 Pƙed 18 dny

      Awesome! You also have the aditional advantage of not being able to create (or seeing) nested objects from the client code.

  • @gpzim981
    @gpzim981 Pƙed měsĂ­cem +10

    The art or resolving non-existing problems

    • @junior.santana
      @junior.santana Pƙed 25 dny

      Programmers: when we don't have enough problems to solve we make an effort to create them 😂

  • @revillsimon
    @revillsimon Pƙed 16 dny

    Thank you Gui. I watched your excellent presentation on Tech Excellence and was watching your video out of interest. I’m a frontend developer and after watching this it made me realise I could use this pattern to build dynamic DOM trees in my test suite without having to create any HTML files. After finding that working, I realised I could also use it to build a custom TypeScript module configuration per test. This has helped me so much, and is having a massive impact on my test code right now so thank you so much for this. 👍
    I had been looking for a way to do this for a while and your video provided the answer with a really simple but powerful pattern.

  • @matthewrossee
    @matthewrossee Pƙed měsĂ­cem +3

    It's also worth pointing out that the builder pattern isn't always the best solution, especially when the object has multiple required fields, because then you're moving compile-time errors to run-time errors.

  • @juliansegura5507
    @juliansegura5507 Pƙed 27 dny

    I used this to create invoices on a system a while back. Didn't know it was a pattern at the time... I loved that "I came up with it" 😅.

  • @guiportooo
    @guiportooo Pƙed 29 dny

    Very nice approach receiving an action to build nested objects. I'll start using it :)
    For the required properties scenario, if you're using the builders for test cases, an alternative would be to have all required properties set to default values on your builder's constructor, so it's not possible to build an empty object for example. You won't have all the control you have with your steps approach but it's easier to maintain.
    I would also suggest checking the AutoBogus library to initialize your builders with generated data.

  • @jimiscott
    @jimiscott Pƙed měsĂ­cem +6

    Good demo, but why oh why did you use the builder pattern for a dto type object? I feel if you had a proper scenario the demonstration may have carried a bit more heft - what you have is an overengineered construction of an object that can/should be initialised with properties.

    • @stefan-d.grigorescu
      @stefan-d.grigorescu Pƙed měsĂ­cem +1

      Yeah, in such a scenario I would just use a parameterless constructor with required properties

    • @Timelog88
      @Timelog88 Pƙed 23 dny

      We currently have a DTO that is created in your suggested way, all IDE's and Linting tools go on a fit because the creation of that DTO goes over the cyclomatic complexity threshold we use (which is the default threshold IF you enable it). Why? Because the DTO is for a detail page that also contains nested collection that may or may not be empty, and the source we need to map it from can be null, so there is a lot of (list?. Where(x => x != null || x.blabla).Select(...) ?? Enumerable.Empty()).
      The builder pattern is great for splitting up that logic in a way that's more readable and maintainable, especially when your DTO's are immutable value objects.

  • @thanzeeljalaldeen
    @thanzeeljalaldeen Pƙed 6 dny

    this looks great!, but how does it align with the DDD pattern. the validation and business logic should be in the domain model ryt

  • @matheussousa2548
    @matheussousa2548 Pƙed měsĂ­cem +1

    Well done!

  • @pedrosilva1437
    @pedrosilva1437 Pƙed měsĂ­cem +1

    Good video! But how do you handle validation errors that happen during the builder member setting? Do you throw exceptions on the provided data or do validation and return error results?

    • @stefan-d.grigorescu
      @stefan-d.grigorescu Pƙed měsĂ­cem +1

      Both are applicable, depends what you want to do.
      Throwing exceptions on the way is easier imo, because it shortcirtcuits the construction process.
      If you want to have all exceptions, not only the first, for some reason, you can introduce something like a private list of exceptions in the builder. Once you have the first exception inside, that builder instance is faulty and should not construct any properties further, but only append other exceptions if encountered. In the end, when you call Build, you'd get either a valid constructed object or a nonempty list of exceptions (using some Discriminated Union implementation, be it custom or with a library)

    • @gui.ferreira
      @gui.ferreira  Pƙed 26 dny

      As @stefan-d.grigorescu said, both are applicable.
      Also, it depends on where you are using them and the strategy you have in place.
      Example: I often use Builders for setup Testing data, and there, an exception is perfect. If I have a builder based on user input, I might prefer to have a "TryBuild" that returns feedback if the configuration is invalid.

    • @stefan-d.grigorescu
      @stefan-d.grigorescu Pƙed 26 dny

      @@gui.ferreira Although, I think of another point with user input, that I have not seen so much in the discussions about exceptions vs result pattern: what about an API that is designed together with its FE app, in a 1-1 scenario? There, many BE pre-validations are only for security in depth, but in almost all scenarios will not be triggered, because there are already some counterpart validations on FE that do not let the flow continue to BE at all.
      For example, null, empty or too long strings. If the API is designed with a sepecific FE that you know it already validates its user input from forms, then meeting a null, empty or too long string on BE really becomes an exceptional situation, hence I feel right about throwing exception.

  • @justsomejunk399
    @justsomejunk399 Pƙed 14 dny

    Looks like your paycheck depends on number of code lines?

  • @lindokuhlemncwabe8909
    @lindokuhlemncwabe8909 Pƙed měsĂ­cem +1

    Nailed it.

  • @FlippieCoetser
    @FlippieCoetser Pƙed měsĂ­cem

    Is it not less efficient to each time return the entire this?

    • @junior.santana
      @junior.santana Pƙed 25 dny

      Nope. There's no entire "this", since it is just a pointer to the actual object

  • @HellPotro
    @HellPotro Pƙed měsĂ­cem +1

    Nice video, this reminds me of fluent assertions. It is always useful to have a more natural way of coding.
    Keep it up!

    • @gui.ferreira
      @gui.ferreira  Pƙed 26 dny +1

      FluentAssertions, FluentValidations, Fluent everything 😁

  • @tempusmagia486
    @tempusmagia486 Pƙed 28 dny

    can you make a comparison with the factory pattern? isnt this the same?

    • @junior.santana
      @junior.santana Pƙed 25 dny

      You use a factory pattern to create/get an instance of an object encapsulating details on how it is created. Usually the caller only has to know about the abstraction, e.g., an interface and it calls the factory to create the instance. Multiple implementations might coexist and the factory is responsible for getting the right one.
      Builder is different. It's more of a sintaxe sugar, a way to build complex objects by composing steps. In this case, the caller has to know how the object is created

  • @guilhermealves577
    @guilhermealves577 Pƙed měsĂ­cem +2

    Thanks for de vĂ­deo but i prefer the normal way hahahahha

  • @MetronSM
    @MetronSM Pƙed měsĂ­cem +3

    Too much unnecessary code. Steps in predefined order etc.

  • @skellious
    @skellious Pƙed měsĂ­cem

    thanks for the tutorial, very useful.
    One note on pronunciation, genre is more often pronounced zjohn-rah, rather than "jen-reh" which sounds a lot like you are saying "gender".

  • @sultonbekrakhimov6623
    @sultonbekrakhimov6623 Pƙed měsĂ­cem +1

    First comment and like, good explanation

  • @jfevia
    @jfevia Pƙed měsĂ­cem +6

    I'm sorry, I just keep thinking of the "but why?" meme 😄. Yes, patterns are cool and all that, but there's little value in writing a bunch of classes if all you want to do is build reports... Chances are you are part of a team that will have to maintain your code at some point. We really need to do better as engineers and keep it simple...

  • @galgol23
    @galgol23 Pƙed 5 dny

    Thanks you showing good stuff, can you please not move fast or jump from place to place so quick, when you learn we need couple of seconds to ingest what is happening

  • @paulmartins5521
    @paulmartins5521 Pƙed měsĂ­cem

    Great video as always. I'm a big fan of the Fluent builder style but I do find it requires a lot of coding to support. I've been working on a Roslyn library to generate the API as extension methods automatically. It's still in development but should go live soon. I think it will be very helpful to engineers and I'd love your feedback on it if you have time.

    • @paulmartins5521
      @paulmartins5521 Pƙed měsĂ­cem +2

      If you are interested, the library is available as a package called Fluentify.

  • @syedhoque9057
    @syedhoque9057 Pƙed měsĂ­cem +3

    Like your content, but.... this is not everyone's cup of tea.
    Lots of verbage.
    🌟

  • @atlesmelvr1997
    @atlesmelvr1997 Pƙed měsĂ­cem +5

    why?... this is just bloat

  • @FilipCordas
    @FilipCordas Pƙed měsĂ­cem +2

    This is terrible code object initializes exist.

  • @bitmanagent67
    @bitmanagent67 Pƙed měsĂ­cem

    C#, meet SmallTalk.

    • @gui.ferreira
      @gui.ferreira  Pƙed 26 dny

      Many design patterns used today, were defined on the Smalltalk days