英文:
Should classes define getters for their dependencies?
问题
假设我有一个名为 car 的类,其中包含一个 motor。
Motor 类的定义如下:
class Motor {
private String code;
public String getCode() {
return code;
}
}
假设我们需要从任何能够访问 car 的地方访问 motor 的 code,那么应该如何定义 car 类呢?
第一种选项:
class Car {
private Motor motor;
public Motor getMotor() {
return motor;
}
}
可以实现以下操作:
Car car = ...
Motor motor = car.getMotor();
String code = motor.getCode();
或者
class Car {
private Motor motor;
public String getMotorCode() {
return motor.getCode();
}
}
可以实现以下操作:
Car car = ...
String code = car.getMotorCode();
我假设第二种选项更好,因为它遵循了德米特法则,而第一种选项则没有。另一方面,外部类是否应该为内部类的每个 getter 方法都进行定义呢?motor 是否应该通过 getMotor()
来暴露出来呢?
英文:
Let's say I have a class car which has a motor.
The class motor is defined like this:
class Motor {
private String code;
public String getCode() {
return code;
}
}
How should the class car be defined, assuming we need to access the motor's code from whoever has access to the car?
class Car {
private Motor motor;
public Motor getMotor() {
return motor;
}
}
Which allows for this:
Car car = ...
Motor motor = car.getMotor();
String code = motor.getCode();
Or
class Car {
private Motor motor;
public String getMotorCode() {
return motor.getCode();
}
}
Which allows for this:
Car car = ...
String code = car.getMotorCode();
I assume the second option is best since it follows the Law of Demeter, which the first one doesn't. On the other hand, should the outer class define every single getter from the inner class(es)? Should the motor ever be exposed with a getMotor()
?
答案1
得分: 1
是的,由于Motor
将单独使用,如果它需要拥有自己的类,那么可以这样做。如果不需要,那么Car
类可以在自身中存储String motorCode
。这前提是Car
是一个组合模型,没有重要逻辑,就像示例中的情况一样。
正是示例中这种瘦削的类设计使得情况不太清楚,但在真实世界的应用中,Motor
类拥有数十个字段和自己的组合类。Car
类不能为所有这些字段都编写方法。
然后,Car
类可以编写方法,从不同的组合字段聚合数据,或者使得一些经常使用的数据更容易访问,而不需要冗长的car.getMotor().getSomething().getData()
链式调用。
在我们的产品中也存在着这种车辆-发动机等层次结构(虽然更加丰富),我知道在真实世界的应用中,Motor
将会被单独处理。还有一些潜在的东西,比如Transmission
(变速器)、Tyres
(轮胎)等等。因此,最终的结果是一种混合方法,可以访问内部的组合元素,因为有时是需要的。对象图过于庞大且不足够有趣,不值得花费大量的设计时间,因此这是封装和开发速度之间的权衡。
英文:
Yes, since Motor
is going to be used by itself without its enclosing Car
if it warrants its own class. If it doesn't, then the Car
class can store String motorCode
in itself. This assumes that Car
is a composite model without significant logic, as it seems to be in the example.
It's the anorectic class design of the example that makes it unclear, but in a real world application the Motor
class has dozens of fields and composite classes of its own. The Car
class can't have methods for all of them.
The Car
class may then have methods that aggregate data from different composite fields, or make some often used data easier to access without long car.getMotor().getSomething().getData()
chains.
Having this same vehicle-motor-etc. hierarchy in our product (although much fuller), I know that in real world applications Motor
will be handled separately. As well as potentially things like Transmission
, Tyres
and so on. So the end result is a hybrid approach where you provide access to the internal composite elements, since sometimes you need it. The object graph is too big and not interesting enough to warrant a lot of design, so it's a trade-off between encapsulation and development speed.
答案2
得分: 1
car.getMotor().getCode()
胜出。
为什么?
- 代码更少;你不需要实现
getMotorCode()
getMotorCode()
违反了迪米特法则 - 一辆车会知道并与其引擎拥有耦合关系,了解其字段。如果有一天出现了一个没有编码的引擎怎么办?getMotorCode()
是一个几乎没有增加价值的便利方法getMotorCode()
不是一个程序员会期望找到车辆引擎代码的地方,但car.getMotor().getCode()
是getMotorCode()
不是行业标准 - 我随意想不起来曾经见过类似的方法getMotorCode()
(而我见过很多代码)
关于违反迪米特法则,引用维基百科:
> 对象 a 可以请求对象实例 b 的服务(调用方法),但对象 a 不应“穿透”对象 b 来访问另一个对象 c 并请求其服务。这样做意味着对象 a 隐含地需要更多了解对象 b 的内部结构。
这恰好描述了 getMotorCode()
的行为。
英文:
car.getMotor().getCode()
wins.
Why?
- It’s less code; you don’t have to implement
getMotorCode()
getMotorCode()
breaks Demeter's Law – a car would know about, and be coupled to, what fields a motor has. What if one day there’s a motor that doesn’t have a code?getMotorCode()
is a convenience method that adds little valuegetMotorCode()
is not where a coder would expect to find a car’s motor’s code, butcar.getMotor().getCode()
isgetMotorCode()
is not industry standard - off hand I cannot recall seeing such a method asgetMotorCode()
(and I’ve seen a lot of code)
Regarding breaking the Law of Demeter, quoting Wikipedia:
> An object a can request a service (call a method) of an object instance b, but object a should not "reach through" object b to access yet another object, c, to request its services. Doing so would mean that object a implicitly requires greater knowledge of object b's internal structure.
This precisely describes what getMotorCode()
does.
答案3
得分: 0
我认为这很简单:Motor
是可变的还是拥有一些应该隐藏的信息(即:不向客户/调用者公开的信息)?如果任何一个答案是肯定的 - 只需通过允许的信息来间接地暴露 Motor
。
否则你会冒这样的风险:
-
允许调用者更改
Motor
的内部(这可能不是你想要的) -
暴露过多的信息(比如
VIN
或许不应该对所有人可见)
英文:
I see this as rather simple: is Motor
mutable or is Motor
having some information that should be hidden (i.e. : not exposed to clients/callers)? If any of the answer is yes - simply don't expose Motor
directly, but only via the information that is allowed.
Otherwise you risk:
-
for callers to change the internals of
Motor
(which you might not want) -
exposing too much (like may be
VIN
should not be visible for everyone)
答案4
得分: 0
@Bohemian♦理解得很对。
在Java中,Getter和Setter用于提供返回数据的标准方法。
以这种方式实现Motor
:
public String getCode() {
return "Code: " + code;
}
你的Car
类是否需要知道如何返回代码?不需要
假设你在Car
中实现了getMotorCode()
,你会复制如何返回代码吗?不会
如果你改变了返回代码的方式,比如return "C: " + code;
,你会需要改变每个类似你已实现的getMotorCode()
的方法吗?不会
car.getMotor().getCode()
是标准的,因为每个部分都管理其自己的工作。
英文:
@Bohemian♦ got this right.
Getter and Setters in Java are used to provide standard ways to return data.
Implement Motor
this way :
public String getCode() {
return "Code: " + code;
}
Does your Car
class needs to know how to return a code ? No
Let's imagine you implement getMotorCode()
in Car
, will you duplicate how to return a code ? No
You change the way to return a code like return "C: " + code;
, will you change every method like getMotorCode()
you've implemented ? No
car.getMotor().getCode()
is the standard because each part manages its job.
答案5
得分: 0
我认为到目前为止,大多数答案都没有提到最重要的一点:你的类应该始终努力提供服务,而不是对象。
每当你有一个返回对象的getter时,你都迈出了朝着“告诉,不要问”的方向迈出的第一步。
告诉你的客户端代码去做某事(通过调用其他对象的服务),而不是强迫客户端检索其他对象,然后根据这些对象的状态做决策!
英文:
I think most answers so far didn't mention the most important aspect: your classes should always strive to provide services, not objects.
Whenever you have a getter that returns objects, you make the first step towards breaking "tell, don't ask".
Tell your client code to do something (by invoking services of other objects) instead of forcing the client to retrieve other objects, to then make decisions based on the state of such objects!
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论