Enums aren't evil. Conditionals everywhere are

2024 ж. 24 Қаң.
16 517 Рет қаралды

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 KZhead
/ @codeopinion
📝 Blog: codeopinion.com
👋 Twitter: / codeopinion
✨ LinkedIn: / dcomartin
📧 Weekly Updates: mailchi.mp/63c7a0b3ff38/codeo...

Пікірлер
  • Want to learn more about software architecture and design? Join thousands of developers getting weekly updates! 🚀mailchi.mp/63c7a0b3ff38/codeopinion

    @CodeOpinion@CodeOpinion3 ай бұрын
  • 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.

    @marna_li@marna_li3 ай бұрын
    • We should be able to curiously question everything 🙂

      @DevLeader@DevLeader3 ай бұрын
    • this 100

      @majormartintibor@majormartintibor3 ай бұрын
    • @@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_li3 ай бұрын
    • @@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.

      @marna_li@marna_li3 ай бұрын
    • @@marna_li I agree with every word of yours.

      @majormartintibor@majormartintibor3 ай бұрын
  • 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 :)

    @CodeOpinion@CodeOpinion3 ай бұрын
  • 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.

    @user-ev9jg6ts6e@user-ev9jg6ts6e3 ай бұрын
    • Ya, lots of options.

      @CodeOpinion@CodeOpinion3 ай бұрын
  • 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

    @ravikumarmistry@ravikumarmistry3 ай бұрын
    • I often rather make things explicit (as in my one example) and use inheritance in very limited use cases, generally.

      @CodeOpinion@CodeOpinion3 ай бұрын
  • 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

    @georgehelyar@georgehelyar3 ай бұрын
    • Which would require a switch statement. Which is not necessarily used.

      @CodeOpinion@CodeOpinion3 ай бұрын
  • I quite like the idea of using an empty interface as a "trait" which can then be patternmatched

    @andrewthompson9714@andrewthompson97143 ай бұрын
    • Rust

      @vincentnthomas1@vincentnthomas13 ай бұрын
    • Rust btw

      @RiwenX@RiwenX2 ай бұрын
  • Thanks so much for sharing your perspectives on this Derek!

    @DevLeader@DevLeader3 ай бұрын
  • 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.

    @allinvanguard@allinvanguard3 ай бұрын
    • 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#.

      @majormartintibor@majormartintibor3 ай бұрын
  • 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

    @SkielCast@SkielCast3 ай бұрын
  • 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!" ;)

    @andersjuul8310@andersjuul83103 ай бұрын
  • 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@newaira3333 ай бұрын
    • 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.

      @newaira333@newaira3333 ай бұрын
    • This is mostly the motivation behind behind my original video 😁

      @DevLeader@DevLeader3 ай бұрын
    • 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?

      @CodeOpinion@CodeOpinion3 ай бұрын
    • 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.

      @Forshen@Forshen3 ай бұрын
    • ​@@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.

      @newaira333@newaira3333 ай бұрын
  • It's awesome you reacted this fast 😃

    @michaldivismusic@michaldivismusic3 ай бұрын
    • Nick pinged me before his video came out.

      @CodeOpinion@CodeOpinion3 ай бұрын
  • Yes, this is great video, thanks

    @Folsets@Folsets3 ай бұрын
  • In which dot net version/C#version we have Option?Thanks!

    @thedacian123@thedacian1233 ай бұрын
    • It's not built-in. It's a package called Optional.

      @CodeOpinion@CodeOpinion3 ай бұрын
  • 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.

    @TheGalantir@TheGalantir2 ай бұрын
  • 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.

    @moonbiscuit8742@moonbiscuit87423 ай бұрын
    • 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.

      @qj0n@qj0n3 ай бұрын
    • ​@@qj0nyep agree, also hate this aspect

      @moonbiscuit8742@moonbiscuit87423 ай бұрын
  • 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

    @OkeCodigo@OkeCodigo3 ай бұрын
    • Ya, good suggestion as another option.

      @CodeOpinion@CodeOpinion2 ай бұрын
  • 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@OEMPlus3 ай бұрын
    • aaah there it is 7 minutes in. haha

      @OEMPlus@OEMPlus3 ай бұрын
    • you got it!

      @CodeOpinion@CodeOpinion3 ай бұрын
  • @CodeOpinion how do you know that the object to pass to the handler is a downloadableproduct?

    @alessandrovangeli8395@alessandrovangeli839510 күн бұрын
  • This is a cool approach

    @icymaru00yue@icymaru00yue3 ай бұрын
  • Very nice solution

    @Computerix@Computerix3 ай бұрын
  • 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.

    @CottidaeSEA@CottidaeSEA3 ай бұрын
  • 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.

    @br3nto@br3nto3 ай бұрын
  • I like Ardalis SmartEnum because, among other things it can centralize code related to the enums

    @JohnMarsing@JohnMarsing3 ай бұрын
  • 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

    @ibrahimhussain3248@ibrahimhussain32483 ай бұрын
  • What about composition over inheritance?

    @mo_zed@mo_zed3 ай бұрын
    • 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).

      @CodeOpinion@CodeOpinion3 ай бұрын
  • My main suggestion is Martin Fowler's Refactoring book.

    @user-ev9jg6ts6e@user-ev9jg6ts6e3 ай бұрын
  • 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

    @maurosampietro9900@maurosampietro99003 ай бұрын
    • you can do this in C# but not in every language if you want to implement multiple interfaces

      @alessandrovangeli8395@alessandrovangeli839510 күн бұрын
    • you can instead use an object inside product as a behaviour that eventually returns null as the implementation of the interface

      @alessandrovangeli8395@alessandrovangeli839510 күн бұрын
  • 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.

    @GnomeEU@GnomeEU3 ай бұрын
    • Take a look at Ardalis SmartEnum

      @tree267@tree2673 ай бұрын
    • 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.

      @CodeOpinion@CodeOpinion3 ай бұрын
    • @@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.

      @GnomeEU@GnomeEU3 ай бұрын
    • @@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!

      @tree267@tree2673 ай бұрын
  • This is very close to the type deriven development

    @Endomorphism@Endomorphism3 ай бұрын
  • 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... 😢

    @frankhaugen@frankhaugen3 ай бұрын
  • baah put'em all in a database and just pass a query to it done 😝

    @kosasigwalo1895@kosasigwalo18953 ай бұрын
  • 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.

    @stochastic84@stochastic843 ай бұрын
  • 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.

    @Forshen@Forshen3 ай бұрын
    • 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. :)

      @majormartintibor@majormartintibor3 ай бұрын
    • @@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

      @Forshen@Forshen3 ай бұрын
    • 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.

      @CodeOpinion@CodeOpinion3 ай бұрын
    • @@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?

      @Forshen@Forshen3 ай бұрын
  • 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

    @damiannizio4039@damiannizio40393 ай бұрын
    • Also another simple option, or as an extension method.

      @CodeOpinion@CodeOpinion3 ай бұрын
KZhead