这个问题是主观的,但我只是好奇大多数程序员是如何处理这个问题的。下面的示例是伪C#,但这也适用于Java、C++和其他OOP语言。
无论如何,在我的类中编写助手方法时,我倾向于将它们声明为静态的,如果助手方法需要它们,只需传递字段。例如,给定下面的代码,我更喜欢使用方法调用#2。
class Foo
{
Bar _bar;
public void DoSomethingWithBar()
{
// Method Call #1.
DoSomethingWithBarImpl();
// Method Call #2.
DoSomethingWithBarImpl(_bar);
}
private void DoSomethingWithBarImpl()
{
_bar.DoSomething();
}
private static void DoSomethingWithBarImpl(Bar bar)
{
bar.DoSomething();
}
}我这么做的原因是,它表明(至少在我看来)助手方法可能对其他对象有副作用--即使没有读取它的实现。我发现我可以快速地摸索使用这种实践的方法,从而帮助我调试一些东西。
在您自己的代码中,您更喜欢做什么?您这样做的原因是什么?
发布于 2011-10-02 14:22:54
这要看情况了。如果您的助手操作的值是原语,那么静态方法是一个不错的选择,正如Péter所指出的。
如果它们很复杂,那么实心应用,更具体地说是S、我和D。
示例:
class CookieJar {
function takeCookies(count:Int):Array<Cookie> { ... }
function countCookies():Int { ... }
function ressuplyCookies(cookies:Array<Cookie>
... // lot of stuff we don't care about now
}
class CookieFan {
function getHunger():Float;
function eatCookies(cookies:Array<Cookie>):Smile { ... }
}
class OurHouse {
var jake:CookieFan;
var jane:CookieFan;
var cookies:CookieJar;
function makeEveryBodyAsHappyAsPossible():Void {
//perform a lot of operations on jake, jane and the cookies
}
public function cookieTime():Void {
makeEveryBodyAsHappyAsPossible();
}
}这跟你的问题有关。您可以使makeEveryBodyAsHappyAsPossible成为一个静态方法,它将接受必要的参数。另一种选择是:
interface CookieDistributor {
function distributeCookies(to:Array<CookieFan>):Array<Smile>;
}
class HappynessMaximizingDistributor implements CookieDistributor {
var jar:CookieJar;
function distributeCookies(to:Array<CookieFan>):Array<Smile> {
//put the logic of makeEveryBodyAsHappyAsPossible here
}
}
//and make a change here
class OurHouse {
var jake:CookieFan;
var jane:CookieFan;
var cookies:CookieDistributor;
public function cookieTime():Void {
cookies.distributeCookies([jake, jane]);
}
}现在,OurHouse不需要知道cookie分发规则的复杂性。它现在必须是一个对象,它实现了一个规则。实现被抽象成一个对象,对象的唯一责任是应用规则。这个对象可以单独进行测试。OurHouse只需使用对CookieDistributor的模拟即可进行测试。您可以很容易地决定更改cookie分发规则。
但是,要注意不要做得太过了。例如,让一个由30个类组成的复杂系统充当CookieDistributor的实现,其中每个类只完成一个很小的任务,这是没有意义的。我对SRP的解释是,它不仅规定每个类可能只有一个责任,而且还规定一个单独的责任应该由单个类来执行。
对于原语或像原语一样使用的对象(例如,表示空间中的点的对象、矩阵之类的对象),静态助手类有很多意义。如果您有选择,而且确实有意义,那么您可能实际上考虑向表示数据的类中添加一个方法,例如,Point有一个add方法是明智的。再说一次,别做得太过分。
因此,取决于你的问题,有不同的方式来解决它。
发布于 2011-10-02 13:45:22
声明实用程序类static的方法是众所周知的成语,因此这类类不需要实例化。遵循这个成语使您的代码更容易理解,这是一件好事。
但是,这种方法有一个严重的限制:这样的方法/类很难被模仿(尽管AFAIK至少对于C#来说是可以实现这一点的模拟框架,但它们并不常见,其中至少有一些是商业的)。因此,如果助手方法具有任何外部依赖项(例如DB),从而使其调用方难以进行单元测试,则最好将其声明为非静态的。这允许依赖注入,从而使方法的调用者更容易进行单元测试。
澄清:上面讨论了实用程序类,这些类只包含低级别的助手方法,并且(通常)不包含状态。有状态的非实用程序类中的助手方法是一个不同的问题;为错误地解释OP而道歉。
在这种情况下,我感觉到一种代码气味:A类的方法主要作用于B类的一个实例,实际上在B类中可能有一个更好的位置,但是如果我决定将它保持在原来的位置,我更喜欢选项1,因为它更简单,更容易阅读。
发布于 2011-10-02 14:08:48
在您自己的代码中,您更喜欢做什么?
我更喜欢#1 (this->DoSomethingWithBarImpl();),除非助手方法当然不需要访问实例的数据/实现static t_image OpenDefaultImage()。
你这样做的原因是什么?
公共接口和私有实现与成员是分开的。如果我选择#2和#1,我总是有成员和实例可用,那么程序将更难阅读。与许多基于面向对象的实现相比,我倾向于使用大量的封装,而且每个类的成员很少。至于副作用--我同意,通常会有状态验证来扩展依赖项(例如,需要通过的参数)。
#1的阅读、维护简单得多,并具有良好的性能特点。当然,在某些情况下,您可以共享实现:
static void ValidateImage(const t_image& image);
void validateImages() const {
ValidateImage(this->innerImage());
ValidateImage(this->outerImage());
}如果#2是代码库中的一个很好的通用解决方案(例如默认的),我会关心设计的结构:类做的太多了吗?是封装不够,还是重用不够?抽象级别足够高吗?
最后,如果您使用的是支持变异的面向对象语言(例如,const方法),那么#2看起来是多余的。
https://softwareengineering.stackexchange.com/questions/111938
复制相似问题