"方法可以设为静态" 可能隐藏了面向对象设计的缺陷






4.95/5 (18投票s)
"方法可以设为静态" - Microsoft 代码分析警告可能隐藏了对面向对象设计的违反
引言
在浏览代码库时,我遇到了一些代码示例,其中 Microsoft 代码分析会发出上述警告。虽然修复方法似乎很简单,但该警告本身可能隐藏着一个更微妙的问题,与对象责任分配有关。
玩具示例
让我们看一下以下代码
public class EmailConstructor
{
private const string Signature = "Regards";
public string Construct(User recipient, string body)
{
var builder = new StringBuilder();
builder.Append($"Hello {GetNiceUserName(recipient)}");
builder.Append(Environment.NewLine);
builder.Append(body);
builder.Append(Environment.NewLine);
builder.Append(Signature);
return builder.ToString();
}
private string GetNiceUserName(User user)
{
return $"{user.Name} {user.Surname}";
}
}
public class User
{
public string Name { get; set; }
public string Surname { get; set; }
}
确实,代码分析会发出警告。
特征羡慕
虽然乍一看可能一切都很明显,但实际上上面的代码是 特征羡慕 (Feature envy) 的一个例子。 作为一种经验法则,可以帮助你在无需使用 ReSharper 等工具的情况下发现这种情况,你可以使用 Craig Larman 在他的书“Applying UML and patterns”中提出的原则之一。
引用信息专家原则将引导你将责任放在拥有履行该责任所需的最完整信息的那一类上。
根据这个原则,你可以通过将方法移动到 User
类中来修复警告,因为 User
拥有更多信息来以某种方式表示他的名字。
public class EmailConstructor
{
private const string Signature = "Regards";
public string Construct(User recipient, string body)
{
var builder = new StringBuilder();
builder.Append($"Hello {recipient.GetNiceUserName()}");
builder.Append(Environment.NewLine);
builder.Append(body);
builder.Append(Environment.NewLine);
builder.Append(Signature);
return builder.ToString();
}
}
public class User
{
public string Name { get; set; }
public string Surname { get; set; }
public string GetNiceUserName()
{
return $"{Name} {Surname}";
}
}
高内聚
自然地,从上面的示例中会产生一个问题:“这会不会导致 User
类因承担过多责任而变得臃肿,例如,处理持久化存储?” 答案是,信息专家原则应该与高内聚原则一起使用。
引用高内聚通常用于支持低耦合。高内聚意味着给定元素中的责任密切相关且高度集中。将程序分解为类和子系统是增加系统内聚属性的活动示例。 另一方面,低内聚是指给定元素具有太多不相关的责任。 低内聚的元素通常难以理解、难以重用、难以维护且难以适应变化。
因此,如果我们想将我们的玩具代码扩展为与持久化存储交互,我们可能会提取高度内聚的函数来专门处理这些功能,并将其放入某种 工作单元 (Unit of Work) 模式中。
结论
Static
成员有一些缺点,例如使单元测试复杂化。 因此,应该谨慎考虑 static
。 Craig Larman 的 GRASP 模式与 SOLID 结合使用,使我们能够检查这些情况,以降低代码库的维护成本。
历史
- 2018 年 2 月 11 日:初始版本
- 2022 年 8 月 16 日:将 ReSharper 替换为 Microsoft 代码分析