Building Complex Objects in a Simple Way with C#

Поділитися
Вставка
  • Опубліковано 16 жов 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...
    📘 Courses: guiferreira.me...
    👨‍🏫 Workshops: guiferreira.me...
    🔗 GET IN TOUCH
    LinkedIn: / gferreira
    Twitter: / gsferreira
    GitHub: github.com/gsf...
    Get my newsletters: guiferreira.me...
    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

КОМЕНТАРІ • 44

  • @argon9113
    @argon9113 3 місяці тому +12

    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 3 місяці тому

      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 3 місяці тому

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

  • @revillsimon
    @revillsimon 3 місяці тому

    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.

  • @br3nto
    @br3nto 3 місяці тому

    Nicely presented! Some nice patterns to consider 😃

  • @guiportooo
    @guiportooo 3 місяці тому

    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.

  • @matthewrossee
    @matthewrossee 3 місяці тому +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.

  • @jimiscott
    @jimiscott 3 місяці тому +7

    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 3 місяці тому +1

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

    • @Timelog88
      @Timelog88 3 місяці тому

      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.

  • @juliansegura5507
    @juliansegura5507 3 місяці тому

    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" 😅.

    • @gui.ferreira
      @gui.ferreira  3 місяці тому

      That's a proof of greatness 😅

  • @gpzim981
    @gpzim981 3 місяці тому +13

    The art or resolving non-existing problems

    • @junior.santana
      @junior.santana 3 місяці тому +1

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

    • @atomicazure
      @atomicazure 2 місяці тому +1

      Once you become an experinced programmer, you will notice these problems. For instance, primitive obsession, bounded context, value objects, strongly typed ids, etc.

  • @thanzeeljalaldeen
    @thanzeeljalaldeen 2 місяці тому

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

  • @justsomejunk399
    @justsomejunk399 3 місяці тому +1

    Looks like your paycheck depends on number of code lines?

  • @pedrosilva1437
    @pedrosilva1437 3 місяці тому +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 3 місяці тому +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  3 місяці тому

      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 3 місяці тому

      @@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.

  • @FlippieCoetser
    @FlippieCoetser 3 місяці тому

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

    • @junior.santana
      @junior.santana 3 місяці тому

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

  • @tempusmagia486
    @tempusmagia486 3 місяці тому

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

    • @junior.santana
      @junior.santana 3 місяці тому

      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

  • @skellious
    @skellious 3 місяці тому

    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".

  • @commander3192
    @commander3192 2 місяці тому

    Great video. I fear looking at the code of the people saying "but why".

  • @HellPotro
    @HellPotro 3 місяці тому +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  3 місяці тому +1

      FluentAssertions, FluentValidations, Fluent everything 😁

  • @MetronSM
    @MetronSM 3 місяці тому +5

    Too much unnecessary code. Steps in predefined order etc.

  • @sultonbekrakhimov6623
    @sultonbekrakhimov6623 3 місяці тому +1

    First comment and like, good explanation

  • @paulmartins5521
    @paulmartins5521 3 місяці тому

    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 3 місяці тому +2

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

  • @guilhermealves577
    @guilhermealves577 3 місяці тому +2

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

  • @matheussousa2548
    @matheussousa2548 3 місяці тому +1

    Well done!

  • @lindokuhlemncwabe8909
    @lindokuhlemncwabe8909 3 місяці тому +1

    Nailed it.

  • @galgol23
    @galgol23 2 місяці тому

    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

  • @jfevia
    @jfevia 3 місяці тому +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...

  • @atlesmelvr1997
    @atlesmelvr1997 3 місяці тому +7

    why?... this is just bloat

  • @syedhoque9057
    @syedhoque9057 3 місяці тому +3

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

  • @FilipCordas
    @FilipCordas 3 місяці тому +3

    This is terrible code object initializes exist.

  • @bitmanagent67
    @bitmanagent67 3 місяці тому

    C#, meet SmallTalk.

    • @gui.ferreira
      @gui.ferreira  3 місяці тому

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