Enums aren't evil. Conditionals everywhere are

Поділитися
Вставка
  • Опубліковано 24 січ 2024
  • Seeing the same conditional statements littered everywhere? There are different ways of handling that but a lot of it comes down to your context. Conditionals around type checking, extension methods, Inheritance and Polymorphism are all options.
    🔗 EventStoreDB
    eventsto.re/codeopinion
    🔔 Subscribe: / @codeopinion
    💥 Join this channel to get access to a private Discord Server and any source code in my videos.
    🔥 Join via Patreon
    / codeopinion
    ✔️ Join via UA-cam
    / @codeopinion
    📝 Blog: codeopinion.com
    👋 Twitter: / codeopinion
    ✨ LinkedIn: / dcomartin
    📧 Weekly Updates: mailchi.mp/63c7a0b3ff38/codeo...
  • Наука та технологія

КОМЕНТАРІ • 75

  • @CodeOpinion
    @CodeOpinion  5 місяців тому +2

    Want to learn more about software architecture and design? Join thousands of developers getting weekly updates!
    🚀mailchi.mp/63c7a0b3ff38/codeopinion

  • @CodeOpinion
    @CodeOpinion  5 місяців тому +14

    Shout-out to anyone that was confused at 6:46 when they heard a notification sound. Oops.. was recording my system audio for Nicks video and I got a notification during recording! Pretty sure there's another you can spot. I'll call this an easter egg :)

  • @marna_li
    @marna_li 5 місяців тому +36

    This made me think about why I dislike professional development in teams. Because I have not been in a situation where people have been discussing and designing together. It has just been an specification that been broken down to tasks for developers to complete. And no one has felt empowered to question this way of working.

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

      We should be able to curiously question everything 🙂

    • @majormartintibor
      @majormartintibor 5 місяців тому +1

      this 100

    • @marna_li
      @marna_li 5 місяців тому +10

      @@majormartintibor Sadly, I think that most developers find development so vast to navigate that they soon just settle for the money as long as they can do their work. So when a person who is truly interested in stuff enters there is going to be tension. This is especially true to someone who is more of a wizard when it comes to design and architecture - like many of us. They are quite an annoyance to the crowd who aren't interested in that. Not that they don't want to learn that too. They have other priorities in life than coding outside work.

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

      @@DevLeader I have often questioned stuff, but most people are just OK as long as they can do their work. I guess that I'm more purpose-driven than the average developer.

    • @majormartintibor
      @majormartintibor 5 місяців тому

      @@marna_li I agree with every word of yours.

  • @BlindVirtuoso
    @BlindVirtuoso 5 місяців тому +17

    Hi Derek. There's Martin Fowler's famous "Replace conditionals with polymorphism" refactoring.
    We can also apply "Tell, don't ask" principle.
    There are tons of ways we can replace conditionals here. Command pattern, Visitor pattern, etc.

  • @ravikumarmistry
    @ravikumarmistry 5 місяців тому +7

    I was big oop fan boy for 10 year and then one night you go into a rabbit whole of functional programming and everything changes. Still in my professional programme I tried to reduce inheritance as much as possible

    • @CodeOpinion
      @CodeOpinion  5 місяців тому

      I often rather make things explicit (as in my one example) and use inheritance in very limited use cases, generally.

  • @georgehelyar
    @georgehelyar 5 місяців тому +4

    The real problem with C# enums in general is that C# doesn't have exhaustive matching so if you add a new value to the enum you have to find all the places that need the new behaviour and it's easy to miss one

    • @CodeOpinion
      @CodeOpinion  5 місяців тому

      Which would require a switch statement. Which is not necessarily used.

  • @andrewthompson9714
    @andrewthompson9714 5 місяців тому +6

    I quite like the idea of using an empty interface as a "trait" which can then be patternmatched

  • @DevLeader
    @DevLeader 5 місяців тому +4

    Thanks so much for sharing your perspectives on this Derek!

  • @SkielCast
    @SkielCast 5 місяців тому +1

    Coming from a Python background, I usually create a Hashmap to map the enums to callables (akin to the Strategy pattern), that way you can map "downloadable enums" to the proper handler (be it a class or a function/method). You end up with several hashmaps but they are all centralized and keeps the code more organized than having the conditions in the method themselves.
    Plus you get exhaustive matching if need be by checking those hashmaps upon creation

  • @allinvanguard
    @allinvanguard 5 місяців тому +4

    Two very interesting approaches! I really like what you suggested - This seems like a classic tradeoff between imperative programming (Checking enum conditions explicitly) and OOP-driven, polymorphic programming. When I started off, I watched a lot of courses by Zoran Horvat, if you know him. He's probably one of the people who "gets" the OOP mindset the most. Your solution is exactly the way he would approach this as well, moving the logic into the model instead of having an anemic POCO model.
    I think the first suggestion is also a reasonable solution in case the behavior does not really "fit" the model.
    But bottom line, this also requires some discipline, and you need to be familiar with your model for sure. It's probably reasonable to start off with an imperative approach, until you have a good enough grasp on your domain model to refactor it into a polymorphic approach, since I feel like you might also run into the opposite inheritance hell when you are too eager with this approach.

    • @majormartintibor
      @majormartintibor 5 місяців тому +1

      Zoran is great, he understands OOP but he also understands Functional Programming and is pragmatic and doesn't mind mixing the 2 paradigms in C#.

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

    @CodeOpinion how do you know that the object to pass to the handler is a downloadableproduct?

  • @newaira333
    @newaira333 5 місяців тому +5

    So the biggest issue with enums is that very often new values get added over time and as a result, wherever enum conditonals are used, one has to go back and check if this new enum value needs to be added to the condition or not. As you know, it's incredibly easy to miss doing this in everywhere, and thus bugs get introduced easily.

    • @newaira333
      @newaira333 5 місяців тому +2

      Something like polymorphism gets around this and is superior to enums, since adding a new product subclass forces one to define all it's behavors so that none are missed.
      The good news is that if you love enums, and who doesnt!, there is a way to structure your code to ensure easy maintentance down the road. "Exhaustive switch statements" are your best friend - I think that would have been useful to include in this video.

    • @DevLeader
      @DevLeader 5 місяців тому

      This is mostly the motivation behind behind my original video 😁

    • @CodeOpinion
      @CodeOpinion  5 місяців тому +1

      I had that in my notes and I totally forgot to mention it. Mainly because most analyzers require you to be using it in a switch statement. Do you know any that do it differently?

    • @Forshen
      @Forshen 5 місяців тому +4

      if more enum values needs to be added, then think about why its a Enum. Imo enum's should't grow over time. Not saying there can never be a new enum value, but it should't be 'normal'. If it is getting added often, think about getting it as a Type/Enitity and handle conditions based on entity data from the database. A simple 'IsDownloadable' column in de database is more then enough to check if its downloadable. Your code can be the same, but ProductTypes can be added.

    • @newaira333
      @newaira333 5 місяців тому

      ​@@CodeOpinion Yes, any conditional involving enums would have to use an exhaustive switch. Not sure how else one can ensure each enum value is being handled explicitly.

  • @andersjuul8310
    @andersjuul8310 5 місяців тому

    I love the Second Opinion you offer to advice -- and would have appreciated the other way around as well. Good to consider alternatives and as a wise man often says -- "It depends!" ;)

  • @michaldivismusic
    @michaldivismusic 5 місяців тому

    It's awesome you reacted this fast 😃

    • @CodeOpinion
      @CodeOpinion  5 місяців тому

      Nick pinged me before his video came out.

  • @Folsets
    @Folsets 5 місяців тому

    Yes, this is great video, thanks

  • @TheGalantir
    @TheGalantir 5 місяців тому +1

    Out of the solutions presented here the enums and if staments are the most performant way of doing this by far.
    You can try to come up with all kinds of fancy solutions to get rid of them but when you really look at what is happening and how much cpu cycles you're adding by just doing all that fancy stuff pretty much nothing beats the enums and if statements.
    And why do we get rid of them? Because someone decided it wasn't clean code.
    Clean code is the reason why our superfast computers are still running what simple software poorly.

  • @br3nto
    @br3nto 5 місяців тому

    1:48 enums used like this is just implementing an NDFA (non-deterministic finite automata) system. NDFAs are confusing when written like this. Realising that this is just an NDFA implementation leads to better patterns being implemented if you know how to work with finite automata systems.

  • @thedacian123
    @thedacian123 5 місяців тому +1

    In which dot net version/C#version we have Option?Thanks!

    • @CodeOpinion
      @CodeOpinion  5 місяців тому +2

      It's not built-in. It's a package called Optional.

  • @Computerix
    @Computerix 5 місяців тому

    Very nice solution

  • @icymaru00yue
    @icymaru00yue 5 місяців тому

    This is a cool approach

  • @CottidaeSEA
    @CottidaeSEA 5 місяців тому +1

    I'd probably just slap a bool on the product which is set on creation or add a method to determine it. Pre-compute as much as possible to simplify your business logic.

  • @OkeCodigo
    @OkeCodigo 5 місяців тому

    I would suggest considering the use of the specification pattern, possibly implemented via an extended method. This approach might enable us to replace the original logic more efficiently, which could be particularly advantageous for integrating new versions or customizations

    • @CodeOpinion
      @CodeOpinion  5 місяців тому

      Ya, good suggestion as another option.

  • @moonbiscuit8742
    @moonbiscuit8742 5 місяців тому +2

    I don't see any problem with the enums. You may have a use-case where reasoning based on enums is the dead simple way, and another use-case where is painful. Context is king. I usually like to think like these are 2 different spaces acting on different dimensions, and what I like to do is provide extensions methods to pass from one space to another with my data, and solve the problem there. As you said once "there is no model to rule them all".
    Personally I liked the refactor, I see it like starting to evolve a separate read model.

    • @qj0n
      @qj0n 5 місяців тому

      There is a problem in the way many languages (more connected to OOP) implement enums. In functional languages you can build a pattern matching/switch case operation, where you specify behavior for all possible values of enum. If you ever modify this list, you'll get at least warning or compile error
      In C#, you may have any numeric value in enum, so you should add default or catch-all clause. But then, if you add a new value to enum, everything compiles fine, the error will be in runtime.

    • @moonbiscuit8742
      @moonbiscuit8742 5 місяців тому

      ​@@qj0nyep agree, also hate this aspect

  • @JohnMarsing
    @JohnMarsing 5 місяців тому

    I like Ardalis SmartEnum because, among other things it can centralize code related to the enums

  • @mo_zed
    @mo_zed 5 місяців тому +1

    What about composition over inheritance?

    • @CodeOpinion
      @CodeOpinion  5 місяців тому

      Sure, you could compose the product and provide it with a Downloadable type that has those values, but ultimately whatever is using the Product needs to decide if its going to use that property (eg, null check).

  • @BlindVirtuoso
    @BlindVirtuoso 5 місяців тому

    My main suggestion is Martin Fowler's Refactoring book.

  • @OEMPlus
    @OEMPlus 5 місяців тому +2

    i'm only like halfway in so maybe you cover this later but i'd make a product base class and then if i need to extend that i would just have a DownloadableProduct inherit from product. this would satisfy the business needs while allowing for the digital download functionality.

    • @OEMPlus
      @OEMPlus 5 місяців тому +1

      aaah there it is 7 minutes in. haha

    • @CodeOpinion
      @CodeOpinion  5 місяців тому +1

      you got it!

  • @ibrahimhussain3248
    @ibrahimhussain3248 5 місяців тому

    If we dont know how many or how many types of products will be in system during compile time then the database lookup is better. Otherwise you are have to update your code as soon as you add another product which is not scalable

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

    Hi. I would introduce IDownloadable and check if a type is Idownlaodable and call the interface specific methods. Inheritance bad beast from my point of view

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

      you can do this in C# but not in every language if you want to implement multiple interfaces

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

      you can instead use an object inside product as a behaviour that eventually returns null as the implementation of the interface

  • @Endomorphism
    @Endomorphism 5 місяців тому

    This is very close to the type deriven development

  • @GnomeEU
    @GnomeEU 5 місяців тому +2

    Enums are great and lightweight. An enum is just an integer w a name. It would be cool sometimes if you could add properties to Enums like in java I think. But then they might be too heavy.

    • @tree267
      @tree267 5 місяців тому

      Take a look at Ardalis SmartEnum

    • @CodeOpinion
      @CodeOpinion  5 місяців тому +1

      I think that's why people tend to favor them instead of a class/type for the simple use and definition. They aren't the problem, it's more so as mentioned using them along with conditionals everywhere.

    • @GnomeEU
      @GnomeEU 5 місяців тому

      @@tree267 this doesn't seem to solve the first problem i see: public static readonly TestEnum One = new TestEnum(nameof(One), 1);
      You already have to pass the name as the first argument, i don't need a library to do that :D
      That's the convenience of enums, that you don't have to write the name "One" two times etc...
      I would need string enums, but something better than the class approach. We have product numbers that are like this. "002345". I would need enums for those. I used the static readonly class approach but im not 100% happy.

    • @tree267
      @tree267 5 місяців тому

      @@GnomeEU well I think that if you get into the Smart Enum a bit more then you would start to see the benefits. You’ve highlighted the declaration of a Smart Enum. The pain in using Enums isn’t in the declaration, but in the usage and the limitations therein. With Smart Enum you have a class, with all the benefits that entails (the ability to use methods properties etc to add behaviour), that can be used much like an enum. If you don’t like the package then there’s not much to rolling your own. Good luck!

  • @kosasigwalo1895
    @kosasigwalo1895 5 місяців тому

    baah put'em all in a database and just pass a query to it done 😝

  • @frankhaugen
    @frankhaugen 5 місяців тому +1

    Wut? Is he arguing for restructuring 10 000 000 enum based database records into that mess? Have he heard about DTOs? Don't know his skill level but he's either next level or junior... Of course some people refuse to have enums and will rather have 5-6 columns for status flags rather than one status enum, and handle the mess of something is active, deleted, restored and paused at the same time... 😢

  • @stochastic84
    @stochastic84 5 місяців тому +1

    You aren't separating out the conditional by moving to inheritance based polymorphism. You are just making it a lot harder to follow since now you have to search a tree of virtual overrides to find out which method (which could change at runtime) handles that particular condition. No thanks. I'll take inspiration from functional programming and pattern matching instead.

  • @Forshen
    @Forshen 5 місяців тому +2

    I don't like either solution. The ProductType should not be an enum, but a Entity. in the current scenario the type has different outcomes based on what it is. This would be so annoying if more types are coming to the application and the codebase just gets more junk.
    Here is what i would do: the database should have a ProductType table and we should create a ProductType Entity. The database / entity should hold some simple setting(s). Is this case we need to know if the type is Downloadable. So the database has a column with a bool value for if its downloadable or not. We when we get the ProductType from the database we can just check code wise of its type is downloadable. Now the code can stay the same, but more ProductTypes can be created.

    • @majormartintibor
      @majormartintibor 5 місяців тому

      In code you don't need the ProductType's ID, you don't care. Two ProductTypes are equal if their value (the type) is equal, so in my eyes that is not an Entity but a Value Object. But that is just nit picking I admit. :)

    • @Forshen
      @Forshen 5 місяців тому

      @@majormartintibor object value has maybe 2-3 values it doesnt have to be unique, i want my product type to be unique. ProductType can have a lot of setting / values

    • @CodeOpinion
      @CodeOpinion  5 місяців тому +2

      Sure, you're suggesting to move some of this to runtime (database query) and then have a type that has a column/property you'll write the conditional against. Basically a combination of a couple of the solutions. That works, but as mentioned in the video, having that I/O call to the DB might not be great idea depending on your context.

    • @Forshen
      @Forshen 5 місяців тому +1

      @@CodeOpinion you already have a product, so you already did a call to the database, in the same call you can also get the productType info and have it in memory (and maybe map the single db record to entities). I don't see a problem here. Can you elaborate what the downside is of getting more data in the same call you get the product?

  • @damiannizio4039
    @damiannizio4039 5 місяців тому +1

    How about a property that would return the check if the item is downloadable? Product.IsDownloadable => Type.Course || Type.Ebook?
    We could call it everywhere we want and actually modify it once in it's class

    • @CodeOpinion
      @CodeOpinion  5 місяців тому

      Also another simple option, or as an extension method.