<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Sealyu

    --- 博客已遷移至: http://www.sealyu.com/blog

      BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
      618 隨筆 :: 87 文章 :: 225 評(píng)論 :: 0 Trackbacks

    長(zhǎng)方形和正方形

    正方形是否是長(zhǎng)方形的子類(lèi)的問(wèn)題,西方一個(gè)很著名的思辨題。

    正確的寫(xiě)法是:

    長(zhǎng)方形類(lèi):兩個(gè)屬性,寬度和高度;正方形類(lèi):一個(gè)屬性,邊。

    (LY注:這是至少流行了十年的思辨題目,最早來(lái)自于C++和Smalltalk領(lǐng)域。類(lèi)似的這種思辨問(wèn)題還有哪些呢?讓我不禁對(duì)哲學(xué)又感冒起來(lái)了。查閱資料時(shí)意外找到了一個(gè)討論區(qū),里面有讀者和作者關(guān)于此處的拓展討論,真讓人高興。)

    (LY注:書(shū)中沒(méi)有提契約即Design by Contract的概念。子類(lèi)應(yīng)當(dāng)完全繼承父類(lèi)的contract。《敏 捷軟件開(kāi)發(fā):原則、模式與實(shí)踐》一書(shū)中這樣寫(xiě),"基于契約設(shè)計(jì)(Design By Constract),簡(jiǎn)稱(chēng)DBC"這項(xiàng)技術(shù)對(duì)LISKOV代換原則提供了支持.該項(xiàng)技術(shù)Bertrand Meyer伯特蘭做過(guò)詳細(xì)的介紹:使用DBC,類(lèi)的編寫(xiě)者顯式地規(guī)定針對(duì)該類(lèi)的契約.客戶(hù)代碼的編寫(xiě)者可以通過(guò)該契約獲悉可以依賴(lài)的行為方式.契約是通過(guò) 每個(gè)方法聲明的前置條件(preconditions)和后置條件(postconditions)來(lái)指定的.要使一個(gè)方法得以執(zhí)行,前置條件必須為真. 執(zhí)行完畢后,該方法要保證后置條件為真.就是說(shuō),在重新聲明派生類(lèi)中的例程(routine)時(shí),只能使用相等或者更弱的前置條件來(lái)替換原始的前置條件, 只能使用相等或者更強(qiáng)的后置條件來(lái)替換原始的后置條件。  本書(shū)中長(zhǎng)方形的Contract是width和height可以獨(dú)立變化,這個(gè)contract在正方形中被破壞了。)

    (LY注:注意,我們所有討論的基礎(chǔ)都應(yīng)由類(lèi)的行為決定。這使得長(zhǎng)方形等類(lèi)是動(dòng)態(tài)的,而不是象現(xiàn)實(shí)生活中一樣是靜態(tài)的概念。)

     

    正方形不可以作為長(zhǎng)方形的子類(lèi)

    如果設(shè)定一個(gè)resize方法,一直增加長(zhǎng)方形的寬度,直到增加到寬度超過(guò)高度才可以。

    那么如果針對(duì)子類(lèi)正方形的對(duì)象調(diào)用resize方法,這個(gè)方法會(huì)導(dǎo)致正方形的邊不斷地增加下去,直到溢出為止。換言之,里氏法則被破壞掉了。

    這個(gè)例子很重要,它意味著里氏代換與通常的數(shù)學(xué)法則和生活常識(shí)有不可混淆的區(qū)別。

    (LY 注:常識(shí)認(rèn)為,正方形is a 長(zhǎng)方形,而且是一類(lèi)特殊的長(zhǎng)方形。但是在這里出了問(wèn)題,如果我們系統(tǒng)中不會(huì)有這樣的resize操作,是否正方形就可以作為長(zhǎng)方形的子類(lèi)了呢?看后文是可以的)

    代碼的重構(gòu)

    長(zhǎng)方形和正方形到底應(yīng)該是什么關(guān)系呢?

    它們應(yīng)該都是四邊形類(lèi)的子類(lèi)。四邊形類(lèi)中沒(méi)有賦值方法,因類(lèi)似上文的resize()方法不可能適用于四邊形類(lèi),而只能只用于不同的具體子類(lèi)長(zhǎng)方形和正方形。因此里氏代換原則不會(huì)被破壞。(LY注:針對(duì)需要賦值操作的情況)

    從抽象類(lèi)繼承

    應(yīng)盡量從抽象類(lèi)繼承,而不是從具體類(lèi)繼承。

    上文對(duì)長(zhǎng)方形和正方形的重構(gòu)使用了重構(gòu)的第一種方法。增加一個(gè)抽象類(lèi),讓兩個(gè)具體類(lèi)都成為抽象類(lèi)的子類(lèi)。

    記住一條指導(dǎo)性的原則,如果有一個(gè)由繼承關(guān)系形成的等級(jí)結(jié)構(gòu)的話(huà),在等級(jí)結(jié)構(gòu)樹(shù)圖上的所有樹(shù)葉節(jié)點(diǎn)都應(yīng)當(dāng)是具體類(lèi);而所有的樹(shù)枝節(jié)點(diǎn)都應(yīng)當(dāng)是抽象類(lèi)或者Java接口。

    問(wèn)答題

    1、  一個(gè)有名的思辨題,filename能不能作為string類(lèi)的子類(lèi)?

    答:不能。Filename對(duì)象不能實(shí)現(xiàn)string對(duì)象的所有行為。比如兩個(gè)string對(duì)象相加可以給出一個(gè)新的有效的string對(duì)象。而兩個(gè)filename對(duì)象相加未必會(huì)得到一個(gè)新的有效的Filename對(duì)象。

           另外,Java中的String類(lèi)型是final類(lèi)型,因此不可以繼承。

    2、  如果正方形的邊長(zhǎng)不會(huì)發(fā)生改變,是否可以成為長(zhǎng)方形的子類(lèi)呢?(LY注:不變正方形,就是邊長(zhǎng)不會(huì)發(fā)生變化的正方形,也就是遵守不變模式的正方形。不變(Immutable)模式,一個(gè)對(duì)象在對(duì)象在創(chuàng)建之后就不再變化。)

    答:可以。實(shí)現(xiàn)時(shí),父類(lèi)有兩個(gè)屬性寬度和高度。子類(lèi)有三個(gè)屬性寬度、高度和邊。針對(duì)每一個(gè)屬性,包含一個(gè)內(nèi)部變量,一個(gè)Set值方法,一個(gè)Get值方法。子類(lèi)正方形只需要將Set值方法不寫(xiě)任何語(yǔ)句即可。

    3、  從里式代換角度看JavaPropertiesHashtable的關(guān)系是否合適?

    答:不合適。在Java中,PropertiesHashtable的子類(lèi)。顯然Properties是一種特殊的Hashtable,它只接受string類(lèi)型的鍵(Key)和值(Value)。但是,父類(lèi)Hashtable可以接受任何類(lèi)型的鍵和值。這意味著,在一些需要非String類(lèi)型的鍵和值的地方,Properties不能取代Hashtable

    (LY注:合成/聚合復(fù)用原則中有更詳細(xì)的討論,應(yīng)使用合成/組合而不是繼承。它們是has a的關(guān)系而不是is a的關(guān)系。)




    另外,有一篇有意思的文章不贊同這個(gè)觀(guān)點(diǎn):

    本文假定讀者已經(jīng)了解有關(guān)正方形不是長(zhǎng)方形的相關(guān)內(nèi)容。
            
            之前人們討論的正方形長(zhǎng)方形的問(wèn)題的關(guān)鍵 在哪里?我覺(jué)得就在于改動(dòng)長(zhǎng)方形的邊的長(zhǎng)度。我們可以這么考慮一下,一個(gè)長(zhǎng)方形的instance的邊長(zhǎng)應(yīng)該是可變的嗎?我覺(jué)得一旦一個(gè)長(zhǎng)方形的邊長(zhǎng)改變 之后它就成了另一個(gè)長(zhǎng)方形了(一個(gè)新的instance)。所以長(zhǎng)方形類(lèi)里面不應(yīng)該有改變其邊長(zhǎng)的方法,一個(gè)長(zhǎng)方形實(shí)例各個(gè)的邊長(zhǎng)應(yīng)當(dāng)在new它的時(shí)候確 定下來(lái),并且它們應(yīng)當(dāng)是immutable的。基于這種考慮,我設(shè)計(jì)的長(zhǎng)方形和正方形的類(lèi)如下所示:
    //長(zhǎng)方形
    public class Rectangle {
      private final int width;
      private final int height;
     
      public Rectangle(int width, int height) {
        this.width = width;
        this.height = height;
      }
     
      public int getWidth() {
        return width;
      }

      public int getHeight() {
        return height;
      }
     
      public int getArea() {
        return width*height;
      }
    }

    //正方形
    public class Square extends Rectangle{
      private final int side;
     
      public Square(int side) {
        super(side, side);
        this.side = side;
      }
     
      public int getSide() {
        return side;
      }
    }

            這種繼承關(guān)系就既符合現(xiàn)實(shí)中的父子關(guān)系也遵循LSP。之所以這么設(shè)計(jì),我的想法是一個(gè)類(lèi)所具有的方法不應(yīng)當(dāng)能夠改變其本質(zhì)。比如有一個(gè)Men類(lèi),它可以有 eat(),sleep(),work(),makeLovewith(Person p)方法,但是如果你在里面定義denatureToWomen(),denatureToEunuch()就很不恰當(dāng)了,因?yàn)檫@改變了其本質(zhì),導(dǎo)致這個(gè) Men的實(shí)例不再屬于Men類(lèi)(至少已經(jīng)和現(xiàn)實(shí)不吻合)了。除非這兩個(gè)方法不能改變?cè)搶?shí)例本質(zhì),否則在Men里面定義這兩個(gè)方法本身就是有問(wèn)題的。不過(guò)如 果用下面這種方式定義也許可行:
    public Women denatureToWomen() {
      Women w = new Women();
      //set attributes here
      return w;
    }

    public Eunuch denatureToEunuch() {
      Eunuch e = new Eunuch();
      //set attributes here
      return e;
    }

    這樣一來(lái),調(diào)用denatureToWomen()會(huì)產(chǎn)生一個(gè)新的實(shí)例,原來(lái)的那個(gè)Men實(shí)例依然存在,這和現(xiàn)實(shí)生活依然不吻合,現(xiàn)實(shí)生活中一個(gè)實(shí)例不光可以上型(upcast),還可以平行型,寒。。。

    總之一句話(huà),一個(gè)類(lèi)的方法不應(yīng)該改變其實(shí)例的本質(zhì)。


    posted on 2009-12-23 15:39 seal 閱讀(2486) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): 設(shè)計(jì)模式
    主站蜘蛛池模板: 日本免费一区二区在线观看| 亚洲好看的理论片电影| 四虎在线免费视频| 国产免费播放一区二区| 亚洲国产成人久久一区二区三区| 亚洲性天天干天天摸| 日本激情猛烈在线看免费观看| 亚洲精品国产电影| 免费国产午夜高清在线视频| 亚洲日韩在线中文字幕综合| 亚洲欧洲国产精品香蕉网| 免费看国产曰批40分钟| 免费91最新地址永久入口| 免费福利在线观看| 亚洲AⅤ男人的天堂在线观看| 亚洲人成777在线播放| 亚洲精选在线观看| 亚洲国产精品无码一线岛国| 国产精品亚洲高清一区二区| 四虎在线视频免费观看视频| 免费一级毛片无毒不卡| 中文字幕乱码系列免费| 亚洲色丰满少妇高潮18p| 亚洲国产成人综合| 亚洲国产精品线观看不卡| 亚洲色成人网站WWW永久| 亚洲AV无码乱码在线观看性色扶 | 中文字幕人成人乱码亚洲电影 | 久久亚洲AV成人无码国产| 亚洲色婷婷一区二区三区| 亚洲精品一品区二品区三品区| 野花高清在线电影观看免费视频| 91大神免费观看| 美女内射毛片在线看免费人动物| 免费看黄网站在线看| 美女黄频免费网站| 亚洲午夜福利在线视频| 亚洲中文字幕一二三四区| 亚洲国产成人久久精品大牛影视 | 久久久久久久久久国产精品免费| a毛片久久免费观看|