Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Oop 设计问题:传递您使用的字段还是传递对象?_Oop_Method Signature - Fatal编程技术网

Oop 设计问题:传递您使用的字段还是传递对象?

Oop 设计问题:传递您使用的字段还是传递对象?,oop,method-signature,Oop,Method Signature,我经常看到方法接口有两种相互冲突的策略,大致概括如下: // Form 1: Pass in an object. double calculateTaxesOwed(TaxForm f) { ... } // Form 2: Pass in the fields you'll use. double calculateTaxesOwed(double taxRate, double income) { ... } // use of form 1: TaxForm f = ... doub

我经常看到方法接口有两种相互冲突的策略,大致概括如下:

// Form 1: Pass in an object.
double calculateTaxesOwed(TaxForm f) { ... }

// Form 2: Pass in the fields you'll use.
double calculateTaxesOwed(double taxRate, double income) { ... }

// use of form 1:
TaxForm f = ...
double payment = calculateTaxesOwed(f);

// use of form 2:
TaxForm f = ...
double payment = calculateTaxesOwed(f.getTaxRate(), f.getIncome());
我见过第二种形式的拥护者,特别是在动态语言中,在动态语言中,可能更难评估正在使用哪些字段

但是,我更喜欢第一种形式:它更短,出错的空间更小,而且如果对象的定义以后更改,您不一定需要更新方法签名,也许只需要更改您在方法内处理对象的方式


这两种形式都有令人信服的一般情况吗?有没有明确的例子说明什么时候应该使用第二种形式而不是第一种形式?是否有可靠或其他OOP原则可以证明我决定使用一种形式优于另一种形式?如果您使用的是动态语言,上面的答案是否会改变?

这取决于您的方法的意图

如果该方法设计为专门处理该对象,并且仅处理该对象,则传递该对象。这是一个很好的封装


但是,如果该方法是更通用的,则可能希望单独传递参数。这样,当信息来自另一个来源(即不同类型的对象或其他派生数据)时,该方法更有可能被重用。

我认为这并不重要。如果传入可能发生变异的对象,则可能会产生副作用。然而,这可能是你想要的。为了缓解这种情况(并帮助测试),您可能最好传递接口,而不是具体类型。好处是,如果要访问对象的另一个字段,则不需要更改方法签名

通过传递所有参数,可以更清楚地了解类型需要什么,并且可以更容易地进行测试(尽管如果您使用该接口,这并没有什么好处)。但是你会有更多的重构


根据每种情况的优点来判断每种情况,并选择最不痛苦的情况。

第一种形式的优点是

  • 抽象-编程到接口而不是实现。从长远来看,它使代码的维护变得更容易,因为只要TaxForm的接口不变,您就可以在不影响客户端代码的情况下更改TaxForm的实现

  • 我强烈推荐第二种解决方案-
    calculateaxesowed()
    计算一些数据,因此需要一些数字输入。该方法与用户界面完全无关,因此不应使用表单作为输入,因为您希望业务逻辑与用户界面分离

    执行计算的方法(通常)甚至不应属于与用户界面相同的模块。在这种情况下,您会得到一个循环依赖关系,因为用户界面需要业务逻辑,而业务逻辑需要用户界面表单——这非常强烈地表明出了问题(但仍然可以使用基于接口的编程来解决)

    更新


    如果税务表单不是一个用户界面表单,那么情况会发生一些变化。在这种情况下,我建议使用
    TaxForm
    类的实例方法
    GetOwedTaxes()
    或实例属性
    OwedTaxes
    公开该值,但我不会使用静态方法。如果计算可以在其他地方重用,仍然可以创建一个使用值而不是表单的静态帮助器方法,并从实例方法或属性中调用此帮助器方法。

    老实说,这取决于所讨论的方法

    如果没有对象的方法有意义,那么第二种形式更容易重用,并且消除了两个类之间的耦合

    如果该方法依赖于对象,则公平地传递该对象


    对于第三种形式,可能有一个很好的理由,您可以在其中传递一个设计用于使用该方法的接口。第一种形式清晰,第二种形式灵活。

    只传递参数更容易进行单元测试,因为您不需要模拟整个对象,只需要测试基本上只是静态计算的功能。如果只使用两个字段,在对象的许多字段中,我倾向于只传递这些字段,其他所有字段都相等

    这样说,<强>当您结束六个、七个或更多字段时,是考虑在“有效载荷”类< /强>(或结构/字典)中传递整个对象或字段的子集(取决于语言的风格)。长方法签名通常令人困惑


    另一个选项是将其设置为类方法,这样就不必传递任何内容。测试不太方便,但当您的方法仅用于分类表对象的数据时,值得考虑。

    这与Martin Fowler关于重构的书中的“引入参数对象”相同。Fowler建议,如果有一组参数倾向于一起传递,则执行此重构。

    如果您相信德米特定律,那么您会倾向于传递所需的参数:


    将用户界面和要操作的数据分离开来

    在您的例子中,您缺少了一个中间类,例如TaxInfo,它代表要纳税的实体。原因是UI(表单)和业务逻辑(税率的计算方式)处于两种不同的“变化轨道”,一种是随着表示技术(“web”、“web 2.0”、“WPF”等)的变化而变化,另一种是随着法律的变化而变化。在它们之间定义一个清晰的接口


    一般性讨论,使用示例:

    考虑一个为名片创建位图的函数。功能的目的是什么

    (1) //根据名字和姓氏设置名片标题的格式

    (2) //设置业务ca的格式
    double payment = f.calculateTaxesOwed;
    
    double calculateTaxesOwed(TaxForm f) 
    {
        return calculateTaxesOwed(f.getTaxRate(), f.getIncome());
    }
    
    double calculateTaxesOwed(double taxRate, double income) { ... }