将大型方法分解为较小的方法时,适当的访问级别实践?
所以我是一个相对较新的程序员,仍在学习,并且遇到了一些问题(所以如果我的标签有点偏离,我深表歉意)。早些时候,我有机会阅读一篇文章,讨论将包含大量代码的方法分解为对许多较小的、名称明确的方法的调用集的优点。总的来说,我觉得这使得代码看起来更干净,并且确实使单元测试变得更加容易。然而,我对是否将所有这些新方法公开还是私有有些担心。将它们设为私有似乎是正确的做法,因为其余代码通常不需要访问这些方法。然而,单元测试私有方法可能会很混乱。这有最佳实践吗?
我现在正在做的事情:
public class WashingMachine {
public Load wash(Load load) {
// Removes one sock from load
for (ClothingItem item : load.getItems()) {
if (item.getType().equalsIgnoreCase("sock") {
load.removeItem(item);
.. // logic for sending sock to proper dimension
break;
}
}
// rest of logic for cleaning clothes
}
}
变成:
public class WashingMachine {
// Wash a load of laundry
public Load wash(Load load) {
// Removes one sock from load
removeSock(load.getItems());
// rest of logic for cleaning clothes
..
}
// Oh no, I can't unit test this easily!
private void removeSock(List<ClothingItem> items) {
...
}
}
So I am a relatively new programmer and am still learning, and have run into a bit of an issue (so apologies if my tags are a little off). Early on I had a chance to read an article discussing the advantages of breaking up methods that contained lots of code into sets of calls to many smaller, clearly-named methods. In general I felt like this made cleaner looking code, and definitely made unit testing a lot easier. However, I have some concerns about whether to make all these new methods public or private. Making them private seems like the right thing to do, since the rest of the code generally doesn't need access to these methods. However, unit testing private methods can be messy. Is there a best practice for this?
What I'm doing right now:
public class WashingMachine {
public Load wash(Load load) {
// Removes one sock from load
for (ClothingItem item : load.getItems()) {
if (item.getType().equalsIgnoreCase("sock") {
load.removeItem(item);
.. // logic for sending sock to proper dimension
break;
}
}
// rest of logic for cleaning clothes
}
}
Turns into:
public class WashingMachine {
// Wash a load of laundry
public Load wash(Load load) {
// Removes one sock from load
removeSock(load.getItems());
// rest of logic for cleaning clothes
..
}
// Oh no, I can't unit test this easily!
private void removeSock(List<ClothingItem> items) {
...
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
将您的公共方法视为您希望允许任何人使用的功能。
其中一些功能可能很复杂,您是对的,该功能应该分为不同的方法。问题是,“他们有什么样的访问权限?”
好吧,如果它们是公共的,那么现在外部类可以访问您的内部功能,而这是您真正不想要的。即使它不会破坏任何东西,当您开始处理大型项目(与更大的团队)时,通过将其公开,其他程序员可能会使用该功能,这现在会阻止您更改它。
例如,以汽车为例。假设您的方法是
accelerate()
。这可能会调用releaseGas()。但是,您不希望外部任何人释放气体。仅应在受控环境中调用它。然后是受保护的访问。受保护的访问适用于可能被扩展类覆盖的方法。这些应该是执行与内部功能相关的特定工作块的方法。再次以汽车为例,可能有一个 RaceCar 类使用特殊类型的气体,因此它希望提供自己的释放气体的方法。这将是
releaseGas
受到保护的一个原因。至于测试,您应该主要测试您的公共合约。这是其他类使用的内容,最后,这才是真正重要的。您的内部方法是您功能的内部方法,并将通过测试您的公共合约来进行测试。这也意味着您的类应该首先根据其外部用途进行设计,然后围绕它构建测试。 (即使使用测试驱动的开发,随着您获得经验,这也会变得更容易。)
当然,有时这些内部方法足够复杂,以至于它们需要自己的单元测试。但是,您不需要保护它们。在这种情况下,您可以将它们设置为默认访问权限。 (既不是公开的也不是受保护的)。只要您的单元测试位于同一个包中,您就可以调用它们。
更好的是,如果您知道不希望任何人扩展它,但需要对其进行测试,请将其设置为
protected final
或只是final
。这仍然允许调用它,但至少可以防止它被覆盖。编辑:
瑞安下面的评论是正确的。如果您的类变得足够复杂,需要测试其内部方法,那么可能应该将这些方法提取到它们自己的类中,然后可以对其进行独立的单元测试。
总的来说,您的测试应该测试各个单元的公共合同。
Think of your public methods as functionality you want to allow anybody to use.
Some of this functionality may be complicated, and you're right, that functionality should be split up into different methods. The question is, "What kind of access do they have?"
Well, if they're public, now external classes can access your internal functionality, which you really don't want. Even if it won't break anything, when you start working on large projects (with larger teams of people), by making it public, other programmers might use that functionality, which now prevents you from changing it.
For example, taking a car analogy. Say your method is
accelerate()
. This will probably callreleaseGas()
. However, you don't want anybody externally to release gas. It should only be called in a controlled environment.Then there's protected access. Protected access is for methods that may be overridden by extending classes. These should be methods that do a specific chunk of work related to internal functionality. Taking the car example again, there might be a RaceCar clas which uses a special type of gas, and so it wants to provide its own method of releasing gas. This would be a reason for
releaseGas
to be protected.As for tests, you should be testing, primarily, your public contract. That's what other classes use, and at the end, that's what really matters. Your internal methods are internal to your functionality, and will be tested by way of testing your public contract. This also means your class should be designed based on its external uses first, and the tests built around that. (Even with test-driven development, this gets easier as you gain experience.)
Granted, sometimes those internal methods are complicated enough that they warrant their own unit tests. However, you don't need to make them protected. In that case, you can make them default access. (Neither public or protected). As long as your unit test is in the same package, you'll be able to call them.
Better yet, if you know that you don't want anyone to extend it but you need it to be tested, make it
protected final
or justfinal
. This still allows being able to call it, but at least prevents it being overridden.Edit:
Ryan's comment below is dead on. If your class is getting complex enough that it needs testing of its internal methods, those should probably be extracted into their own class, which can then be independently unit-tested.
Overall, your tests should be testing the public contract of your separate units.
我认为您应该根据将调用它的(主要的,而不是测试的)代码来设计您的类。如果没有人需要调用
WashingMachine.removeSock()
那么我会将其保留为私有
。至于单元测试,这当然意味着您无法在
WashingMachine
中测试这一单独的逻辑 - 但您仍然可以测试wash()
方法,如果这是使用WashingMachine
的其他组件所可见的全部内容,也是您真正需要关注测试的内容。编写的单元测试可能太且关注细节。
I think you should design your class based on the (main, not test) code that will be calling it. If no one has a need to ever call
WashingMachine.removeSock()
then I would leave itprivate
.As for the unit test, sure this means you can't test this one individual piece of logic within
WashingMachine
- but you can still test thewash()
method, which if this is all that the other components usingWashingMachine
have visible, is all you really need to concern your test with.It is possible to write a unit test that is too granular and concerned with details.