关于java:interface和contract:在这个例子中

interface and contract : in this example

所以我试图理解接口,但我几乎只看到解释"如何"使用接口的文章,我的问题是理解"为什么":所以最好使用接口,而不是创建和子类,这可能是无用的,所以我们在类中实现接口方法,但我不理解为什么这是一件好事,

让我们说:像Car.java这样的类定义了制造汽车的所有代码我们用start()、stop()等多种方法创建接口Working.java。我们在Diesel_Car.javaElectric_Car.java等中实现了这些方法。那么它对Car.java有什么变化呢?这可能不是最好的例子,因为似乎汽车应该是Diesel_Car.java等的父母,但是在这些类中实现这些方法的意义是什么呢?Car.java中是否有一个方法以某种方式"调用"Diesel_Car.java类及其接口方法?

我已经读过这个接口就像一个"契约",但是我只看到这个契约的第二部分(方法是在哪里实现的),我很难想象第一部分在哪里发生?

谢谢你的帮助


我正在尝试将接口的概念解释为合同。

一个典型的使用场景是,当您想使用java.util.Collections对元素列表进行排序时:> void sort(java.util.List ts)

这个签名是什么意思?sort()方法将接受类型为t的对象的java.util.List,其中t是实现可比较接口的对象。

因此,如果要将collections.sort()与对象列表一起使用,则需要它们来实现Comparable接口:

1
2
3
public interface Comparable<T>   {  
    int compareTo(T t);
}

因此,如果您实现一个类型为Car的类,并且希望使用collections.sort()按汽车的重量比较它们,那么您将必须在类汽车中实现可比较的接口/契约。

1
2
3
4
5
6
7
8
9
10
11
12
public class Car implements Comparable<Car> {
   private int weight;

   //..other class implementation stuff

   @Override
   public int compareTo(Car otherCar) {
       if (this.weight == otherCar.weight) return 0;
       else if (this.weight > otherCar.weight) return 1;
       else return -1;
   }
}

collections.sort()将在对列表排序时调用CompareTo的实现。


(非常)人为的示例(为了清晰起见,删除了非一般性、错误处理等)。

1
List theList = new ArrayList();

theListList,在这种情况下由ArrayList实现。假设我们把它传递给第三方API,在它的内部某处添加了一些内容。

1
2
3
public void frobList(List list) {
    list.add(new Whatever());
}

现在,让我们假设出于某种原因,我们想对添加到列表中的项目执行一些不寻常的操作。我们无法更改第三方API。但是,我们可以创建一个新的列表类型。

1
2
3
4
5
public FrobbableList extends ArrayList {
    public boolean add(E e) {
        super.add(Frobnicator.frob(e));
    }
}

现在,在我们的代码中,我们更改了我们实例化的列表,并像以前一样调用API:

1
2
List theList = new FrobbableList();
frobber.frobList(theList);

如果第三方API采用了EDOCX1(实际类型)而不是EDOCX1(接口),那么我们就不能这么容易地做到这一点。通过不将API锁定到特定的实现中,它为我们提供了创建自定义行为的机会。

更进一步说,这是可扩展、可调试、可测试代码的基本概念。像依赖注入/控制反转这样的事情依赖于对接口的编码。


我们以Electric_CarDiesel_Car子类的Car基类为例,稍微扩展一下模型。

汽车可能有以下接口

  • Working:用start()stop()方法
  • Moving:用move()turn()stop()方法
  • Car可能包含一个类AirConditioner的实例,该实例还应实现接口Working

    Driver对象可以与对象交互,而不是实现Working,驱动程序可以与start()stop()交互。(司机可分别启动或停止汽车和空调)。

    另外,由于Driver可以自己走动(并不总是需要一辆车),他应该实现Moving接口。对象Ground现在可以与实现Moving的任何对象交互:汽车或驾驶员。


    Design by contract (DbC), also known as programming by contract and design-by-contract programming, is an approach for designing computer software. It prescribes that software designers should define formal, precise and verifiable interface specifications for software components, which extend the ordinary definition of abstract data types with preconditions, postconditions and invariants. These specifications are referred to as"contracts", in accordance with a conceptual metaphor with the conditions and obligations of business contracts. Wikipedia

    Ok.

    简捷。

    /好的。

    如果你遵循的好的实践,对界面的编码,你知道的defines接口的类必须实现所有的合同adhere OP。

    /好的。

    我们自行设计的合同延长安Java,Java中的方法,这是在合同specified接口。我们确定的三维设计的目标。

    /好的。

    • 首先,在没有契约和程序的情况下合同Java程序。完全满足的合同应该表现得像在没有JAVA合同。
    • 其次,用传统的Java编译器编译的程序必须能够与程序进行互操作。在JAVA合同下编译。
    • 最后,除非某个类声明它满足某个特定的契约,否则决不能因为未能满足该契约而受到指责。合同。抽象地说,如果调用类型为t的对象的方法m,则只应将与T和M相关的先决条件合同只应归咎于与T和M相关的后决条件合同。用T.这些设计目标提出了几个有趣的问题,并要求做出平衡语言设计与软件工程问题。本节描述了每个主要的设计问题、备选方案、我们的决策,我们的理由和决定的后果。这些决定不是正交的;一些后来的决定依赖于早期的。

    合同JAVA中的合同是接口中方法签名的修饰。每个方法声明都可能使用前置条件表达式和后置条件表达式;两个表达式的计算结果都必须为布尔值。这个pre-condition指定调用方法时必须为true的内容。如果失败,方法调用的上下文是责备没有在适当的上下文中使用该方法。post条件表达式指定当方法返回。如果失败了,方法本身就是不建立承诺条件的罪魁祸首。JAVA合同不限制合同的表达。不过,良好的编程规则要求表达式不应影响项目的结果。特别是,表达式不应该有任何副作用。前和后条件表达式都是通过方法和伪变量的参数进行参数化的。这个。后者绑定到当前对象。此外,合同的后置条件可参考绑定到方法调用结果的方法的名称。协定是基于方法调用的类型上下文强制执行的。如果对象的类型是接口类型,则方法调用必须满足接口中的所有协定。例如,如果一个对象实现了接口i,则调用到i的方法之一必须检查i中指定的前置条件和后置条件。如果对象的类型是类类型,对象没有合同义务。因为程序员总是可以为任何类创建接口,所以我们出于效率原因,不选中具有类类型的对象。例如,考虑接口rootfloat:好的。

    1
    2
    3
    4
    5
    6
    interface RootFloat {
        float getValue ();
        float sqRoot ();
        @pre { this.getValue() >= 0f }
        @post { Math.abs(sqRoot * sqRoot - this.getValue()) < 0.01f }
    }

    它描述了提供sqroot方法的浮点包装类的接口。第一个方法getValue没有合同。它不接受任何参数,并返回未包装的浮点。sqroot方法也不接受任何参数,但有合同。前提条件断言展开值大于或等于零。结果类型sqroot的值是float。post条件说明结果的平方必须在float值的0.01范围内。即使合同语言足够强大,可以在某些情况下指定完整的行为,例如前面的例子,在设计这些合同时,完全或部分的正确性不是我们的目标。通常,合同无法表示方法的完整行为。事实上,在合同可以满足的接口和验证量。例如,考虑这个堆栈接口:好的。

    1
    2
    3
    4
    interface Stack {
        void push (int i);
        int pop ();
    }

    由于接口中只有推送和弹出操作,因此无法指定推送后的顶部堆栈中的元素是刚刚推送的元素。但是,如果我们用一个显示堆栈上最顶层的项(不移除它),然后可以指定push将项添加到堆栈:好的。

    1
    2
    3
    4
    5
    6
    interface Stack {
         void push (int x);
         @post { x = this.top() }
         int pop ();
         int top ();
    }

    总之,我们不限制合同的语言。这使得合同语言尽可能灵活;合同表达式的评估甚至可能有助于计算的最终结果。尽管合同语言,并非所有理想的合同都是可以表达的。有些合同是无法表达的,因为它们可能包括检查不可确定的属性,而其他属性由于接口不够允许而无法表达。观察。好的。好啊。


    契约是一个关于类之间如何工作的概念。其思想是,接口类定义了方法的返回类型和名称,但不提供如何实现它的思想。这是由实现类完成的。

    概念是,当接口A定义方法A和B时,实现该接口的任何类都必须实现A和B及其自己的方法。所以可能是这样的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    interface InterfaceA {
        void methodA();
        void methodB(String s);
    }

    public class ClassA implements InterfaceA {
         public void methodA() {
             System.out.println("MethodA");
         }

         public void methodB(String s) {
             System.out.println(s);
         }
    }

    契约原则是任何实现接口的东西都必须实现整个接口。任何不这样做的事情都必须是抽象的。

    希望这有帮助。