究竟是什么意思,一个班级对另一个人的了解太多了?
示例(改编自此处https://courses.cs.washington.edu/courses/cse331/12au/lectures/08-events.pdf)
class AAA {
public void method1() {
//do something
method2();
}
public void method2();
// do something
}
}
class BBB {
private AAA ref = new AAA();
public void start() {
while(true) {
if (something) {
ref.method1();
}
}
}
}
class Main {
public static void main(String[] args) {
BBB ref = new BBB();
ref.start();
}
}
在文件中说明了
提出的问题是
关于2. - 是吗?如果没有,为什么不呢?为什么依赖于AAA会阻止它在新的上下文中使用?这里的上下文是否意味着“有不同的阶级”?
它说虽然BBB需要调用method1(),但它不需要知道该方法的作用。因此,您应该通过使用AAA的接口/抽象类而不是AAA本身来削弱依赖性(松散耦合)
所以新:
class AAA extends AbstractAAA {
// same
}
class BBB {
AbstractAAA ref;
public BBB (AbstractAAA param) {
this.ref = param;
}
public void start() {
while(true) {
if (something) {
ref.method1();
}
}
}
}
class Main {
BBB ref = new BBB(new AAA());
ref.start();
}
现在它说main仍然依赖于BBB,它取决于AAA的构造函数,但BBB不受AAA实现细节的影响。
所以依赖意味着在最广泛的意义上“使用一个实例”,所以我看到因此Main依赖于BBB而AAA和BBB以前依赖于AAA而现在依赖于抽象类,这是一个较弱的依赖。
我没有得到的是与依赖关系和实现细节完全相关的问题,如果另一个类发生更改则必须更改。我认为最好使用接口和抽象类作为方法和构造函数的参数,因为你可以传递任何类扩展/实现类/接口。 但有实施细节的事情。当我有一个类的引用(并创建一个依赖项)并调用一个公共方法时,我如何依赖于实现细节?仅仅因为我称这种方法并不意味着我知道它内部发生了什么......?
我已经在这里查看了其他示例,但我仍然没有真正得到它......
答案 0 :(得分:4)
关于2. - 是吗?如果没有,为什么不呢?为什么可以 对AAA的依赖阻止它在新的上下文中使用?是否 这里的上下文意味着"与不同的班级"?
代码的问题是BBB
和AAA
的高度耦合。
BBB
正在创建AAA
的实例,但这意味着如果您需要AAA
的其他变体,则会稍微计算method1()
(例如,更多对某些类型的输入有效),你不能使用它,因为通过创建BBB
的实例 - 你不能选择使用AAA
的哪个实现,制作这个类( BBB)不可重复使用。
如果您需要隔离其实例,可以让BBB
在构造函数或AbstractFactory
类中接收AAA
的实例来解决此问题。
我没有得到的是依赖性和问题的确切问题 实现细节,如果另一个类改变则必须改变
f(g(x))
。
f()
和4种不同的方式来实施g()
。
当我有一个类的引用(并创建依赖项)并调用时 一个公共方法,我如何依赖于实现细节? 仅仅因为我称这种方法并不意味着我知道最新情况 在里面......?
不,但是你依赖于这个类,如果你使用的那个方法改变了它的签名 - 你也需要改变你的代码,所以如果在类A
中进行了更改,那么你也要改变你的班级,你依赖于班级A
。
答案 1 :(得分:1)
当我有一个类的引用(并创建一个依赖项)并调用一个公共方法时,我如何依赖于实现细节?仅仅因为我称这种方法并不意味着我知道它内部发生了什么......?
该方法内部发生的事情很大程度上取决于您所依赖的类/实现。在您链接到的示例中,Timer
类(您的AAA
)取决于TimeToStretch
类(您的BBB
)。
是的,你可以改变TimeToStretch
的实现来改变你的伸展方式。但是你不能改变饮用苏打水的实施方式,这会破坏(隐含)关于该类所做的事情的预期。系统的其他部分可能取决于拉伸。
关于2. - 是吗?如果没有,为什么不呢?为什么依赖于AAA会阻止它在新的上下文中使用?这里的语境是否意味着“有不同的阶级”?
是。如果你想重复使用Timer
类来定期喝苏打水,而不是拉伸,如果Timer
类实例化(并因此耦合到)TimeToStretch
,你就不能这样做类。相反,如果你有:
Activity
界面Timer
类的参数然后,您可以将计时器用于所有这些类型的活动。另请注意名称:Activity
接口不是AbstractTimeToStretch
接口。接口的函数(以及名称)由客户端使用该接口(Timer
)定义,而不是由(第一个)实现定义。
希望有所帮助。