简体   繁体   English

在构造函数中进行冗长的操作会被认为是不好的设计吗?

[英]Is it considered bad design to do lengthy operations in a constructor?

I am implementing a class to compare directory trees (in C#). 我正在实现一个比较目录树的类(在C#中)。 At first I implemented the actual comparison in the class's constructor. 起初我在类的构造函数中实现了实际的比较。 Like this: 像这样:

DirectoryComparer c = new DirectoryComparer("C:\\Dir1", "C:\\Dir2");

But it doesn't feel "right" to do a possible lengthy operation in the constructor. 但是在构造函数中执行可能的冗长操作并不感觉“正确”。 An alternative way is to make the constructor private and add a static method like this: 另一种方法是将构造函数设为私有,并添加如下静态方法:

DirectoryComparer c = DirectoryComparer.Compare("C:\\Dir1", "C:\\Dir2");

What do you think? 你怎么看? Do you expect a constructor to be "quick"? 你期望构造函数“快速”吗? Is the second example better or is it just complicating the usage of the class? 第二个例子是更好的还是只是使类的使用复杂化?

BTW: BTW:

I wont mark any answer as accepted because I don't think there is a correct answer, just preference and taste. 我不会将任何答案标记为已被接受,因为我认为没有正确的答案,只有偏好和品味。

Edit: 编辑:

Just to clarify my example a little. 只是为了澄清我的例子。 I'm not only insterested if the directories differs, I'm also interested in how they differ (which files). 我不仅对目录不同感兴趣,我也对它们的区别(哪些文件)感兴趣。 So a simple int return value wont be enough. 所以一个简单的int返回值就不够了。 The answer by cdragon76.myopenid.com actually is pretty close to what I want (+1 to you). cdragon76.myopenid.com的答案实际上非常接近我想要的(+1给你)。

I would think a combination of the two is the "right" choice, as I would expect the Compare method to return the comparison result, not the comparer itself. 我认为两者的结合是“正确的”选择,因为我希望Compare方法返回比较结果,而不是比较器本身。

DirectoryComparer c = new DirectoryComparer();

int equality = c.Compare("C:\\Dir1", "C:\\Dir2");

...and as Dana mentions, there is an IComparer interface in .Net that reflects this pattern. ......正如Dana所提到的,.Net中有一个反映这种模式的IComparer接口。

The IComparer.Compare method returns an int since the use of IComparer classes is primarily with sorting. IComparer.Compare方法返回一个int,因为IComparer类的使用主要是使用排序。 The general pattern though fits the problem of the question in that: 一般模式虽然适合问题的问题:

  1. Constructor initializes an instance with (optionally) "configuring" parameters 构造函数使用(可选)“配置”参数初始化实例
  2. Compare method takes two "data" parameters, compares them and returns a "result" 比较方法采用两个“数据”参数,比较它们并返回“结果”

Now, the result can be an int, a bool, a collection of diffs. 现在,结果可以是int,bool,diff的集合。 Whatever fits the need. 无论什么适合需要。

I prefer the second one. 我更喜欢第二个。

I expect the constructor to instanciate the class. 我希望构造函数能够实现类。 The method compare does what it is designed to do. 方法比较实现了它的设计目的。

I think an interface might be what you're after. 我认为界面可能就是你所追求的。 I would create a class to represent a directory, and have that implement the DirectoryComparer interface. 我将创建一个表示目录的类,并实现DirectoryComparer接口。 That interface would include the compare method. 该接口将包括比较方法。 If C# already has a Comparable interface, you could also just implement that. 如果C#已经有一个Comparable接口,你也可以实现它。

In code, your call would be: 在代码中,您的电话将是:

D1 = new Directory("C:\");
..
D1.compare(D2);

You should never do anything that might fail in a constructor. 你永远不应该做任何可能在构造函数中失败的事情。 You don't want to ever create invalid objects. 您不希望创建无效对象。 While you could implement a "zombie" state where the object doesn't do much, it's much better to perform any complex logic in seperate methods. 虽然你可以实现一个“僵尸”状态,其中对象没有做太多,但在单独的方法中执行任何复杂的逻辑要好得多。

I agree with the general sentiment of not doing lengthy operations inside constructors. 我同意不在构造函数内部进行冗长操作的一般情绪。

Additionally, while on the subject of design, I'd consider changing your 2nd example so that the DirectoryComparer.Compare method returns something other than a DirectoryComparer object. 另外,在设计主题上,我会考虑更改第二个示例,以便DirectoryComparer.Compare方法返回DirectoryComparer对象以外的其他内容。 (Perhaps a new class called DirectoryDifferences or DirectoryComparisonResult .) An object of type DirectoryComparer sounds like an object you would use to compare directories as opposed to an object that represents the differences between a pair of directories. (也许是一个新的类名为DirectoryDifferencesDirectoryComparisonResult )类型的对象DirectoryComparer听起来像一个对象,你会用比较的目录,而不是代表一对目录之间的差异的对象。

Then if you want to define different ways of comparing directories (such as ignoring timestamps, readonly attributes, empty directories, etc.) you could make those parameters you pass to the DirectoryComparer class constructor. 然后,如果要定义比较目录的不同方法(例如忽略时间戳,只读属性,空目录等),可以将这些参数传递给DirectoryComparer类构造函数。 Or, if you always want DirectoryComparer to have the exact same rules for comparing directories, you could simply make DirectoryComparer a static class. 或者,如果您始终希望DirectoryComparer具有用于比较目录的完全相同的规则,则可以简单地使DirectoryComparer成为静态类。

For example: 例如:

DirectoryComparer comparer = new DirectoryComparer(
    DirectoryComparerOptions.IgnoreDirectoryAttributes
);
DirectoryComparerResult result = comparer.Compare("C:\\Dir1", "C:\\Dir2");

Yes, typically a constructor is something quick, it is designed to prepare the object for use, not to actually do operations. 是的,通常构造函数是快速的,它旨在准备对象以供使用,而不是实际操作。 I like your second option as it keeps it a one line operation. 我喜欢你的第二个选项,因为它保持一行操作。

You could also make it a bit easier by allowing the constructor to pass the two paths, then have a Compare() method that actually does the processing. 通过允许构造函数传递两个路径,然后使用实际进行处理的Compare()方法,您也可以使它更容易一些。

I like the second example because it explains what is exactly happening when you instantiate the object. 我喜欢第二个例子,因为它解释了实例化对象时究竟发生了什么。 Plus, I always use the constructor to initialize all of the global settings fro the class. 另外,我总是使用构造函数来初始化类的所有全局设置。

I think for a general purpose comparer you may on construction only want to specify the files you are comparing and then compare later- this way you can also implement extended logic: 我认为对于通用比较器,您可能只是想在构造中指定要比较的文件,然后再进行比较 - 这样您也可以实现扩展逻辑:

  • Compare again- what if the directories changed? 再次比较 - 如果目录改变了怎么办?
  • Change the files you are comparing by updating the members. 通过更新成员来更改您要比较的文件。

Also, you may want to consider in your implementation receiving messages from your OS when files have been changed in the target directories- and optionally recomparing again. 此外,您可能希望在实现中考虑在目标目录中更改文件时从操作系统接收消息,并可选择重新进行重新比较。

The point is- you are imposing limits by assuming that this class will only be used to compare once for a single instance of those files. 关键在于 - 假设此类仅用于对这些文件的单个实例进行一次比较,从而强加限制。

Therefore, I prefer: 因此,我更喜欢:

DirectoryComparer = new DirectoryComparer(&Dir1,&Dir2);

DirectoryComparer->Compare();

Or 要么

DirectoryComparer = new DirectoryComparer();

DirectoryComparer->Compare(&Dir1,&Dir2);

I think it's not only okay for a constructor to take as much time as needed to construct a valid object, but the constructor is required to do so. 我认为构造函数不仅可以根据需要花费尽可能多的时间来构造有效对象,但构造函数也需要这样做。 Deferring object creation is very bad as you end up with potentially invalid objects. 推迟创建对象非常糟糕,因为最终可能会出现无效对象。 So, you will have to check an object everytime before you touch it (this is how it is done in the MFC, you have bool IsValid() methods everywhere). 因此,每次触摸它之前都必须检查一个对象(这是在MFC中完成的,你到处都有bool IsValid()方法)。

I only see a slight difference in the two ways of creating the object. 我只看到创建对象的两种方式略有不同。 One can see the new operator as a static function of the class anyway. 无论如何,人们可以将新运算符看作是类的静态函数。 So, this all boils down to syntactic sugar. 所以,这一切都归结为语法糖。

What does the DirectoryComparer class do? DirectoryComparer类的功能是什么? What is it's responsibility? 它的责任是什么? From my point of view (which is a C++ programmer's view) it looks like you'd be better off with just using a free function, but I don't think that you can have free functions in C#, can you? 从我的观点来看(这是一个C ++程序员的观点),看起来你最好只使用免费功能,但我不认为你可以在C#中拥有免费功能,可以吗? I guess you will collect the files which are different in the DirectoryComparer object. 我猜你会收集DirectoryComparer对象中不同的文件。 If so, you could better create something like an array of files or an equivalent class that's named accordingly. 如果是这样,您可以更好地创建类似文件数组或相应命名的等效类。

If you are working with C#, you could use extension methods to create a method for comparing 2 directories that you would attach to the build in DirectoryClass, so it would look some thing like: 如果您正在使用C#,您可以使用扩展方法创建一个方法来比较您将附加到DirectoryClass中的构建的2个目录,因此它看起来像:

Directory dir1 = new Directory("C:\.....");
Directory dir2 = new Directory("D:\.....");

DirectoryCompare c = dir1.CompareTo(dir2);

This would be much clearer implementation. 这将是更清晰的实施。 More on extension methods here . 更多关于扩展方法的信息

If an operation may take an unknown amount of time, it is an operation you might want to export into a different thread (so your main thread won't block and can do other things, like showing a spinning progress indicator for example). 如果某个操作可能需要一段未知的时间,那么这是一个您可能想要导出到另一个线程的操作(因此您的主线程不会阻塞并可以执行其他操作,例如显示旋转进度指示器)。 Other apps may not want to do this, they may want everything within a single thread (eg those that have no UI). 其他应用可能不想这样做,他们可能想要一个线程内的所有内容(例如那些没有UI的内容)。 Moving object creation to a separate thread is a bit awkward IMHO. 将对象创建移动到单独的线程有点尴尬恕我直言。 I'd prefer to create the object (quickly) in my current thread and then just let a method of it run within another thread and once the method finished running, the other thread can die and I can grab the result of this method in my current thread by using another method of the object before dumping the object, since I'm happy as soon as I know the result (or keeping a copy if the result involves more details I may have to consume one at a time). 我更喜欢在我当前的线程中创建对象(快速),然后让它的方法在另一个线程中运行,一旦方法完成运行,另一个线程就可以死了,我可以在我的方法中获取此方法的结果当前线程通过在转储对象之前使用对象的另一个方法,因为一旦我知道结果我就很高兴(或者如果结果涉及更多细节则保留副本,我可能必须一次消耗一个)。

If the arguments are just going to be processed once then I don't think they belong as either constructor arguments or instance state. 如果参数只是被处理一次,那么我认为它们不属于构造函数参数或实例状态。

If however the comparison service is going to support some kind of suspendable algorithm or you want to notify listeners when the equality state of two directories changes based on filesystem events or something like that. 但是,如果比较服务将支持某种可挂起的算法,或者您希望在两个目录的相等状态根据文件系统事件或类似事件发生更改时通知侦听器。 Then ther directories is part of the instance state. 然后,目录是实例状态的一部分。

In neither case is the constructor doing any work other than initializing an instance. 在任何情况下,构造函数都不会执行除初始化实例之外的任何工作。 In case two above the algorithm is either driven by a client, just like an Iterator for example, or it's driven by the event listening thread. 如果算法上面的两个由客户端驱动,就像Iterator一样,或者它由事件监听线程驱动。

I generally try to do things like this: Don't hold state in the instance if it can be passed as arguments to service methods. 我通常尝试这样做:如果可以作为参数传递给服务方法,则不要在实例中保持状态。 Try to design the object with immutable state. 尝试使用不可变状态设计对象。 Defining attributes, like those used in equals and hashcode should allways be immutable. 定义属性,如equals和hashcode中使用的那些属性应始终是不可变的。

Conceptualy a constructor is a function mapping an object representation to the object it represents. Conceptualy构造函数是一个将对象表示映射到它所代表的对象的函数。

By the definition above Integer.valueOf(1) is actually more of a constructor than new Integer(1) because Integer.valueOf(1) == Integer.valueOf(1). 根据上面的定义,Integer.valueOf(1)实际上是一个构造函数而不是新的Integer(1),因为Integer.valueOf(1)== Integer.valueOf(1)。 , In either case this concept also means that all the cosntructor arguments, and only the constructor argument, should define the equals behavior of an object. 在任何一种情况下,这个概念也意味着所有的cosntructor参数,只有构造函数参数,应该定义一个对象的equals行为。

I would definitely do the second. 我肯定会做第二个。

Long actions in a constructor are fine if they are actually building the object so it is usable. 如果构造函数中的长动作实际构建对象以使其可用,则它们很好。

Now one thing that I see people do in constructors is call virtual methods. 现在我看到人们在构造函数中做的一件事就是调用虚方法。 This is BAD since once someone uses you as a base class and overrides one of those functions you will call the base class's version not the derived class once you get into your constructor. 这是不好的,因为一旦有人使用你作为基类并覆盖其中一个函数,一旦你进入你的构造函数,你将调用基类的版本而不是派生类。

I don't think that talking about abstract terms like "lengthy" have anything to do with the decision if you put something in an constructor or not. 我不认为谈论像“冗长”这样的抽象术语与决定有什么关系,如果你在构造函数中放置一些东西。

A constructor is something that should be used to initialize an object, a method should be used to "do something", ie have a function. 构造函数应该用于初始化对象,应该使用一种方法来“做某事”,即具有一个函数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 继承:为什么更改对象的类型被认为是糟糕的设计? - Inheritance: why is changing an object's type considered bad design? 在存储库接口中使用可选参数会被视为糟糕的设计吗? - Would using optional parameters in a repository interface be considered bad design? 创建方法“模板”(通过将某些方法传递给模板方法)会被认为是不良设计吗? - Would creating a method “template” (by passing some method to the template method) be considered bad design? 财产注入被认为是坏事吗? - Is property injection considered to be bad? 如何在C#中对继承的基本构造函数进行操作? - How do I do operations on an inherited base constructor in C#? 这是不好的设计吗? - Is this bad design? 查找ASP.Net超时原因(在冗长的操作期间) - Finding ASP.Net Timeout Reason (During lengthy operations) 使用动态是否被认为是不好的做法? - Is the use of dynamic considered a bad practice? 进行长时间的操作时,请始终专注于主窗口 - Keep focus on main window while doing lengthy operations 在 .Net Core MVC 中,当重复代码以在 dbcontext 中做同样的事情时,这是否被认为是不好的做法? - Is this considered bad practise in .Net Core MVC, when repeating code to do the same thing in dbcontext?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM