将标志传递给方法的替代方法?
有时,当修复现有代码库中的缺陷时,我可能(通常出于懒惰)决定将方法从:更改
void
MyClass::foo(uint32_t aBar)
{
// Do something with aBar...
}
为:
void
MyClass::foo(uint32_t aBar, bool aSomeCondition)
{
if (aSomeCondition)
{
// Do something with aBar...
}
}
在代码审查期间,一位同事提到更好的方法是子类 MyClass 来提供这种专门的功能。
但是,我认为只要 aSomeCondition
不违反 MyClass
的目的或内聚力,它就是可以接受的使用模式。只有当代码中渗透了标志和 if 语句时,继承才是更好的选择,否则我们可能会进入架构宇航员的领域。
这里的转折点是什么?
注意:我刚刚看到此相关答案 这表明 enum
可能是更好的选择 选择而不是 bool
,但我认为我的问题在这种情况下仍然适用。
Sometimes when fixing a defect in an existing code base I might (often out of laziness) decide to change a method from:
void
MyClass::foo(uint32_t aBar)
{
// Do something with aBar...
}
to:
void
MyClass::foo(uint32_t aBar, bool aSomeCondition)
{
if (aSomeCondition)
{
// Do something with aBar...
}
}
During a code review a colleague mentioned that a better approach would be to sub-class MyClass
to provide this specialized functionality.
However, I would argue that as long as aSomeCondition
doesn't violate the purpose or cohesion of MyClass
it is an acceptable pattern to use. Only if the code became infiltrated with flags and if
statements would inheritance be a better option, otherwise we would be potentially be entering architecture astronaut territory.
What's the tipping point here?
Note: I just saw this related answer which suggests that an enum
may be a better
choice than a bool
, but I think my question still applies in this case.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
此类问题的解决方案不仅只有一种。
布尔的语义非常低。如果您想在将来添加新条件,则必须添加新参数...
经过四年的维护,你的方法可能有六个参数,如果这些参数都是布尔值,那么对于维护人员来说这是一个很好的陷阱。
如果情况是排他性的,Enum 是一个不错的选择。
枚举可以轻松迁移到位掩码或上下文对象。
位掩码:C++包含C语言,您可以使用一些简单的旧做法。有时,无符号整型上的位掩码是一个不错的选择(但您会失去类型检查),并且您可能会错误地传递不正确的掩码。这是从布尔值或枚举参数顺利过渡到这种模式的便捷方法。
可以通过一些努力将位掩码迁移到上下文对象。如果您必须保持构建时兼容性,您可能必须实现某种按位算术,例如
operator |
和operator &
。如果行为分裂很大并且该行为与实例的生命周期相关,继承有时是一个不错的选择。请注意,您还必须使用多态性,如果大量使用此方法,这可能会减慢该方法的速度。
最后,继承会导致所有工厂代码发生变化...如果您有几种以排他方式进行更改的方法,您会怎么做?你会让你的特定类的代码变得混乱......
事实上,我认为这通常不是一个好主意。
方法拆分:另一种解决方案是有时将方法拆分为多个私有方法并提供两个或多个公共方法。
上下文对象:C++ 和 C 缺少命名参数,可以通过添加上下文参数来绕过。我经常使用这种模式,特别是当我必须跨复杂框架的级别传递许多数据时。
笔记:
这对于最大限度地减少静态数据的访问(或使用)或对单例对象的查询也很有用,TLS ...
上下文对象可以包含更多与算法相关的缓存数据。
...
我让你的想象力自由发挥......
反模式
我在这里添加了几个反模式(以防止签名的某些更改):
*永远不要这样做*
There is not only one solution for this kind of problem.
Boolean has a very low semantic. If you want to add in the future a new condition you will have to add a new parameter...
After four years of maintenance your method may have half a dozen of parameters, if these parameters are all boolean it is very nice trap for maintainers.
Enum is a good choice if cases are exclusive.
Enums can be easily migrated to a bit-mask or a context object.
Bit mask : C++ includes C language, you can use some plain old practices. Sometime a bit mask on an unsigned int is a good choice (but you loose type checking) and you can pass by mistake an incorrect mask. It is a convenient way to move smoothly from a boolean or an enum argument to this kind of pattern.
Bit mask can be migrated with some effort to a context-object. You may have to implement some kind of bitwise arithmetics such as
operator |
andoperator &
if you have to keep a buildtime compatibility.Inheritence is sometime a good choice if the split of behavior is big and this behavior IS RELATED to the lifecycle of the instance. Note that you also have to use polymorphism and this is may slow down the method if this method is heavily used.
And finally inheritence induce change in all your factory code... And what will you do if you have several methods to change in an exclusive fashion ? You will clutter your code of specific classes...
In fact, I think that this generally not a very good idea.
Method split : Another solution is sometime to split the method in several private and provide two or more public methods.
Context object : C++ and C lack of named parameter can be bypassed by adding a context parameter. I use this pattern very often, especially when I have to pass many data across level of a complex framework.
Note:
That this is also useful to minimize access (or use) of static data or query to singleton object, TLS ...
Context object can contain a lot more of caching data related to an algorithm.
...
I let your imagination run free...
Anti patterns
I add here several anti pattern (to prevent some change of signature):
*NEVER DO THIS *
不幸的是,我认为这个问题没有明确的答案(这是我在自己的代码中经常遇到的问题)。使用 boolean:
调用很难理解。
使用枚举:
很容易理解,但您往往会得到这样的代码:
这很难说是一种改进。并且具有多个功能:
您最终会得到更多的代码。但我想这是最容易阅读的,也是我倾向于使用的,但我仍然不完全喜欢它:-(
然而,我绝对不会做的一件事是子类。继承应该是 您使用过的最后一个工具。
Unfortunately, I don't think there is a clear answer to the problem (and it's one I encounter quite frequently in my own code). With the boolean:
the call is hard to understand .
With an enum:
it is easy to understand but you tend to end up with code like this:
which is hardly an improvement. And with multiple functions:
you end up with a lot more code. But I guess this is the easiest to read, and what I'd tend to use, but I still don't completely like it :-(
One thing I definitely would NOT do however, is subclass. Inheritance should be the last tool you ever reach for.
主要问题是该标志是否影响类或该函数的行为。函数局部更改应该是参数,而不是子类。运行时继承应该是最后使用的工具之一。
The primary question is if the flag affects the behaviour of the class, or of that one function. Function-local changes should be parameters, not subclasses. Run-time inheritance should be one of the last tools reached for.
我使用的一般准则是:如果
aSomeCondition
以主要方式改变了函数的性质,那么我考虑子类化。与添加仅具有较小影响的标志相比,子类化是一项相对较大的工作。
一些示例:
当然,通过完全隐藏底层数据结构可能会更好地处理最后一个,但我假设您出于性能等原因希望能够选择其中之一。
The general guideline I use is: if
aSomeCondition
changes the nature of the function in a major way, then I consider subclassing.Subclassing is a relatively large effort compared to adding a flag that has only a minor effect.
Some examples:
Of course, that last one may be better handled by totally hiding the underlying data structure but I'm assuming here that you want to be able to select one of many, for reasons such as performance.
恕我直言,aSomeCondition 标志会更改或取决于当前实例的状态,因此,在某些条件下,此类应该更改其状态并以不同的方式处理提到的操作。在这种情况下,我可以建议使用状态模式。希望有帮助。
IMHO,
aSomeCondition
flag changes or depends on the state of current instance, therefore, under certain conditions this class should change its state and handle mentioned operation differently. In this case, I can suggest the usage of State Pattern. Hope it helps.我只需将 code: 更改
为:
我总是省略 bool 作为函数参数,而更喜欢放入结构中,即使我必须调用
myClass->enableCondition();
I would just change code:
to:
I always omit bool as function parameter and prefer to put into struct, even if I would have to call
myClass->enableCondition();