1. 程式人生 > >面試不在懵比,如何修煉面向物件的六大原則大法

面試不在懵比,如何修煉面向物件的六大原則大法

最近聽朋友A去面試遇到一些問題,問題並不難都是非常基礎的,但是確是我們最容易忽視的地方,這裡總結一下只為做記錄,各位觀眾老爺不喜勿噴吐舌頭;接下來我要開始裝逼了,哈哈

修仙睡覺

 場景:面試基礎問題

這個問題就是Java的核心思想:面向物件,這裡不展開面向物件的概念,面試題是回答一下面向物件的幾大原則,當時朋友A就一臉懵比(內心一萬個草泥馬),平時都是寫程式碼,況且面向物件這麼抽象的概念如何描述呢?(成妾做不到啊)。當時我也是懵比,就馬上去看了一下面向物件的原則,然後簡單的記錄一下。

面向物件的六大原則:

1、單一職責

2、開閉原則

3、里氏替換

4、依賴倒置

5、介面隔離

6、迪米特原則

是不是覺得記住了這6大原則就可以出去裝逼了?(不存在的,哈哈),正當你得意的說出這六大原則的時候面試官說解釋一下什麼是單一職責?我估計你會。。。。

欲言又止

廢話說太多了,接下來我們來一一解釋下概念性的問題:

1、單一職責

定義:就一個類而言,應該僅有一個引起它變化的原因。這個比較好理解就是自己做自己的事情,不要做超過自己職責範圍之外的事情(我想起了葫蘆娃七兄弟大笑);比如在Android原始碼設計模式中提到小明在寫快取模組的時候把所有的快取方式都是寫在一個類中;現實我們coding的時候也經常這樣,當我們完成後那業務邏輯的程式碼開起來是那麼酸爽(笑哭,埋坑中。。),比如釋出後產品說想改一下業務實現規則,這下你回去看你的程式碼發現自己都看不懂了(請不要給自己或則別人挖坑,偷笑),其實簡單說在coding中就是把一組相關性很高的函式、資料封裝在一個類中(這裡是沒毛病),如果有多個功能區分的函式我們可以拆開多個類,這就是簡單的單一職責。

2、開閉原則

官方定義:在軟體中的物件(類,模組、函式等)應該對於擴充套件是開放的,但是對於修改是封閉的。這句話通俗的理解為程式在完成以後,程式中一個類的實現只應該因錯誤而被修改,新的或者改變的特性應該通過新建不同的實現類,新建的類可以通過繼承的方式來重用原類的程式碼,讓程式更穩定靈活。這就是簡單的開閉原則。

3、里氏替換原則

定義所有引用基類的地方必須能透明的使用其子類的物件。我們知道面向物件的語言有三大特點是繼承、封裝、多型,里氏替換原則就是依賴繼承、多型兩大特性。通俗的說只要是父類能出現的地方子類就可以出現,而且替換為子類後不會出現任何錯誤或者異常,使用者不需要知道是父類還是子類,但是反過來就不行,有子類出現的地方,父類就未必適應。總結一下就是兩個字:

抽象;我們可以結合Android裡面View、Button、TextView的關係就很好理解了。該原則的核心原理就是抽象,而抽象又是依賴繼承特性,在面向物件中繼承的優點非常明顯,程式碼重用,子類擁有父類的方法屬性,程式碼擴充套件性高。但是缺點也是有的,繼承具有很強的侵入性,只要繼承了就必須擁有父類的屬性跟方法,降低了靈活性,會造成子類程式碼冗餘。里氏替換原則就是通過抽象建立規範,具體的實現在執行時替換掉抽象,保證系統的擴充套件性,靈活性與開閉原則往往是生死相依,不離不棄的。

4、依賴倒置原則

定義:模組間的依賴通過抽象發生,實現類之間不發生直接的依賴關係,其依賴關係是通過介面或抽象類產生的。簡單的說就是面向介面程式設計,這句話相信大家聽的最多了。比如小明之前只是實現了sd卡的快取,現在主管要求實現記憶體快取,小明這個時候應該把快取這個功能函式抽象為一個公共介面,然後去各自實現sd卡快取策略跟記憶體快取策略。如果下次主管再加一個別的快取方式,這個時候小明就可以不修改之前的快取策略而擴充套件一個新的快取策略就可以了。

5、介面隔離原則

定義:類間的依賴關係應該建立在最小介面上。介面隔離原則將非常龐大、臃腫的介面拆分成更小的和更具體的介面,這樣客戶端只需要關係他們感興趣的方法。介面隔離原則目的就是系統解耦,提高程式碼質量。比如在java6之前有個OutputStream的關閉問題,每次使用完後都要關閉,各種try...catch巢狀程式碼可讀性非常差。我們通過看原始碼知道有一個Closeable介面,該介面定義了一個可關閉物件,恰好OutputStream又實現了這個介面,所以我們可以封裝一個工具類來關閉物件,這裡直接貼程式碼:

public final class CloseUtils{
      private CloseUtils(){}
      /**
       **關閉Closeable物件
       ** @param closeable
       **/
       public  static  void closeQuietly(Closeable closeable){
         if(closeable!=null){
              try{
                 closeable.close();
              }catch(IOExecption e){
                  e.printStackTrace();
              }
          }
     }
}

我們直接在用到的地方呼叫就可以。CloseUtils的closeQuietly方法的基本原理就是依賴於Closeable抽象而不是具體實現,並建立在最小的依賴原則基礎上,只需要知道這個物件可以關閉,其他不需要關心。

6、迪米特原則

渡邊曜問號懵?

大家肯定會問這個是什麼鬼?根本沒聽過。。(原諒我的亂入)

定義:一個物件應該對其他物件有最少的瞭解(還有一個解釋是:只與直接的朋友通訊)。通俗的講,一個類應該對自己需要耦合或者呼叫的類知道最少,類的內部如何實現與呼叫者或者依賴者沒關係,呼叫者或者依賴者只需要知道它需要的方法即可,其他一律不關心。是不是覺得還是很難理解?我們舉個栗子,我們程式設計師去租房子,在一線城市租房大部分都是通過中介找房,假如我們只關心房間的面積跟租金,其他一概不管,中介只需要將符合我們要求的房子提供給我們就好了。接下來我們從code層面上會比較好理解一點。我們抽象出一個房間類(Room),中介類(Mediator),租戶類(Tenant)

    /**
     * 房間
     */
    public class Room{
        public float area;//面積
        public float price;//價格

        public Room(float area, float price) {
            this.area = area;
            this.price = price;
        }

        @Override
        public String toString() {
            return "Room{" +
                    "area=" + area +
                    ", price=" + price +
                    '}';
        }
    }

    /**
     * 中介
     */
    public  class Mediator{
        List<Room> rooms = new ArrayList<>();//中介手上的房間數
        public Mediator(){
            for(int i=0;i<5;i++){
                rooms.add(new Room(14+i,(14+i)*150));
            }
        }

        public List<Room> getRooms() {
            return rooms;
        }
    }

    /**
     * 租戶
     */
    public class Tenant{
        public float roomArea;
        public float roomPrice;
        public static final float diffPrice = 100.00001f;
        public static final float diffArea = 0.00001f;

        /**
         * 租賃房子
         * @param mediator
         */
        public  void rentRoom(Mediator mediator){
            List<Room> rooms = mediator.getRooms();
            for(Room room:rooms){
                if(isSuitable(room)){
                   System.out.println("租到房子啦!"+room);
                    break;
                }
            }
        }

        /**
         * 是否符合租戶要求
         * @param room
         * @return
         */
        private boolean isSuitable(Room room){
            return  Math.abs(room.price-roomPrice)<diffPrice&&Math.abs(room.area-roomArea)<diffArea;
        }
    }

從上面程式碼可以看到,租戶(Tenant)不僅依賴了中介(Mediator),還需要頻繁與房間(Room)打交道。租戶的要求只是要求通過中介找房子,如果把這些檢測條件都放在租戶(Tenant)中,那麼中介的功能就被弱化了,導致租戶與房子的耦合較高,還要關心房子的細節。當Room變化時候Tenant也必須跟著變化;而Tenant又與Mediator耦合,這樣就出現了多重關係。結構關係圖(UML)如下:


既然耦合嚴重,那我們就見招拆招。首要要明確的是租戶只跟中介通訊,從程式碼中提現就是將Room相關的操作從Tenant中移除,這些操作是屬於中介(Mediator)。重構程式碼後:

/**
     * 中介
     */
    public  class Mediator{
        List<Room> rooms = new ArrayList<>();//中介手上的房間數
        public Mediator(){
            for(int i=0;i<5;i++){
                rooms.add(new Room(14+i,(14+i)*150));
            }
        }

        /**
         * 中介租賃房子
         * @param area
         * @param price
         * @return
         */
        public Room rentOut(float area,float price){
            for (Room room:rooms){
                if(isSuitable(area,price,room)){
                    return room;
                }
            }
            return  null;
        }

        /**
         * 是否符合租戶要求
         * @param room
         * @return
         */
        private boolean isSuitable(float area,float price,Room room){
            return  Math.abs(room.price-price)<Tenant.diffPrice&&Math.abs(room.area-area)<Tenant.diffArea;
        }
    }

    /**
     * 租戶
     */
    public class Tenant{
        public float roomArea;
        public float roomPrice;
        public static final float diffPrice = 100.00001f;
        public static final float diffArea = 0.00001f;

        /**
         * 租賃房子
         * @param mediator
         */
        public  void rentRoom(Mediator mediator){
                   System.out.println("租到房子啦!"+mediator.rentOut(roomArea,roomPrice));
        }
    }

重構後結構圖:


我們只是將租戶對於房間的判斷操作放到了中介中,這本來就是屬於中介的職責,根據租戶設定的要求找到符合條件的房子,並將結果交給租戶就可以了。租戶不需要了解房間太多的細節,所以事情租戶與中介溝通就好,這樣“只與直接的朋友通訊”就能夠將租戶與中介、房間之間複雜關係中抽離出來,降低程式的耦合,穩定性更好。
以上就是Java核心面向物件的原則的解釋與場景分析。新手上路,請各位觀眾老爺df二連帶走。



PS:寫完這部落格的感覺就是

相關推薦

嘮嘮面試常問的Java 面向物件設計的六大原則

這篇文章主要講的是面向物件設計中,我們應該遵循的六大原則。只有掌握了這些原則,我們才能更好的理解設計模式。 我們接下來要介紹以下6

面向物件六大原則之單一

單一職責原則-SRP(Single Responsibility Principle) 通俗的說,即一個類只負責一項職責 如:類T負責兩個不同的職責:職責P1,職責P2。當由於職責P1需求發生改變而需要修改類T時有可能會導致原本執行正常的職責P2功能發生故障。 如:對資料庫的增刪查改,對資料

java面向物件六大原則

面向物件特點:         1:將複雜的事情簡單化。         2:面向物件將以前的過程中的執行者,變成了指揮者。 &

面向物件六大原則----里氏替換原則,依賴倒置原則

單一職責原則 英文名稱是Single Responsibility Principle,簡稱SRP 開閉原則英文全稱是Open Close Principle,簡稱OCP 里氏替換原則 英文全稱是Liskov Substitution Principle,簡稱LSP 依賴倒置原則 英文全稱是Depe

面向物件六大原則——單一職責原則

什麼是單一職責原則(Single Responsibility Principle, SRP)  在講解什麼是單一職責原則之前,我們先說一個例子,吊一下口味:我們在做專案的時候,會接觸到使用者,機構,角色管理這些模組,基本上使用的都是RBAC模型(Role-Ba

【設計模式】面向物件六大原則

主要內容 關於面向物件六大原則 單一職責原則(Single Responsibility Principle) 縮寫為SRP。 對於一個類而言,應該僅有一個引起它變化的原因。或者說一個類中應該是一組相關性很高的函式、資料的封裝。大意就是一個類應該只做一件事情,這就是職

面向物件六大原則(三):依賴倒置原則

出處:http://blog.csdn.net/zhengzhb/article/details/7289269 定義:高層模組不應該依賴低層模組,二者都應該依賴其抽象;抽象不應該依賴細節;細節應該依賴抽象。 問題由來:類A直接依賴類B,假如要將類A改為依賴類C,則必須通

Java設計模式之——面向物件六大原則

面向物件六大原則: 設計模式六大原則(1):單一職責原則 設計模式六大原則(2):開閉原則 設計模式六大原則(3):里氏替換原則 設計模式六大原則(4):依賴倒置原則 設計模式六大原則(5):介面隔離原則 設計模式六大原則(6):迪米特原則 設計模式六大

[面向物件六大原則] 里氏替換原則(LSP)

里氏替換原則 - Liskov Substitution Principle定義一:如果對每一個型別為S的物件O1,都有型別為T的物件O2,使得以T定義的所有程式P在所有的物件O1都替換成O2時,程式P的行為沒有發生變化,那麼型別S是型別T的子型別。定義二:所有引用基類的地方

面試如何修煉面向物件六大原則大法

最近聽朋友A去面試遇到一些問題,問題並不難都是非常基礎的,但是確是我們最容易忽視的地方,這裡總結一下只為做記錄,各位觀眾老爺不喜勿噴;接下來我要開始裝逼了,哈哈  場景:面試基礎問題 這個問題就是J

面向物件設計原則實踐:之五.迪米特原則介面隔離原則

六、迪米特(第三者互動)原則 1. 定義 每一個軟體單位對其他的單位都只有最少的知識,而且侷限於那些與本單位密切相關的軟體單位。   2. 分析 1) 迪米特法則就是指一個軟體實體應當儘可能少的與其他實體發生相互作用。 這樣,當一個模組修改時,就會盡量少的影響其他的

unity裡 lua 面向物件方式成員物件有可能殘留的問題。

TaskItemUI = { name = nil, content = nil, schedule = nil, schedulebg = nil, targetObj = nil, taskData = nil, curAimId =

js閉包的用途(匿名自執行函式快取實現封裝實現面向物件

文章轉載自:http://blog.csdn.net/sunlylorn/article/details/6534610 我們來看看閉包的用途。事實上,通過使用閉包,我們可以做很多事情。比如模擬面向物件的程式碼風格;更優雅,更簡潔的表達出程式碼;在某些方面提升程式碼的

java程式猿應該瞭解的10個面向物件設計原則(每次看都很有感悟特意拿來和大家共享)

Java程式設計最基本的原則就是要追求高內聚和低耦合的解決方案和程式碼模組設計。檢視Apache和Sun的開放原始碼能幫助你發現其他Java設計原則在這些程式碼中的實際運用。 面向物件設計原則是OOPS(Object-Oriented Programming System,

static的作用面向物件和麵向過程中的應用

 在C語言中,static的字面意思很容易把我們匯入歧途,其實它的作用有三條。 (1)先來介紹它的第一條也是最重要的一條:隱藏。 當我們同時編譯多個檔案時,所有未加static字首的全域性變數和函式都具有全域性可見性。為理解這句話,我舉例來說明。我們要同時編譯兩個原始檔,一

面向物件設計原則:不要STUPID堅持GRASP和SOLID

不要STUPID,堅持GRASP和SOLID 聽過SOLID編碼嗎?有人可能會說:這是描述設計原則的一個專業術語,由我們可愛的程式碼整潔之道傳教者鮑勃(羅伯特C. 馬丁)大叔提出,是一組用於指導我們如何寫出“好程式碼”的原則。 在程式設計界充滿了這樣由單詞首字母組成的縮略詞

看球學習兩誤 看世界盃 學面向物件

  java零基礎入門-面向物件篇(三) 類和物件 (上) 前面的基礎打的差不多了,基礎部分其實很多語言都大同小異,但是接下來的可是面嚮物件語言獨有的知識了,這是java中最核心最重要的部分,沒有之一。關於面向物件的一些概念,我在前面有篇文章簡單的提了一下,沒看過的同學快

(轉)面向物件設計原則--面試遭遇

         一、單一職責原則(SRP)        就一個類而言,應該僅有一個引起它變化的原因。軟體設計真正要做的許多內容,就是發現職責並把那些職責相互分離。測試驅動的開發實踐常常會在設計出現臭味之前就迫使我們分離職責。        二、開閉原則(OCP)        軟體實體(類、模組、函

面試總結之談談你對面向物件的理解

對面向物件的理解 在我理解,面向物件是向現實世界模型的自然延伸,這是一種“萬物皆物件”的程式設計思想。在現實生活中的任何物體都可以歸為一類事物,而每一個個體都是一類事物的例項。面向物件的程式設計是以物件為中心,以訊息為驅動,所以程式=物件+訊息。 面向物件有三大特性,封裝、

組合還是繼承這是一個問題?——由模式談面向物件原則之多用組合、少用繼承

                                                      組合還是繼承,這是一個問題                                              ——由模式談面向物件的原則之多用組合、少用繼承剛剛接觸模式或者學習模式的人,經常