【问题标题】:Do function arguments violate encapsulation?函数参数是否违反封装?
【发布时间】:2016-03-07 08:41:12
【问题描述】:

这个问题通常与 OOP 实践有关。 假设我们有一个带有公共函数的类,它接受从对象外部传入的参数。这不是违反封装本身吗?另一方面,为什么这种做法被如此广泛地使用?毕竟类的构造函数和成员变量在调用函数时有点“绕过”。作为一个相对较新的 OOP 程序员和我对封装的理解,我的函数参数通过 setter 传递到对象中,因此我只使用传入的成员变量来保留我的所有函数而没有任何参数。 我知道某些参数可以通过构造函数传入(顺便说一句,我使用依赖注入),但是如果这些参数在对象被实例化后发生了变化怎么办?必须有一种方法可以在创建对象后更改这些值。到目前为止,我发现除了使用 setter 来完成这项任务之外没有其他选择,但是程序员之间关于 getter 和 setter 是“邪恶的”或者至少被认为不是好的编程实践的长期讨论。 谁能告诉我我在哪里错过了重点以及如何以干净的方式解决这个困境? 非常感谢您的任何支持。

这是一个使用 C# 的具体非常简单的示例:

我们在一个 windows 表单项目中有一个表单,其中包含 3 个文本框,名为 textBox1 和 textBox2 和 textBox3。

任务是添加 textBox1 和 textBox2 的值,并在 textBox1 或 textBox2 的值发生变化时使用由事件处理程序实例化的类 AddTextboxValues 将结果返回给 textBox3:

我经常看到,问是否违反封装的方式:

public class AddTextBoxValues
{
    public double TextBoxValueSum(double textBox1value, double textBox2Value)
    {
       return textBox1value + textBox2Value;
    }
}

根据我对封装的理解,这是我目前使用的方式:

public class AddTextBoxValues
{
    private double textBox1Value;
    private double textBoxValue2;
    private double textBoxValue3;

    public double TextBox1Value
    {
        set { textBox1Value = value; }
    }

    public double TextBoxValue2
    {
        set { textBoxValue2 = value; }
    }

    public double TextBoxValue3
    {
        get { return textBoxValue3; }
    }


    public void TextBoxValueSum()
    {
        textBoxValue3= textBox1Value + textBoxValue2;
    }

}

这还有一个好处是可以注入到表单构造函数中。

非常感谢任何评论。

【问题讨论】:

  • 如果你能给出一个具体的例子会很有帮助 - 听起来你将对象的状态与它需要处理的其他状态混淆了。 (例如,日期格式化程序需要知道它打算使用的格式,但它会传递“要格式化的日期”,而这绝不是对象本身状态的一部分。)
  • 嗯,这是实现相同结果的两种不同方法 - 但您真的希望文本框值成为类状态的一部分吗?您认为在所有种情况下它们应该是吗?
  • 你使用什么定义的“封装”会暗示它被使用方法参数“绕过”了?
  • 结果是一样的,但哪一个是更优雅/正确的方式?请记住,我的问题是针对封装暴力的(首先是暴力吗?)。对于我的特定示例,您能否具体说明“班级状态”和“所有案例”的含义?
  • 我的意思是 object 的状态,真的。为什么你认为一个对象必须改变它的状态(即它的字段的值)才能处理数据? “所有案例”部分真的是“您认为这是否普遍适用”?从根本上说,永远不应该使用参数的想法对我来说是一个非常非常糟糕的想法,这会使编码变得更加痛苦。是的,如果你有一个Adder 类,你可以创建一个新对象,每次你希望它添加两个数字,但你为什么要这样做呢?但是不知道背景很难写出具体的答案。

标签: function parameters arguments encapsulation getter-setter


【解决方案1】:

非常感谢 Jon Skeet,你是​​一位真正的专业人士。 您准确地诊断了我的问题,并为我缺乏知识来理解和找到我自己的问题的解决方案而睁大了眼睛。确实是对封装和“对象状态”的更深入理解构成了我拼图中缺失的部分。 现在对我来说一切似乎都很合乎逻辑和清晰,我希望它将来也能对其他人有所帮助。

【讨论】:

    【解决方案2】:

    这两个例子都不是面向对象的编程。它们是过程式编程的示例。

    第一个“类”实际上只是一个包装TextBoxValueSum 函数的命名空间。 第二个“类”只是一个带有公共字段的结构(getters-setter 和公共字段没有区别)。

    如果你想使用真正的面向对象编程,你应该想到对象,它们是事物的表示。 在你的情况下,我会写 class Sum 这是一个真实的东西,代表 一个特定的总和

    class Sum {
      private double a, b;
      public Sum (double a, double b) { this.a = a; this.b = b; }
      public double value() { return this.a + this.b; }
    }
    

    【讨论】:

    • 我是否必须为每个 sum 操作创建和处置 sum 类的对象?那么 OOP 将如何处理依赖注入?,如果 DI 在 OOP 中很重要,那么下一个问题出现了:问题是对象在初始化时不知道操作数的情况下被注入到表单中,它只会在表单创建时创建一次被创建并且需要在其整个生命周期(相当于表单的生命周期)中更改输入值,以在文本框值更改时构建新的总和。那么我们如何在对象创建后改变值呢?
    • 您可以使用工厂(它不仅可以创建Sum对象,还可以创建一些不同的对象,例如Difference等)并注入它。 void clientFunction(NumberFactory numFactory) { double sum = numFactory.sum(textBox1.value, textBox2.value).value(); }
    猜你喜欢
    • 2019-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-15
    相关资源
    最近更新 更多