多态替换if else
更好的软件设计 (BETTER SOFTWARE DESIGN)
Sure, if-else
and switch
makes for condense, simple code. But your software should not be comprised of the fewest lines possible, sacrificing readability, maintainability, or flexibility.
当然, if-else
和switch
可以生成简洁的代码。 但是,您的软件不应包含最少的内容,以免降低可读性,可维护性或灵活性。
I see a lot of branching happening over enums, or, other discrete values. Some developers even get aggravated when told not to use if-then-else
.
我看到在枚举或其他离散值上发生了很多分支。 当被告知不要使用if-then-else
时,某些开发人员甚至会感到恼火。
But, what do you think the consequences of using enums in your if-then-else
statement are?
但是,您认为在if-then-else
语句中使用枚举的后果是什么?
Branching on discrete values makes your software difficult to change. Every new feature requires you to track down where the branching is happening, and modify your existing code accordingly.
在离散值上分支会使您的软件难以更改。 每个新功能都要求您跟踪分支发生的位置,并相应地修改现有代码。
This is definitely not how we want to develop great software. It’s perhaps a perfect first step towards making your code work. But, as you progress to making your code better, switch
and if-then-else
must be long gone.
这绝对不是我们想要开发出色软件的方式。 这可能是使代码正常工作的完美第一步。 但是,随着您逐步改进代码 , switch
和if-then-else
必须早已不复存在。
The traditional approach to branching using if-else and switch is obsolete. It’s not SOLID. It’s not flexible.
使用if-else和switch进行分支的传统方法已过时。 它不是固体。 它不灵活。
It’s just horrific.
太恐怖了。
There’s surely nothing Object-Oriented about the traditional approach. But yet, it’s flourishing. Which makes sense, since students are beaten to thinking it’s correct, or even, a best practice.
传统方法肯定没有面向对象的。 但是,它正在蓬勃发展。 这是有道理的,因为学生被殴打认为这是正确的,甚至是最佳实践。
Sure, the code is functioning, but you know you can do much better. The goal should be to implement a new requirement by creating a new class.
当然,代码正在运行,但是您知道可以做得更好。 目标应该是通过创建新类来实现新要求。
让我想象一下这个问题。 (Let me visualize the issue.)
Say you have to implement a way to update users, given some reason. For simplicity, a user only has two reasons to be updated in your system.
假设出于某种原因,您必须实施一种更新用户的方法。 为简单起见,用户只有两个要在系统中进行更新的原因。
You implement these two simple cases in the following code snippet.
您可以在以下代码段中实现这两种简单的情况。
Take a second to read this poorly designed code. Lots of senior developers are without a doubt having nightmares over this and might even be considered a PTSD-trigger.
花一点时间阅读这段设计欠佳的代码。 毫无疑问,许多高级开发人员对此都有噩梦,甚至可能被认为是PTSD触发。
Yes, I’ve seen crazy code like this in the wild. It’s an incredibly naive implementation that assumes there’ll never be more reasons for a user to change.
是的,我在野外看到过如此疯狂的代码。 这是一个非常幼稚的实现,它假定用户再也没有理由进行更改了。
The only good thing to say about this code is the attempt to pull off a semi CQS-ish design pattern.
关于此代码的唯一要说的好事是尝试实现一种半CQS式的设计模式。
If your inclination is to say “that should be a switch!” you should take a moment to reflect on what’s important in software development. Switch
over if-else
is completely irrelevant.
如果您倾向于说“那应该是一个开关!” 您应该花点时间思考一下软件开发中的重要内容。 if-else
Switch
完全无关紧要。
您会受到新要求的打击。 每时每刻。 (You’ll get hit by new requirements. All the time.)
New requirements come along. Who would have thought? You were so sure nothing would happen.
新的要求随之而来。 谁曾想到? 您确定不会发生任何事情。
Say your requirements now look like this.
说您的要求现在看起来像这样。
The question is, are you really going to implement these two new reasons to update a user by adding additional enum values and slapping in two extra else-if
?
问题是,您是否真的要通过添加其他枚举值并拍打两个else-if
来实现这两个新原因来更新用户?
Here’s what it’d look like if you decided to go down the wrong path. This code doesn’t read well.
如果您决定走错路,这就是它的样子。 这段代码读得不好。
This kind of implementation is essentially a poor man’s polymorphism.
这种实现本质上是穷人的多态性。
Besides continuously having to add additional branches — which is a questionable practice by itself — whenever you have to debug or carry out a bugfix, you’ll do so surrounded by entirely irrelevant code.
除了必须不断添加其他分支(这本身就是一个可疑的做法)之外,每当您不得不调试或执行错误修正时,您都将被完全不相关的代码所包围。
There’s one more issue. This method signature is lying to us as it’s not just updating a user. It’s also picking which algorithm to execute based on the update reason, and it even knows each implementation. It must be obvious to everyone by now this method is having a myriad of responsibilities.
还有一个问题。 这种方法签名对我们来说是骗人的,因为它不仅在更新用户。 它还根据更新原因选择要执行的算法,甚至知道每个实现。 现在,对于每个人来说,显而易见,这种方法承担着无数的责任。
I’m sure this example solidifies everything terrible about if-else
and switch
.
我敢肯定,这个示例巩固了if-else
和switch
所有可怕问题。
Let’s take a look at how you can avoid nasty approaches like this.
让我们看一下如何避免这种讨厌的方法。
重构为多态执行非常容易。 (Refactoring to polymorphic execution is astoundingly easy.)
You’ll be refactoring the messy branching-based code into cohesive, simple classes, that align well with the actual requirements.
您将把凌乱的基于分支的代码重构为具有凝聚力的简单类,这些类与实际需求非常吻合。
Before anyone shrieks in horror about using classes, let me just clear one thing up. The cost of instantiating new classes is often neglectable. Don’t try to optimize your code before it’s a proven bottleneck.
在有人惊恐使用类之前,让我先澄清一件事。 实例化新类的成本通常可以忽略不计。 在遇到瓶颈之前,请不要尝试优化代码。
Okay, let’s carry on.
好吧,继续吧。
I mentioned earlier we can do much better. By better, I mean writing code that is 1) readable, 2) maintainable, and 3) flexible.
我之前提到过,我们可以做得更好。 更好的是,我的意思是编写以下代码:1)可读性,2)可维护性和3)灵活。
By replacing traditional branching with polymorphic execution, there’ a clear relationship between the class and the requirement it manages. Simple, highly cohesive classes with clear responsibilities are easy to maintain. Detecting and correcting defects becomes a breeze.
通过用多态执行代替传统的分支,类与它管理的需求之间存在明确的关系。 具有明确职责的简单,高度凝聚力的课程易于维护。 检测和纠正缺陷变得轻而易举。
On top of that, your software easily accommodates new features, without having to modify an existing class.
最重要的是,您的软件可以轻松容纳新功能,而无需修改现有类。
让重构开始 (Let the refactoring begin)
You’ll see just how far we can get without a single if-then-else
or switch
.
您将看到,如果没有一个if-then-else
或switch
我们可以走多远。
The UpdateAsync(Reason, User)
has now become this simple.
UpdateAsync(Reason, User)
现在变得如此简单。
Notice how you’re now taking an interface argument instead of the enum. Now, the method delegates the responsibility of knowing how to perform the update to a specialized object.
请注意,您现在如何使用接口参数而不是枚举。 现在,该方法将了解如何执行更新的职责委托给专门的对象。
The IUpdateReason
concrete implementations look like this. I leave details about constructor arguments and method implementation to your imagination.
IUpdateReason
具体实现如下所示。 我将有关构造函数参数和方法实现的细节留给您的想象。
Every class is completely aligned with the requirement it manages. Debugging, fixing bugs, and testing is now immensely easier than compared to the awful obsolete approach.
每个类都完全符合其管理的要求。 与可怕的过时方法相比,调试,修复错误和测试现在要容易得多。
In this case, any new requirement results in a specialized class.
在这种情况下,任何新要求都会导致一个专门的类。
We can easily stop here and call it a day. You refactored the nasty branching out and replaced it with polymorphism. Your code is now object-oriented and incredibly easy to maintain. Great job.
我们可以轻松地停在这里并称之为一天。 您重构了讨厌的分支,并将其替换为多态。 您的代码现在是面向对象的,并且易于维护。 很好。
但是,还有最后一个问题。 (But, there’s a final issue.)
Your UpdateAsync(Reason, User)
is somewhat redundant now.
您的UpdateAsync(Reason, User)
现在有些多余。
To solve this, we’re no longer doing refactoring. We’re moving into re-designing parts of the system.
为了解决这个问题,我们不再进行重构。 我们正在重新设计系统的各个部分。
It makes sense to create command objects and command handlers in this case. It’ll simplify the calling code, as it’d just dispatch a command like UpdateUserAddress
and a corresponding handler's action would be invoked.
在这种情况下,创建命令对象和命令处理程序是有意义的。 这将简化调用代码,因为它只调度了诸如UpdateUserAddress
类的命令,并且将调用相应的处理程序的动作。
I have an article published in the JetBrains June 2020 .NET newsletter about precisely this. Here’s the link below.
我在JetBrains 2020年6月.NET新闻中发表了一篇有关此内容的文章。 这是下面的链接。
您的主要外卖。 (Your main takeaway.)
In summary, traditional branching is often a student’s tool until a more suitable, polymorphic approach is discovered.
总之,在发现更合适的多态方法之前,传统的分支通常是学生的工具。
if-then-else
and switch
makes your code significantly more difficult to read, maintain, and adjust.
if-then-else
和switch
会使您的代码更加难以阅读,维护和调整。
The next time you’re amid implementing a feature using traditional multiway branching, take moment and analyze how you can leverage polymorphism and modern approaches.
下次您要使用传统的多路分支来实现功能时,请花点时间并分析如何利用多态性和现代方法。
Resources for the curious
-------------------------Making Your C# More Object-Oriented by Zoran HorvatCreating Highly Configurable Code in Three Simple Steps by the Author
翻译自: https://levelup.gitconnected.com/if-else-is-a-poor-mans-polymorphism-ab0b333b7265
多态替换if else