oop:理解依赖关系和类之间的耦合

时间:2015-02-25 11:23:56

标签: java oop

究竟是什么意思,一个班级对另一个人的了解太多了?

示例(改编自此处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();
    }

}

在文件中说明了

  1. 主要课程取决于BBB
  2. BBB取决于AAA
  3. 提出的问题是

    1. BBB需要依赖AAA吗?
    2. BBB是否可以在新环境中重复使用?
    3. 关于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而现在依赖于抽象类,这是一个较弱的依赖。

      我没有得到的是与依赖关系和实现细节完全相关的问题,如果另一个类发生更改则必须更改。我认为最好使用接口和抽象类作为方法和构造函数的参数,因为你可以传递任何类扩展/实现类/接口。 但有实施细节的事情。当我有一个类的引用(并创建一个依赖项)并调用一个公共方法时,我如何依赖于实现细节?仅仅因为我称这种方法并不意味着我知道它内部发生了什么......?

      我已经在这里查看了其他示例,但我仍然没有真正得到它......

2 个答案:

答案 0 :(得分:4)

  

关于2. - 是吗?如果没有,为什么不呢?为什么可以   对AAA的依赖阻止它在新的上下文中使用?是否   这里的上下文意味着"与不同的班级"?

代码的问题是BBBAAA的高度耦合。
BBB正在创建AAA的实例,但这意味着如果您需要AAA的其他变体,则会稍微计算method1()(例如,更多对某些类型的输入有效),你不能使用它,因为通过创建BBB的实例 - 你不能选择使用AAA的哪个实现,制作这个类( BBB)不可重复使用。

如果您需要隔离其实例,可以让BBB在构造函数或AbstractFactory类中接收AAA的实例来解决此问题。

  

我没有得到的是依赖性和问题的确切问题   实现细节,如果另一个类改变则必须改变

  1. 它避免重复使用现有代码而无需复制粘贴大量代码, 那是一个发生很多错误的地方。
  2. 此外,假设您正在尝试计算f(g(x))
    现在,假设您有3种不同的方式"实施f()和4种不同的方式来实施g()
    如果你的班级是高度耦合的,那么每种计算方法都需要一个新的班级,所以你必须写3 * 4 = 12个班级。
    但如果他们脱钩,你只需要3 + 4 = 7班。
    在大项目中,这种情况很快发展到太多类,无法正常维护。
  3.   

    当我有一个类的引用(并创建依赖项)并调用时   一个公共方法,我如何依赖于实现细节?   仅仅因为我称这种方法并不意味着我知道最新情况   在里面......?

    不,但是你依赖于这个类,如果你使用的那个方法改变了它的签名 - 你也需要改变你的代码,所以如果在类A中进行了更改,那么你也要改变你的班级,你依赖于班级A

答案 1 :(得分:1)

  

当我有一个类的引用(并创建一个依赖项)并调用一个公共方法时,我如何依赖于实现细节?仅仅因为我称这种方法并不意味着我知道它内部发生了什么......?

该方法内部发生的事情很大程度上取决于您所依赖的类/实现。在您链接到的示例中,Timer类(您的AAA)取决于TimeToStretch类(您的BBB)。 是的,你可以改变TimeToStretch的实现来改变你的伸展方式。但是你不能改变饮用苏打水的实施方式,这会破坏(隐含)关于该类所做的事情的预期。系统的其他部分可能取决于拉伸。

  

关于2. - 是吗?如果没有,为什么不呢?为什么依赖于AAA会阻止它在新的上下文中使用?这里的语境是否意味着“有不同的阶级”?

是。如果你想重复使用Timer类来定期喝苏打水,而不是拉伸,如果Timer类实例化(并因此耦合到)TimeToStretch,你就不能这样做类。相反,如果你有:

  • Activity界面
  • 此类型Timer类的参数
  • 实现接口的多个类

然后,您可以将计时器用于所有这些类型的活动。另请注意名称:Activity接口不是AbstractTimeToStretch接口。接口的函数(以及名称)由客户端使用该接口(Timer)定义,而不是由(第一个)实现定义。

希望有所帮助。