Keep an Eye on SRP - That Might Just Save Your Broken Code Base

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

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

  • @zoran-horvat
    @zoran-horvat  Год назад

    Download the source code from Patreon: www.patreon.com/posts/source-code-for-88367914
    Learn more from the video course *Beginning Object-Oriented Programming with C#* ► codinghelmet.com/go/beginning-oop-with-csharp
    Learn from related videos:
    Clean Code Tip: Remove Messy Constructor Calls ► ruclips.net/video/6g4ggpOxxCc/видео.html
    Remove Separate Concerns From a Class and Make It Favor SRP Again ► ruclips.net/video/5CU7137IWf4/видео.html
    Avoid Returning Null From Methods - There Is a Better Way To Write Them! ► ruclips.net/video/HRLdcMil7Ec/видео.html

  • @codingbloke
    @codingbloke Год назад +16

    I think one of the difficulties of SRP is just accepting that a "responsibility" can be very very small. Its easy to conflate a set of small responsibilities into what you imagine is a "single" one but it is not. I can't tell you the number of times I have come across code that largely duplicates logic because the developer (which admittedly is often me) couldn't bring themselves to separate out the responsibilities. A very, very valuable video, thank you!

    • @zoran-horvat
      @zoran-horvat  Год назад +22

      The worst part of SRP is that it is subjective. None of the SOLID principles has verifiable rules when and how to apply it. I believe if I left this video for a year and watched it then I wouldn't agree with myself in half of the decisions I made today.

    • @adambickford8720
      @adambickford8720 Год назад +3

      @@zoran-horvat that's both upsetting and completely relatable

  • @bonchodikov6248
    @bonchodikov6248 Год назад +4

    Sir, years after I have watched one of your many courses in plural site I am still getting amazed from your deep logic and simplicity of your code. Thank you very much, definitely you are pushing me to be a better dev. Thank you and please don't stop sharing your skill for the rest of us.

  • @julealgon
    @julealgon Год назад +6

    Always interesting to see more people pushing for the extremely powerful decorator pattern.
    The restricted discount could also leverage the specification pattern for more complex conditions as needed.
    I'd like to see you tackle potentially 2 distinct things on top of this model though:
    1. How would you persist these composite discounts (say, in a database) and transfer them through an API
    2. What would you propose to isolate the "UI representation" concern of the discount object (formatting into a label/string) from the rest of its implementation.

    • @zoran-horvat
      @zoran-horvat  Год назад +9

      You have asked two perfect questions for this model.
      1. Hierarchical structures like the one from my demo are best persisted and transferred as a document (e.g. a JSON document). This structure is not suitable for relational databases. I would start from serialization/deserialization to JSON.
      2. You are right about that. I didn't want to extend the talk further, but yes, any transformation of the domain object into whatever other form belongs to the place that produces that other form. In this case, turning the domain object into labels certainly belongs to the UI layer. There are several reasons for that, one being internationalization - the UI might require something other than a plain string to represent labels so that they can be translated depending on the output language. That responsibility certainly does not belong to where the domain model is defined.

  • @marko_losonc
    @marko_losonc Год назад +2

    What I like the most with this approach is that the entire logic in DemoDiscountServer class is soooo descriptive that it could easily be understood by almost anyone, even non-programmers.

    • @zoran-horvat
      @zoran-horvat  Год назад +5

      There will be an entire video on that coding style. I find it critically important in classes with complex logic.

    • @marko_losonc
      @marko_losonc Год назад

      @@zoran-horvat I'm looking forward to it.

  • @martins.8349
    @martins.8349 Год назад +1

    your videos are impressively good
    everyone always talks about fancy patterns but never explains what to do with them
    you nailed that and use simple words in a slow manner so anyone can understand them

  • @AlexUkrop
    @AlexUkrop Год назад +1

    Very cool functional programming at the end. Thanks

  • @ivandrofly
    @ivandrofly Год назад +1

    Thank you

  • @adambickford8720
    @adambickford8720 Год назад +1

    SRP: the 'No true Scotsman' of software engineering. If it didn't work, you didn't use enough!
    One of the things I like about FP is it really encourages SRP. Your `map`, `filter`, etc, by definition, are expected to do only thing. You're even encouraged to keep them as small '1 liners'. I find loops to be the root of this kind of complexity.

    • @zoran-horvat
      @zoran-horvat  Год назад +5

      Everything you said also stands for OOP.
      Moreover, for every functional program there is an isomorphic object-oriented program. If it is good, then it is good in OOP, too. Therefore, it is not the coding paradigm that makes the difference, but coding practices applied by ones or the others. Don't forget functional programmers who write monstrously large imperative functions, still believing they made functional code!
      The fact that it is less likely you will encounter bad FP code is due to having less bad coders there, which is the result of FP posing a much higher entry threshold for programmers.
      In other words: we need more expertise among programmers, no matter the paradigm.

    • @adambickford8720
      @adambickford8720 Год назад

      @@zoran-horvat Sure, but getting there in OOP is much, much more difficult. IME OOP has too much 'art' to it. Even when done well, its maintainers often compromise it. The upside is its lower barrier to entry means it's likely 'good enough', even if you botch it.
      FP devs are 'better' than OOP ones for the same reasons linux users are usually more capable than windows users; they have to be ;)

  • @RiversJ
    @RiversJ Год назад

    I would've solved this with a class that takes in a collection of conditions and generic math discount operands that would allow multiple discounts to apply with a deterministic order of operations.
    Would probably be a bit more complex but allow very high extensibility. Can definitely see the value in this as well, method chaining to compose the discount would be very easy for a new developer arriving at this class system.

  • @nishiths9228
    @nishiths9228 Год назад

    wonderful

  • @ivandrofly
    @ivandrofly Год назад

    One question the extension method .WhenBookAuthoredBy(Person) is just returning a new instance of BookAuthorDiscount class?
    19:31

    • @zoran-horvat
      @zoran-horvat  Год назад +1

      Yes, but the one that wraps the discount to which the extension method is applied.

    • @ivandrofly
      @ivandrofly Год назад

      Thanks for the reply.
      What is the name of that discount? Can I have a peak on the .WhenBookAuthoredBy(Person) maybe I will be able to understand
      Thanks in advance

    • @ivandrofly
      @ivandrofly Год назад

      Is this the product of calling `WhenBookAuthorBy(Person)` extension method (see below)?
      .WhenBookAuthorBy(Person) -> WhenBookAuthorBy(this Discount discount, Person person) => new BookAuthorDiscount(person, discount) @@zoran-horvat

  • @rustamhajiyev
    @rustamhajiyev Год назад +1

    I rest my case - the mic drops 🫳🎤
    Awesome episode! 😎🥰

    • @zoran-horvat
      @zoran-horvat  Год назад +5

      Many programmers only see object composition through its intrinsic complexity. The complexity is real. Debugging is hard. All that is true.
      But what they fail to see so often is that object composition is a necessity. It is impossible to develop a large application with great numbers of rules, often contending ones, without composition.

    • @rustamhajiyev
      @rustamhajiyev Год назад

      @@zoran-horvat Can't agree more

  • @SteveNgaiCheeWeng
    @SteveNgaiCheeWeng Год назад

    I think in real-life e-commerce system, the business rules are complicated (i.e. different countries could have different rules), and dynamic (they keep changing and i.e. special rules to be applied during a campaign period only,). Is it better that we store the business rules in the database and have one generic class that covers all the business rules dynamically instead of creating many classes with business rules in the class? With the rules in the database, it is also easier for the admin to make changes to the business rules from the UI. What are your thoughts on this?

    • @zoran-horvat
      @zoran-horvat  Год назад

      The Demo class is what its name is suggesting - a demonstration. In a real system, all those small classes could be instantiated and combined based on some document which could vary based on the use case.

  • @tonyyip1969
    @tonyyip1969 Год назад

    Thanks for the awesome video. Could decorator pattern able to achieve the same result?

    • @zoran-horvat
      @zoran-horvat  Год назад +3

      A few of those classes are indeed decorators, but not all of them.

  • @LordErnie
    @LordErnie Год назад +1

    Great episode for sure. I never thought about something like a simple mathematical operation as something that is, from the domain standpoint, something that should be calculated within its own seperate unit that carries responsibility for that calculation. An eye opener for sure. But this is one of those cases where OOP loses me a bit. I still study informatics and software engineering, so please don't take any offense. The usage of decorators (are they? It's an estimated guess) and wrapping classes is a bit confusing. I'm losing track of what goes before what and why. The code, to me, doesn't speak for itself and I would need a chart or map of some sort that would show me the flow and what objects belong where inside of that flow. The scenarios where these types of solutions are applicable are higher up in the industry. It's a solution for a very big application, or at least one that needs to give a developer very fine-grained control over every single step of a process. If you need this much control over a discounting system, I would say that this is something that would almost be at the level where you define a completely separate pipeline for it. I'm probably wrong half the time here, but taking SRP this far is something that still obliviates me a bit. How does one understand the wrapping and the combining of classes like you do here? It's wild but confusing.

    • @zoran-horvat
      @zoran-horvat  Год назад +9

      I see the source of your confusion, and I even plan to make a separate video on that issue. It is a common problem, especially in young programmers and I would argue that understanding that single point is the critical understanding required to become a senior programmer.
      The problem you are facing in trying to see the order of execution is real. It is there. But it is not substantial. The true problem you are facing is **wanting** to see the order of execution. That is irrelevant! It is established once the composition is created.
      Now, that leaves a huge gap that must be filled. What **does** count then? It is the contract of the interface or a method. The promise of a type or a call. If the call returns IDiscount, then it is the contract of IDiscount that matters to you and nothing else. Trying to see beyond the interface is to break its encapsulation, and that is just wrong. The interface doesn't promise anything with respect to the issues you have listed, so why ask? Now try to watch this demo from that angle and you will begin to understand it.

    • @marklord7614
      @marklord7614 Год назад

      I relate to the criticizm. As a casual programmer, I developed a habit of trying to understand the order of execution of new code and, in this case, it leads me down a rabbit hole. However, I will take the advise of backing off and pay more attention to method signatures instead and treat the two ways of looking at code separately. @@zoran-horvat

    • @ClickOkYT
      @ClickOkYT Год назад

      @@zoran-horvat I totally relate to this *wanting to see the order of execution* thing. I am a developer for +25 years, so I "born" in procedural world, then was painful to follow things in deep hierarchy in OOP, but chains of functional coding burn the brain cells too... I found really easier, for example, newline anything after a dot in Linq because that. Tips about that kind of thing will really be amazing!

  • @muhammadumair9074
    @muhammadumair9074 Год назад

    Hi Zoran, Great videos and I am amazed how you sync yourself with the code screen on Visual Studio. It will be great if you make a video on how you create/record your videos. -Umair

    • @zoran-horvat
      @zoran-horvat  Год назад

      Guess what: I will! There is plan for that in the near future.

    • @muhammadumair9074
      @muhammadumair9074 Год назад

      @@zoran-horvat Great. Thanks for that.

  • @pickle1987
    @pickle1987 Год назад

    SRP = single line in each class

  • @lucaffo99
    @lucaffo99 Год назад +2

    If you have a discount based on a name, you quickly go in the rabbit hole of localization. Some books are translated in other languages, so, in reality your book model need a category or tags to set discount based on tags. This would be my solution to this specific problem.

    • @zoran-horvat
      @zoran-horvat  Год назад +7

      Don't get this example too literally. The purpose of the demo is to show a specific situation where the execution of a method (i.e. filtering out a call) depends on a piece of data that is not available at instantiation time. Try to view the demo through those lenses and you will see the difficulty that the class alone cannot address.

  • @vbachris
    @vbachris Год назад +1

    everyone is going to patreon, why doesn't youtube have it's own sponsorship system?

    • @zoran-horvat
      @zoran-horvat  Год назад +3

      RUclips does have its sponsorship system, but the capabilities end at controlling access to videos. RUclips does not support sharing files with sponsors - and that is precisely what we, programmers need. I believe that is the reason why so many programming channels opt to use an external platform to distribute files with sponsors, where RUclips platform would be a natural choice.

    • @ClickOkYT
      @ClickOkYT Год назад

      RUclips is unstable, it can block/demonetize/etc by lots of random reasons.

    • @evancombs5159
      @evancombs5159 Год назад +1

      ​@@ClickOkYTto be fair the same can be said about Patreon or any other platform. Usually I would say the best method is to not be loyal to a single site, and use multiple sites. The downside being duplicate work.

  • @rezakaaccount
    @rezakaaccount Год назад +1

    All those extension methods introduce what I call hard-implicit-dependencies. These violate dependency inversion. I agree that the readability is enhanced but good luck testing the methods/classes that use extension methods specially when they contain business logic.

    • @zoran-horvat
      @zoran-horvat  Год назад +3

      This comment has too many misconceptions to clear them all with another comment, but I will still try.
      All extension methods demonstrated in the video are factory methods. One cannot mix them up with static utility methods, such as DateTime.Now or Random.Next. Moreover, the only method in the demo that consumes them is a factory method itself! One rarely injects factories into other factories, when a more direct solution exists in the form of injecting their products instead - that is precisely how this system of factory methods in the demo works, each factory method receiving one or more products of another, unknown factory.
      DIP is very well observed in the demo at the place where it belongs, i.e. where the factory's product is required. That is, in particular, the page model, which only depends on the product, injected by the IoC framework.
      Testing static factory methods is as easy as any other, as they allow method injection of subordinate products. We normally apply contract testing and structural inspection to the product returned.

    • @Alguem387
      @Alguem387 Год назад

      'Using int.MaxValue creates a hard implicit dependency what of I want to mock Int32 for unit testing'
      You sound like this