当将数据而不是方法参数传递给构造函数时,类的概念如何改变?


12

假设我们正在做一个解析器。一种实现可以是:

public sealed class Parser1
{
    public string Parse(string text)
    {
       ...
    }
}

或者我们可以将文本传递给构造函数:

public sealed class Parser2
{
    public Parser2(string text)
    {
       this.text = text;
    }

    public string Parse()
    {
       ...
    }
}

在两种情况下用法都很简单,但是与另一种相比,启用向的参数输入什么意思Parser1?当他们查看API时,我向同一个程序员发送了什么消息?此外,在某些情况下是否存在任何技术优势/劣势?

当我意识到接口在第二种实现中将变得毫无意义时,又出现了另一个问题:

public interface IParser
{
    string Parse();
}

...第一个接口上的接口至少可以达到某些目的。这是否特别表示某类是“可接口的”?


这是我第一次看到有关OO语义实际上如何表达意图的一些有用的问答。到目前为止,对我来说,这只是语法糖。

Answers:


11

从语义上讲,在OOP中,您只应向构造函数传递一组用于构建类所需的参数-类似地,在调用方法时,应仅向其传递执行其业务逻辑所需的参数。

您需要在构造函数中传递的参数是没有合理默认值的参数,如果您的类是不可变的(或者实际上是struct),则必须传递所有非默认属性。

关于您的两个示例:

  • 如果传递text给构造函数,则表明Parser2将专门构建该类以在以后解析该文本实例。这将是一个特定的解析器。当构建类非常昂贵或微妙时,通常会发生这种情况,RegEx可能会在构造函数中进行编译,因此,一旦您拥有一个实例,便可以重新使用它而不必支付编译成本。另一个示例是初始化PRNG-如果很少执行则更好。
  • 如果传递text给该方法,则该信号表示Parser1可以重新使用以通过调用解析不同的文本。

3
我要补充一点,即有一个微弱的信号表明Parser1保持状态-即,特定的文本字符串可能会产生不同的结果,具体取决于先前在tat实例上所做的事情。不一定是这样,但是可以。
jmoreno 2013年

8

好吧,让我们回想一下将变量作为构造函数参数传递的含义:初始化对象以便在对象的方法中使用其实例变量。关键是您可能想在一种以上的方法中使用它,因为您希望在类中具有较高的凝聚力

将参数直接传递给方法意味着将消息发送给对象并可能接收到答案。这样,客户希望对象为他提供服务。

因此,总而言之,这是两种非常不同的传递参数的方式,您应该选择在内部管理某些信息的同时,对象是内部提供服务还是提供某些功能。


+1。凝聚力是我决定它属于方法还是构造函数的方式。
jhewlett

4

在两种情况下用法都很简单,但是与其他方法相比,启用向Parser1输入参数的含义是什么?

这是一个根本的设计转变。设计应该传达意图和意义。您要解析的每个字符串是否都需要有单独的对象?换句话说,为什么我们需要一个带有stringX的解析器实例和另一个带有stringY的实例?解析和给定的字符串,这两个必须一起生活和消亡是什么?假设“基本的[解析]实现”(如Robert Harvey所说的)没有变化,似乎没有意义。即使如此,其可疑的恕我直言。

当将数据而不是方法参数传递给构造函数时,类的概念如何改变?

构造函数参数告诉我,这些是对象所必需的。没有它们,就不能保证正确的状态。另外,我知道一个解析器与另一个解析器有何根本区别。

构造函数参数使我不必过多地了解如何使用该类。如果相反,我应该设置某些属性-我怎么知道?一整个蠕虫打开。有什么特性?以什么顺序?在我使用什么方法之前?等等。

当我意识到接口在第二种实现中将变得毫无意义时,又出现了另一个问题:

与API中一样,接口是公开给客户端代码的方法和属性。不要只被包裹public interface { ... }。因此,接口的含义在“或”或“构造函数”与“方法参数困境”中,而非“ public interface Iparser与”public sealed class Parser

这个sealed班很奇怪。如果我在考虑不同的解析器实现-您确实提到了“ Iparser”,那么继承是我的第一个想法。这只是我思想中的自然概念扩展。IE all ParserX基本上Parser是s。还有什么怎么说呢?...德国谢泼德是一只狗(继承力),但是我可以训练我的鹦鹉吠叫(像狗一样-“接口”);但是波莉不是狗,只是假装了,已经学会了狗的一部分。无论是抽象类还是其他类,都可以很好地用作接口


如果它像解析器一样走路,像解析器一样说话,那就是……鸭子!

2

该类的第二个版本可以设为不可变的。

该接口仍可用于提供交换基础实现的功能。


通过以一种功能性的方式在类内传递数据,它也不能在第一个版本中变得不可变吗?
ciscoheat 2013年

2
绝对。但是,不变性的一般模式是使用构造函数设置类成员,并且具有只读属性。对于函数式编程,您甚至不需要类。
罗伯特·哈维

Scylla和Charybdis:我选择OO还是不变数据?我以前从未听说过那样的话。

2

解析器1

使用默认构造函数进行构建并将输入文本传递到方法中意味着Parser1可重用。

解析器2

将输入文本传递给构造函数意味着必须为每个输入字符串创建一个新的Parser2。


当然,如果我们将其带入一个新的水平,那么与不可重用的对象相比,可重用的对象有何结论?
ciscoheat 2013年

我不承担更多的责任,而是顺便参考文档。
Mike Partridge

身份更改的“可重用对象”是非必填项。有了托管框架,您甚至不必扔掉东西,覆盖它们或让它们超出范围,这肯定是不必要的。构造起来!
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.