这是将数据传递给方法的更好方式

本文关键字:方法 更好 方式 数据 | 更新日期: 2023-09-27 18:05:22

设我们有两个类Expense1Expense2。哪个类实现比另一个更受欢迎,或者哪个被认为更接近面向对象?

我一直认为做Exp2.Calculate(1.5M,2)exp1.Calculate()和使用exp1类的属性作为计算方法的所需值。

Expense1

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
public class Expense1
{
    public decimal ExpenseValue { get; set; }
    public int NumberOfItems { get; set; }
    private decimal result;
    public decimal Result
    {
        get
        {
            return this.NumberOfItems * this.ExpenseValue;
        }
    }
    public void Calculate()
    {
        this.result = this.ExpenseValue * this.NumberOfItems;
    }
    public void expense1()
    {
        this.ExpenseValue = 0;
        this.NumberOfItems = 0;
    }
}

Expense2

class Expense2
{
    public decimal Calculate(decimal expenseValue, int numberOfItems)
    {
        return expenseValue * numberOfItems;
    }
}

两个类的实现

class Program
{
    static void Main(string[] args)
    {
        Expense1 exp1 = new Expense1();
        exp1.NumberOfItems = 2;
        exp1.ExpenseValue = 1.5M ;
        exp1.Calculate();
        Console.WriteLine("Expense1:" + exp1.Result.ToString());
        Expense2 exp2 = new Expense2();
        string result = string.Empty;
        result = exp2.Calculate(1.5M,2).ToString();
        Console.WriteLine("Expense2:" + result);
        Console.ReadKey();
    }
}

这是将数据传递给方法的更好方式

Expense2更容易阅读和理解发生了什么,因为从调用中可以看出使用了哪些参数。

Expense1可能有各种副作用,因为调用者可能在调用Calculate()之前忘记设置用于计算的变量。

如果稍后需要访问用于计算结果的值,可以使用如下内容

public class Expense {
    public decimal ExpenseValue { get; private set; }
    public int NumberOfItems { get; private set; }
    public decimal Result { get; private set; }

    public decimal Calculate(decimal expenseValue, int numberOfItems) {
        ExpenseValue = expenseValue;
        NumberOfItems = numberOfItems;
        Result = ExpenseValue * NumberOfItems;
        return Result;
    }
    public Expense() {
        Calculate(0, 0);
    }
}

这将允许Expense的内部状态与Calculated

的定义在对象的生命周期内保持一致。

Expense2.Calculate是确定性的(每次使用相同的参数调用时都有相同的结果),并且因为它没有副作用(参数是提供给它的,而不是通过属性),所以它也是线程安全的。最后,它更简单,更容易阅读。

Expense1是一个典型的非线程安全状态的OOP问题,并且不能保证Result将返回线程调用Calculate的结果。而且,它冗长,阅读和维护起来很复杂。

每次我都更喜欢Expense2而不是Expense1。我唯一要改变的是使它成为静态的,因为没有必要创建Expense2的实例来调用Calculate

我一点也不喜欢Expense1我认为通常应该避免使用public get/set属性,因为它没有提供封装。它甚至很难比公共字段更好(尽管它确实给了一些保护条款等空间)。

然而,在我看来,比这更糟糕的是Calculate()方法。它不带参数,但仍然会改变对象的内部状态。目前尚不清楚是如何做到的。同时,它写入的字段result没有由属性Result返回,该属性再次进行相同的计算(如果必须进行计算,通常更倾向于使用方法而不是属性)。

最后,没有必要在构造函数中将基本类型初始化为默认值,数字类型总是初始化为0。

= = =

Expense2更好,它更清楚发生了什么。方法接受两个命名良好的参数,并直接给出合理的结果。

如果这个方法是该类的唯一用例,那么我将考虑重命名它,因为它不表示费用,它表示的是接近于费用计算器的东西。

这完全取决于你想做什么,但经验法则是,当属性真正与对象相关联时使用属性,当属性位于对象外部时,将需要的属性作为参数传递。

一个简单的例子,考虑将Person作为一个类,它的一个属性是birthday,所以在这种情况下你应该这样做

public class Person{
     public DateTime Birthday{get;set;}
     public int GetAge(){
          //some implementation
     }
}

另一个例子,考虑有一个立方体对象,在这个立方体对象中,你有一个与另一个立方体相交的方法,在这种情况下,你应该将第二个立方体作为参数传递,而不是使其成为立方体的属性,因为它是外部的东西。

public class Cube{
     public Cube Intersect(Cube other){
         //Do your logic here
     }
}