2

我已经阅读了一些关于在面向对象编程中使用 getter/setter 的内容,并且许多诸如此类的资源表明它们应该很少使用。那篇文章的一句话说:

不要要求你提供工作所需的信息;请有信息的对象为您完成工作。

虽然这在原则上听起来不错,但我不确定如何最好地实施。说我有一门描述汽车的课程。在该类中,假设该类中存储的这辆车的品牌是“Toyota”。现在我有一个 GUI 想要显示这是一个“丰田”。没有吸气剂如何做到这一点?

即使我抽象出“Toyota”存储为字符串并返回一个已经写有“Toyota”的TextBox,这听起来充其量就像原始属性的包装形式的getter。我只是认为像 TextBoxes 这样的东西应该只在 GUI 的类中,而不是像我这里的汽车类这样的辅助类。

4

4 回答 4

3

那篇文章描述了您应该使用方法而不是属性获取器/设置器。基本上,它建议一个方法应该执行一个操作来修改状态,而不是允许设置器修改状态。例如,我可以有一个具有月和日属性的类。然后我可以有这样的代码:

obj.Day = 28;
obj.Month = Months.February;
obj.Day = 30;
obj.Month = Months.March;

一旦我将一天设置为 30 而月份是二月,我就会处于无效状态。

文章建议一个类不应该允许这种类型的事情,而是提供特定的方法来执行特定的操作,例如:

obj.ChangeDate(Months.March, 30);

如果日期时间类比由于 DateTime 而令人困惑,您可以使用 lat/long 坐标的类似示例:

obj.Latitude = 45.4112;
obj.Longitude = -75.6981;
//... 
obj.Latitude = 39.73;
obj.Longitude = -86.27;

当纬度设置为 39.73 时,该位置将变为靠近纽瓦克而不是渥太华(第一个纬度/经度)或印第安纳波利斯(第二个纬度/经度)的地方。保留纬度/经度设置会使界面开放,无法验证其不变量,有些人可能会说不是真正面向对象的。因此,您可能有一个方法:

obj.MoveTo(39.73, -86.27);

OO 意味着封装状态和行为。虽然有些人可能认为属性“封装”了状态,但它们通常不会。它们将实现与接口分离,以便用作属性后备存储的内容可以更改——但是,它很少这样做;因此,这些属性只是提供与提供公共字段不同的实现细节。

有像命令查询分离这样的概念建立在这个想法上,建议接口应该更新状态或查询状态,但不能同时更新。同时具有 getter 和 setter 的属性提供了更新状态和查询状态的接口。上面的例子MoveToChangeDate将是“命令”。从概念上讲,您将提供其他查询状态的方法;如果需要的话。

属性的另一个问题是人们习惯性地添加 getter 和 setter 仅仅是因为它是一个属性。获取或设置属性的能力可能不是必需的,只需通过死记硬背的创建和接口添加 getter 和 setter,这可能永远不需要测试和维护。

微妙的?当然; 但他说的是面向对象的设计,而不是“正常工作”的代码。

于 2012-08-29T22:40:56.137 回答
2

你忽略了“做工作”。如果您要完成的工作是汽车的品牌,那么返回品牌的吸气剂就可以完成工作。

这篇文章的意思是,如果你想在课堂数据上完成工作,就把工作放在课堂上。不要从类中提取数据,对其进行处理,然后将其返回给类:

var c = new Car();
c.Odometer += 10;  // bad
c.DriveMiles(10);  // good

我也质疑那篇文章对 C# 的适用性;它声称:

Getter 和 setter 方法(也称为访问器)是危险的,原因与公共字段危险的原因相同:它们提供对实现细节的外部访问。

在 C# 中这是错误的,C# 访问器可以接受任意类,它们不“提供对实现细节的外部访问”。

于 2012-08-29T22:51:33.653 回答
1

Getter 和 setter 并不邪恶,它们只是不应该为每个属性自动生成。就您的示例而言,Car 类可能应该有一个返回 String 的 getBrand 方法,但可能不需要此属性的 setter。Car 中返回带有品牌名称的 TextBox 的方法很可能比返回 String 的简单 getter 更糟糕,因为它不是很通用。

我现在可以看到 Peter Ritchie 的回答:changeDate 的好例子。Date 类应该提供这样的方法,而不是提供日期和月份的 getter 并要求用户代码自己计算日历中的第二天。

于 2012-08-29T22:50:49.033 回答
1

基本上他的意思是,假设你有一个有十个属性的类,你想把它转储到一行 csv 中。在类中添加一个 Tocsv 方法以返回一个包含一堆 csv 的字符串。不要编写通过属性访问它并构建 csv 字符串的其他类,因为它们必然会变得非常紧密耦合。

不知道他为什么选择访问器,问题与获取和设置的离散方法完全相同,或者更糟糕的是成员变量。

显然是有限制的,你必须有很好的理由更换

SomeLabel.Text = SomeInstance.StringValue;

SomeInstance.MakeStringValueALabel(SomeLabel);
于 2012-08-29T23:05:30.357 回答