发布新日志

  • 我对Java接口的理解

    2007-08-07 11:58:12

     
     

     

     

     

    刚学Java语言时,就很难理解为什么要有接口这个概念,虽说是可以实现所谓的多继承,可一个只有方法名,没有方法体的东西,我实现它又有什么用呢?我从它那什么也得不到,除了一些方法名,我直接在具体类里加入这些方法不就行了吗?

    为什么一定要有抽象类这个概念?为什么就不能把这个父类写成一个具体的类,子类再继承它不就可以了吗?何必弄一个抽象类出来,还要弄一些没有方法体的抽象方法,弄得又象接口又象类的,让人捉摸不定。

    当我开始学习java设计模式,真正走进面向对象设计的大门之后,我才发现,自己对面向对象设计的理解原来是那么的片面,那么的肤浅,根本就没有真正理解面向对象思想的精髓,在某一种程度上还受着面向过程的影响,以为弄出了一个个类,就算是面向对象了,而其实还是被过程所驱使着。

    面向对象思想,最关键的就是抽象

    一个软件设计的好坏,我想很大程度上取决于它的整体架构,而这个整体架构其实就是你对整个宏观商业业务的抽象框架,当代表业务逻辑的高层抽象层结构合理时,你底层的具体实现需要考虑的就仅仅是一些算法和一些具体的业务实现了。当你需要再开发另一个相近的项目时,你以前的抽象层说不定还可以再次利用呢,面对对象的设计,复用的重点其实应该是抽象层的复用,而不是具体某一个代码块的复用.

    既然面向对象设计的重点在于抽象,那Java接口和Java抽象类就有它存在的必然性了。

    Java接口和Java抽象类代表的就是抽象类型,就是我们需要提出的抽象层的具体表现。OOP面向对象的编程,如果要提高程序的复用率,增加程序的可维护性,可扩展性,就必须是面向接口的编程,面向抽象的编程,正确地使用接口、抽象类这些太有用的抽象类型做为你结构层次上的顶层。

    1、Java接口和Java抽象类最大的一个区别,就在于Java抽象类可以提供某些方法的部分实现,而Java接口不可以,这大概就是Java抽象类唯一的优点吧,但这个优点非常有用。
    如果向一个抽象类里加入一个新的具体方法时,那么它所有的子类都一下子都得到了这个新方法,而Java接口做不到这一点,如果向一个Java接口里加入一个新方法,所有实现这个接口的类就无法成功通过编译了,因为你必须让每一个类都再实现这个方法才行.

    2、一个抽象类的实现只能由这个抽象类的子类给出,也就是说,这个实现处在抽象类所定义出的继承的等级结构中,而由于Java语言的单继承性,所以抽象类作为类型定义工具的效能大打折扣。
    在这一点上,Java接口的优势就出来了,任何一个实现了一个Java接口所规定的方法的类都可以具有这个接口的类型,而一个类可以实现任意多个Java接口,从而这个类就有了多种类型。

    3、从第2点不难看出,Java接口是定义混合类型的理想工具,混合类表明一个类不仅仅具有某个主类型的行为,而且具有其他的次要行为。

    4、结合1、2点中抽象类和Java接口的各自优势,具精典的设计模式就出来了:声明类型的工作仍然由Java接口承担,但是同时给出一个Java抽象类,且实现了这个接口,而其他同属于这个抽象类型的具体类可以选择实现这个Java接口,也可以选择继承这个抽象类,也就是说在层次结构中,Java接口在最上面,然后紧跟着抽象类,哈,这下两个的最大优点都能发挥到极至了。这个模式就是“缺省适配模式”。
    在Java语言API中用了这种模式,而且全都遵循一定的命名规范:Abstract +接口名。

    Java接口和Java抽象类的存在就是为了用于具体类的实现和继承的,如果你准备写一个具体类去继承另一个具体类的话,那你的设计就有很大问题了。Java抽象类就是为了继承而存在的,它的抽象方法就是为了强制子类必须去实现的。

    使用Java接口和抽象Java类进行变量的类型声明、参数是类型声明、方法的返还类型说明,以及数据类型的转换等。而不要用具体Java类进行变量的类型声明、参数是类型声明、方法的返还类型说明,以及数据类型的转换等。

    我想,如果你编的代码里面连一个接口和抽象类都没有的话,也许我可以说你根本没有用到任何设计模式,任何一个设计模式都是和抽象分不开的,而抽象与Java接口和抽象Java类又是分不开的。

    接口的作用,一言以蔽之,就是标志类的类别。把不同类型的类归于不同的接口,可以更好的管理他们。把一组看如不相关的类归为一个接口去调用.可以用一个接口型的变量来引用一个对象,这是接口我认为最大的作用.

    自己的感想

    在平时的JAVA编程中,用JDBC连接数据库是非常常用的.而这里面涉及到的有DriverManager,Connection,Statement,其中第一个是类,后两者是接口.Connection用于获取一个指定了数据库的连接.而这个数据库的指定是在程序的开头或者配置文件中指定.那么通过DriverManager.getConnection就可以获得根据指定数据库的具体数据库连接对象.

    那么,问题的关键就在这里,在以后的程序中,我门所使用的这个Connection,都是这个接口引用的一个对象.它即可以是oracle数据库连接对象, 也可以是sql server连接对象.但光看内部程序,我们并不知道它具体是那种类型的.因为通过接口.它展现给我们的都是Connection类型的.不管我们换了什么数据库,程序中总是Connection conn=...

    但是假如我们不用Connection接口.而换用具体的类,那么如果我们只用一种数据库比如sql server,那我们就用这个SqlserverConnection类来实例一个对象然后在程序中调用.但是假设有天我们要换成mysql数据库呢?那么,程序用所有的SqlServerConnection是不是都要换成MysqlConnection呢,并且,方法可能都会失效.

    这就是接口的优势体现,如果用接口,我们不用去管程序中具体是在调用哪个类,我只要知道是调用具有某种共同属性的类.而这个类的指定都交给工厂类去完成.在程序内部,我们完全只能看见的是对接口的调用.这个接口就代表着具体的实现类了.

    现在学习MVC模式。使得WEB开发以多层的方式。而再这些层中关系比较密切的就是模型层,持久化层,然后是底层数据库。模型层中需要BO,DTO,VO。而持久化层就是DAO类啦。不过按照大型项目架构。每层之间都应该通过接口。

    这点比较重要。接口的作用是为了降低层之间的耦合度。这样,下层只对上层公开接口。而封闭了内部实现。这是好处1。第二呢就是当接口的实现改变时。上层的调用代码是不用改变的。最后一点呢。就是接口本身的好处了,那就是一个接口,多种实现。具体要用到那种实现由工厂指定.那么万一实现改变了,也只用改工厂,不用改程序.

    这是我自己的一点感想.---kellymalue

  • 首个单元测试

    2007-07-31 16:48:04

    一个单元测试是程序员编写的一段代码,用于执行另一段代码并确定那段代码的行为是否与程序员期望的一致.为了检验代码的行为与你期望的一致,你需要一些assertion(断言),它是个简单的方法调用,用于判断某个语句是否为真.具体的实现如下所示.

            public void asserTrue(boolean condition){

                    if(!condition){

                          abort();

                    }

           }  

    由于某种原因,当调用assertTrue()的时候条件不成立,那么程序将被终止,事实上你可以用断言来判断所有的事物.

             既然我们经常需要检测是否相等,如果能够有一个专门针对于数字的断言将会更为方便.例如为了检测二个整数是否相等,我们可以编写如下的断言.

            public void  assertEquals(int a, int b){

                    assertTrue(a==b);

            }

            为了使得概念更加具体,让我们来编写一个"寻找最大数"的函数,并对它进行测试.

            public class Largest {
                 /**
                  * return the Largest element of the list
                  *
                  * @param list A list of integers
                  * @return The Largest number in the given List
                  */
                 public static int largest(int[] list){
                           int index,max=Integer.MAX_VALUE;
                           for(index=0; index<list.length;index++){
                                    if(list[index]>max){
                                             max=list[index];
                                    }
                           }
                           return max;
                  }
           }

          你想有多少个测试?

          显然对于一个List而言,元素的位置对该方法的结果并不会产生影响;于是你马上会想到如下的测试(在下面我们将把输入输出写成"输入--->你所期望的输出"这样的格式)。

          [7,8,9]----->9

          [8, 9,7]----->9

          [9,7,8 ]----->9

          如果List中存在二个相等的最大值,将会出现什莫情况。

          [7,8,9,9,7]--->9

         如果这些元素是int类型,而不是对象类型,你可能不能不在意那个9会作为结果返回,只要返回其中的一个就行了。

         如果List中只有一个元素,那末结果会是什莫?

         [1]----->1

         如果List包含负数,结果有回是怎样?

          [-9,-8,-7]------->-7

          *测试一个简单的方法。

           首个测试非常的简单,因为除了代码以外还有许多的因素需要被测试:凌乱的类名,程序库的位置,还要确定代码能否被编译。在你的首个测试里假设一切都成立。

           首先,让我们传递一个没有重复的简单数组给函数,并对这种情况进行测试,下面是详细的代码。

           import junit.framework.TestCase;

           public class TestLarget extends TestCase {
                  public TestLarget(String name){
                          super(name);
                  }
                  public void testSimply(){
                          assertEquals(9,Largest.largest(new int[] {7,8,9}));
                  }
                  public void assrtEquals(int a, int b){
                         assertTrue(a==b);
                 }
          }

          运行刚才的代码后,你可能看到类似于下面的错误信息(在eclipse上的运行结果):

          junit.framework.AssertionFailedError: expected:<9> but was:<2147483647>
         at junit.framework.Assert.fail(Assert.java:47)
         at junit.framework.Assert.failNotEquals(Assert.java:282)
         at junit.framework.Assert.assertEquals(Assert.java:64)
         at junit.framework.Assert.assertEquals(Assert.java:201)
         at junit.framework.Assert.assertEquals(Assert.java:207)
         at TestLarget.testSimply(TestLarget.java:8)
         at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
         at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
         at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
         at java.lang.reflect.Method.invoke(Unknown Source)
         at junit.framework.TestCase.runTest(TestCase.java:154)
         at junit.framework.TestCase.runBare(TestCase.java:127)
         at junit.framework.TestResult$1.protect(TestResult.java:106)
         at junit.framework.TestResult.runProtected(TestResult.java:124)
         at junit.framework.TestResult.run(TestResult.java:109)
         at junit.framework.TestCase.run(TestCase.java:118)
         at junit.framework.TestSuite.runTest(TestSuite.java:208)
         at junit.framework.TestSuite.run(TestSuite.java:203)
         at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:478)
         at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:344)
         at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)

            whoops!为什莫和我们期望的不一致那?原来max=Integer.MAX_VALUE;这行代码出现了错误,应该是max=0,我们的想法是初始化max这样的一个值,任何的数字和其比较之后,可以立刻的取代它。修改以后重新的编译,并最终通过测试。

            如果最大数位于List的不同位置,那末将会产生什莫样的情况,显然bug经常出现在边界位置,上面的例子中边界是指list的首个、最后一个位置,下面的代码中,我们把三个断言写在一个测试中,但是我们每次只添加一个断言。

           public void testOrder(){
                   assertEquals(9,Largest.largest(new int[] {9,8,7}));
                   assertEquals(9,Largest.largest(new int[] {7,9,8}));
                   assertEquals(9,Largest.largest(new int[] {7,8,9}));
           }

           junit.framework.AssertionFailedError: expected:<9> but was:<8>
           at junit.framework.Assert.fail(Assert.java:47)
           at junit.framework.Assert.failNotEquals(Assert.java:282)
           at junit.framework.Assert.assertEquals(Assert.java:64)
           at junit.framework.Assert.assertEquals(Assert.java:201)
           at junit.framework.Assert.assertEquals(Assert.java:207)
           at TestLarget.testOrder(TestLarget.java:15)
           at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
           at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
           at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
           at java.lang.reflect.Method.invoke(Unknown Source)
           at junit.framework.TestCase.runTest(TestCase.java:154)
           at junit.framework.TestCase.runBare(TestCase.java:127)
           at junit.framework.TestResult$1.protect(TestResult.java:106)
           at junit.framework.TestResult.runProtected(TestResult.java:124)
           at junit.framework.TestResult.run(TestResult.java:109)
           at junit.framework.TestCase.run(TestCase.java:118)
           at junit.framework.TestSuite.runTest(TestSuite.java:208)
           at junit.framework.TestSuite.run(TestSuite.java:203)
           at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:478)
           at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:344)
           at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)

            当我们测试到最后一个断言的时候,我们发现函数的最大值为什莫会是8那?好像代码根本没有考虑List的最后一个元素,原来代码中for循环结束太早了,一个典型的off-by-one错误的一个例子。在我们的代码中:

            for(index=0; index<list.length-1;index++){

            实际应该是:

            for(index=0; index<=list.length-1;index++){

            for(index=0; index<list.length;index++){

            在继承了c的语言(包括java)中,第二中表述方式比较常用并且容易阅读。修改代码后从新测试运行。

             现在检测存在重复最大值的情况,编写并运行下面的代码。

             public void testDups(){
                        assertEquals(9,Largest.largest(new int[] {9,7,8,9}));
             }

             测试的结果和我们期望的一致。接下来我们需要测试仅仅存在一个元素的List.

              public void testOne(){
                        assertEquals(1,Largest.largest(new int[] {1}));
             }       

             Hey,他通过了!你正在朝着正确的方向前进,并确定我们的例子中的bug已经被除去了。我们再来调试全部为负值的List.

             public void testNegative(){
                        assertEquals(-7,Largest.largest(new int[] {-7,-8,-9}));
             }  

             junit.framework.AssertionFailedError: expected:<-7> but was:<0>
            at junit.framework.Assert.fail(Assert.java:47)
            at junit.framework.Assert.failNotEquals(Assert.java:282)
            at junit.framework.Assert.assertEquals(Assert.java:64)
            at junit.framework.Assert.assertEquals(Assert.java:201)
            at junit.framework.Assert.assertEquals(Assert.java:207)
            at TestLarget.testNegative(TestLarget.java:23)
            at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
            at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
            at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
            at java.lang.reflect.Method.invoke(Unknown Source)
            at junit.framework.TestCase.runTest(TestCase.java:154)
            at junit.framework.TestCase.runBare(TestCase.java:127)
            at junit.framework.TestResult$1.protect(TestResult.java:106)
            at junit.framework.TestResult.runProtected(TestResult.java:124)
            at junit.framework.TestResult.run(TestResult.java:109)
            at junit.framework.TestCase.run(TestCase.java:118)
            at junit.framework.TestSuite.runTest(TestSuite.java:208)
            at junit.framework.TestSuite.run(TestSuite.java:203)
            at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:478)
            at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:344)
            at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)

             whoops!这个零是从哪里来得那?看来用0初始化max是个错误的做法,因为我们实际需要初始             max=Integer.MIN_VALUE;从而使它比任何的负数都小.

  • 一个人的测试

    2007-05-16 09:50:19


  • Linux必学60个命令文件处理

    2007-05-08 16:07:13

  • 检测网站服务器的承受能力

    2007-04-26 13:57:05

  • 开心工作,享受生活

    2007-04-19 15:29:26

Open Toolbar