Building Complex Objects in a Simple Way with C#

Поделиться
HTML-код
  • Опубликовано: 1 ноя 2024

Комментарии • 44

  • @argon9113
    @argon9113 4 месяца назад +13

    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 4 месяца назад

      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.

  • @juliansegura5507
    @juliansegura5507 4 месяца назад

    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  4 месяца назад

      That's a proof of greatness 😅

  • @br3nto
    @br3nto 3 месяца назад

    Nicely presented! Some nice patterns to consider 😃

  • @guiportooo
    @guiportooo 4 месяца назад

    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 4 месяца назад +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.

  • @gpzim981
    @gpzim981 4 месяца назад +13

    The art or resolving non-existing problems

    • @junior.santana
      @junior.santana 4 месяца назад +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.

  • @jimiscott
    @jimiscott 4 месяца назад +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 4 месяца назад +1

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

    • @Timelog88
      @Timelog88 4 месяца назад

      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 3 месяца назад

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

  • @HellPotro
    @HellPotro 4 месяца назад +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  4 месяца назад +1

      FluentAssertions, FluentValidations, Fluent everything 😁

  • @skellious
    @skellious 4 месяца назад

    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 3 месяца назад

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

  • @justsomejunk399
    @justsomejunk399 3 месяца назад +1

    Looks like your paycheck depends on number of code lines?

  • @pedrosilva1437
    @pedrosilva1437 4 месяца назад +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 4 месяца назад +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  4 месяца назад

      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 4 месяца назад

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

  • @tempusmagia486
    @tempusmagia486 4 месяца назад

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

    • @junior.santana
      @junior.santana 4 месяца назад

      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

  • @MetronSM
    @MetronSM 4 месяца назад +5

    Too much unnecessary code. Steps in predefined order etc.

  • @matheussousa2548
    @matheussousa2548 4 месяца назад +1

    Well done!

  • @lindokuhlemncwabe8909
    @lindokuhlemncwabe8909 4 месяца назад +1

    Nailed it.

  • @guilhermealves577
    @guilhermealves577 4 месяца назад +2

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

  • @FlippieCoetser
    @FlippieCoetser 4 месяца назад

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

    • @junior.santana
      @junior.santana 4 месяца назад

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

  • @sultonbekrakhimov6623
    @sultonbekrakhimov6623 4 месяца назад +1

    First comment and like, good explanation

  • @jfevia
    @jfevia 4 месяца назад +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 3 месяца назад

    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 4 месяца назад

    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 4 месяца назад +2

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

  • @atlesmelvr1997
    @atlesmelvr1997 4 месяца назад +7

    why?... this is just bloat

  • @syedhoque9057
    @syedhoque9057 4 месяца назад +3

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

  • @FilipCordas
    @FilipCordas 4 месяца назад +3

    This is terrible code object initializes exist.

  • @bitmanagent67
    @bitmanagent67 4 месяца назад

    C#, meet SmallTalk.

    • @gui.ferreira
      @gui.ferreira  4 месяца назад

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