如何在C#静态和非静态方法之间做出决定?

Inn*_*nno 14 c# oop class-design

[编辑]

我的原始问题是"为什么要在静态和非静态之间做出决定?两者都是一样的......"

不幸的是,它被编辑成一个C#特定的问题,我真的想避免.

所以,让我做一些补充:

当我说接口时,我不是指C#-keyword接口,而是我理解的东西,比如C++接口:一组定义良好的函数来操作我的对象.当说削弱我的界面时,我的意思是我有不同的功能(静态/非静态)做同样的事情.当有不同的功能来做同样的事情时,我的界面不再明确定义.

所以,正如看门人Bob发布的那样,我可以实现一个Validate()函数

Document.Validate(myDocumentObject);    
Run Code Online (Sandbox Code Playgroud)

但是也

myConcreteDocumentObject.Validate();
Run Code Online (Sandbox Code Playgroud)

要返回我的Copy() - 示例可以实现Copy()之类的

myConcreteDocument.Copy(toPath);
Run Code Online (Sandbox Code Playgroud)

但是也

Document.Copy(myConcreteDocumentObject, toPath)
Run Code Online (Sandbox Code Playgroud)

要么

Document.Copy(fromPath, toPath)
Run Code Online (Sandbox Code Playgroud)

当我想到一个文件夹,其中包含属于我的Document的所有文件(在这种情况下,我不依赖于具体的实例 - 但我依赖于其他东西:)).

一般来说,我说的是静态方法,而不是静态类(对不起,如果我忘了推荐).

但正如Anton Gogolev所说,我认为我的文档课不是一个很好的例子而且没有很好的设计,所以我想我必须看看单一责任原则.

我还可以实现某种与DocumentClass一起运行的ManagerClass:

例如:

myDocumentManagerObject.Copy(myConcreteDocumentObject, toPath);
Run Code Online (Sandbox Code Playgroud)

要么

myDocumentManagerObject.Copy(myConcreteDocumentObject, toPath);
Run Code Online (Sandbox Code Playgroud)

但是,如果我参考方法1)我倾向于创建自己执行任务的对象,而不是使用我的DocumentObject 执行某些操作的其他对象(DocumentManager).

(我希望这不会采取关于OOP的宗教讨论的方向;).)

[/编辑]


旧版:

起初这似乎是一个非常基本的问题,比如"何时使用静态方法,何时不使用",但这是我偶尔遇到的问题(我很难描述真正的问题是什么;也许只是为了得到原因(不)使用1)或为什么(不)使用2)).

(虽然我使用的是C#-Syntax但这不是C#限制的问题)

在OOP中,有两种处理对象的方法(以及其他方法):

1)如果我想要我的对象做某事,我只是告诉他这样做:

myConcreteObject.DoSomething();
Run Code Online (Sandbox Code Playgroud)

这就像和一个对象交谈一样.

2)或者如果你是静态方法的粉丝:

ObjectClass.JustDoIt();
Run Code Online (Sandbox Code Playgroud)

在某种程度上,我认为静态函数只是"感觉"更好.所以我倾向于经常使用静态方法(独立于具体实例 - 独立性总是好事).

所以,在设计课程时,我经常要决定是采用方法1)还是方法2):

想象一下,你有一个"文档"类,它应该代表一个应该保存到数据库中的文档:

一个文件

  • 由来自文件系统的一个或多个图像文件组成(这些文件成为单个文档页面)
  • 有类似参考书目的字段 - 用户可以添加有关文档的信息的字段 - 保存到额外的文件中
  • 并且应该有一些操作,如Copy(),AddPage(),RemovePage()等.

现在我遇到了几种创建这个类的方法:

//----- 1) non static approach/talking to objects -----
Document newDocument = new Document();

// Copy document to x (another database, for example)
newDocument.Copy(toPath);
Run Code Online (Sandbox Code Playgroud)

我喜欢这样:我告诉文档将自己复制到数据库x,对象就是这样做的.尼斯.

//----- 2) static approach ----------------------------
Document.Copy(myDocumentObject, toPath);
Run Code Online (Sandbox Code Playgroud)

为什么不?也不错,感觉非常方便......

那么,实施哪一个?都?或者将静态方法放到一种辅助类中?或者选择方法1)并坚持使用它不会削弱我的Document类的界面?

在考虑这两种方法时,我得出的结论是(在理论上)可以将任何函数实现为静态函数:

Class.Function(aConcreteClassObject, parameters);
Run Code Online (Sandbox Code Playgroud)

但也是非静态的:

aConcreteObject.DoSomething(parameters);
Run Code Online (Sandbox Code Playgroud)

举一个现实世界的例子:

[编辑(从路径添加参数"对不起,我忘了")]

//----- 2) static approach ----------------------------
File.Copy(fromPath, toPath);    // .Net-Framework-like
Run Code Online (Sandbox Code Playgroud)

[/编辑]

但是也:

//----- 1) non static approach ------------------------
ExampeFileClass fileObject = new ExampleFileClass();
fileObject.Copy(toPath);
Run Code Online (Sandbox Code Playgroud)

甚至(OOP-Overkill的种类):

//----- 1) non static approach, too -------------------
fileObject.ToPath = @"C:\Test\file.txt";     // property of fileObject
fileObject.Copy();                           // copy to toPath
Run Code Online (Sandbox Code Playgroud)

那么,为什么(不)使用1)或为什么(不)使用2)?

(我不会过多地关注Document类示例,因为它更像是一个关于良好类设计的一般性问题.)

Ant*_*lev 16

开始了.

首先:

所以我倾向于经常使用静态方法(独立于具体实例 - 独立性总是好事).

恰恰相反:使用静态方法时,您非常依赖于具体实例.

就你Document而言,我不会这样做.您已经列出了Document类的所有职责,包括数据聚合,将自身保存到数据库以及页面上的操作和复制.

这是很多的方式.根据SRP,每个"模块"(此处"模块"用作全能术语)应该只有一个原因需要改变.你Document有很多责任,因此它有很多改变的理由.这不好.

考虑到这一点,我会将所有逻辑移到其他具有严格定义职责的类中.我相信,Herb Sutter或Andrei Alexandrescu引入了或多或少可接受的移动标准,如下所示:所有可以通过公共合同与对象一起执行的操作(思考方法)应该移到外面有问题的对象.


  • 你是说一个班级应该没有公共方法吗? (2认同)

Gro*_*roo 9

您不能使用静态方法来实现接口,也不能覆盖静态方法.所以使用静态方法意味着你根本就不做OOP.

想想如何仅使用静态方法实现以下功能?

interface IDocument 
{
   void Print(IDevice targetDevice);
}

IDocument instance;

instance = new PdfDocument();
instance.Print(printer);

instance = new WordDocument();
instance.Print(printer);
Run Code Online (Sandbox Code Playgroud)


cgp*_*cgp 8

吻.如果你不必调用构造函数,那就更好了.

另外,一个静态方法应该告诉你一些函数如何运作:

  • 它不会对传递给它的变量进行操作.
  • 除了调用方法之外,它不需要任何内存(不计算从函数返回的内容)

还有一些需要注意的重要事项:

  • 某些实例中的静态方法(Java)无法被覆盖/子类化,因此它们更适合于不需要更改实现的情况.
  • 有些人认为静态方法本质上难以测试.

我也会参考这个帖子,以及一个简单的谷歌搜索,坦率地提供大量关于这个话题的讨论.


Ser*_*gio 6

我的"规则"是:

  • 如果我不需要使用我的类中的属性,请将其设置为静态.(换句话说,如果方法没有真正附加到类,只是用于逻辑关联,使用静态)