Answers:
有两种常见的静态方法:
“不安全”静态变量有几种常见用法(例如,在Singleton模式中),但请注意,尽管您称呼它们为漂亮名字,但您只是在对全局变量进行突变。在使用不安全的静电之前,请仔细考虑。
这实际上只是John Millikin的出色回答的后续措施。
尽管将无状态方法(几乎是函数)静态化是安全的,但有时可能导致难以修改的耦合。考虑一下您有一个静态方法:
public class StaticClassVersionOne {
public static void doSomeFunkyThing(int arg);
}
您称之为:
StaticClassVersionOne.doSomeFunkyThing(42);
一切都很好,也很方便,直到遇到必须修改静态方法的行为并发现与它紧密联系的情况StaticClassVersionOne
。可能您可以修改代码,这会很好,但是如果还有其他调用者依赖于旧的行为,则需要在方法的主体中加以考虑。在某些情况下,如果该方法主体试图平衡所有这些行为,则可能会变得难看或难以维护。如果拆分方法,则可能必须在几个地方修改代码以考虑到它,或者调用新类。
但是考虑一下您是否已经创建了一个接口来提供该方法,并将其提供给调用方,现在当必须更改行为时,可以创建一个新类来实现该接口,该类更干净,更易于测试并且更易于维护,而是提供给呼叫者。在这种情况下,不需要更改甚至不需要重新编译调用类,并且更改已本地化。
可能出现或可能不会出现这种情况,但我认为值得考虑。
静态类只要在正确的位置使用它们就可以。
即:作为“叶”方法的方法(它们不修改状态,它们只是以某种方式转换输入)。诸如Path.Combine之类的例子就是很好的例子。这些事情很有用,有助于简化语法。
该问题我有静有很多:
首先,如果您有静态类,则依赖项将被隐藏。考虑以下:
public static class ResourceLoader
{
public static void Init(string _rootPath) { ... etc. }
public static void GetResource(string _resourceName) { ... etc. }
public static void Quit() { ... etc. }
}
public static class TextureManager
{
private static Dictionary<string, Texture> m_textures;
public static Init(IEnumerable<GraphicsFormat> _formats)
{
m_textures = new Dictionary<string, Texture>();
foreach(var graphicsFormat in _formats)
{
// do something to create loading classes for all
// supported formats or some other contrived example!
}
}
public static Texture GetTexture(string _path)
{
if(m_textures.ContainsKey(_path))
return m_textures[_path];
// How do we know that ResourceLoader is valid at this point?
var texture = ResourceLoader.LoadResource(_path);
m_textures.Add(_path, texture);
return texture;
}
public static Quit() { ... cleanup code }
}
查看TextureManager,您无法通过查看构造函数来判断必须执行哪些初始化步骤。您必须深入研究该类,以找到其依赖关系并以正确的顺序初始化事物。在这种情况下,它需要在运行之前初始化ResourceLoader。现在扩大这种依赖的噩梦,您可能可以猜测会发生什么。想象一下,在没有明确的初始化顺序的情况下尝试维护代码。将其与实例的依赖项注入进行对比-在这种情况下,如果不满足依赖项,则代码甚至都不会编译!
此外,如果您使用修改状态的静态变量,那就像是纸牌屋。您永远都不知道谁可以访问什么,而且设计趋向于像意大利面条怪物。
最后,同样重要的是,使用静态方法会将程序与特定的实现联系在一起。静态代码是可测试性设计的对立面。测试充满静电的代码是一场噩梦。静态调用永远不能交换为测试双重测试(除非您使用专门设计用于模拟静态类型的测试框架),因此静态系统会使使用它的所有内容都成为即时集成测试。
简而言之,在某些情况下,静态方法适用于小型工具或一次性代码,我不会阻止使用它们。然而,除此之外,它们对于可维护性,良好的设计和易于测试来说是一场血腥的噩梦。
这是一篇有关问题的好文章:http : //gamearchitect.net/2008/09/13/an-anatomy-of-despair-managers-and-contexts/
这似乎是一种合理的方法。您不想使用太多静态类/方法的原因是,最终您将不再使用面向对象的编程,而是更多地进入结构化编程领域。
在您只是将A转换为B的情况下,请说我们要做的就是将文本转换为
"hello" =>(transform)=> "<b>Hello!</b>"
这样,静态方法就有意义了。
但是,如果您经常在对象上调用这些静态方法,并且它对于许多调用而言往往是唯一的(例如,使用它的方式取决于输入),或者它是对象固有行为的一部分,那么它将使它成为对象的一部分并保持其状态是明智的。一种实现方法是将其实现为接口。
class Interface{
method toHtml(){
return transformed string (e.g. "<b>Hello!</b>")
}
method toConsole(){
return transformed string (e.g. "printf Hello!")
}
}
class Object implements Interface {
mystring = "hello"
//the implementations of the interface would yield the necessary
//functionality, and it is reusable across the board since it
//is an interface so... you can make it specific to the object
method toHtml()
method toConsole()
}
编辑:大量使用静态方法的一个很好的例子是Asp.Net MVC或Ruby中的html helper方法。它们创建的HTML元素与对象的行为无关,因此是静态的。
编辑2:将函数式编程更改为结构化编程(出于某种原因,我感到困惑),这是Torsten指出的支持。
如果这是一种实用程序方法,则最好将其设为静态。Guava和Apache Commons是基于此原理构建的。
我对此的看法纯粹是务实的。如果是您的应用程序代码,则静态方法通常不是最好的选择。静态方法具有严格的单元测试限制-无法轻松模拟它们:您不能将模拟的静态功能注入其他测试中。您通常也不能将功能注入静态方法中。
因此,在我的应用程序逻辑中,我通常有一些小型的类似于静态实用程序的方法调用。即
static cutNotNull(String s, int length){
return s == null ? null : s.substring(0, length);
}
好处之一是我不测试这种方法:-)
好吧,当然没有灵丹妙药。对于小工具/助手来说,静态类是可以的。但是使用静态方法进行业务逻辑编程肯定是邪恶的。考虑以下代码
public class BusinessService
{
public Guid CreateItem(Item newItem, Guid userID, Guid ownerID)
{
var newItemId = itemsRepository.Create(createItem, userID, ownerID);
**var searchItem = ItemsProcessor.SplitItem(newItem);**
searchRepository.Add(searchItem);
return newItemId;
}
}
您看到ItemsProcessor.SplitItem(newItem);
对它的静态方法调用
BusinessService
将其与隔离ItemsProcessor
(大多数测试工具不会模拟静态类),并且这使得无法进行单元测试。没有单元测试==低质量