DesignPattern系列__05開閉原則

来源:https://www.cnblogs.com/JackHou/archive/2019/08/05/11303973.html
-Advertisement-
Play Games

介紹 開閉原則是編程設計中最基本、最重要的原則。 定義:一個軟體實體如類、方法和模塊等,應該對擴展(提供方)開放,對修改(使用方)關閉。用抽象構建框架,用實現擴展細節。 也就是說,在需求發生新的變化時,我們不應該修改原來的代碼,而應該通過擴展來滿足新的需求。 例子引入 我們要實現一個畫圖的功能,能夠 ...


介紹

開閉原則是編程設計中最基本、最重要的原則。

定義:一個軟體實體如類、方法和模塊等,應該對擴展(提供方)開放,對修改(使用方)關閉。用抽象構建框架,用實現擴展細節。

也就是說,在需求發生新的變化時,我們不應該修改原來的代碼,而應該通過擴展來滿足新的需求。

例子引入

我們要實現一個畫圖的功能,能夠畫出圓形、矩形、三角形等,最常見的思路就是利用面向對象的思想,抽象出一個所有圖形對象的基類Shape,具體的圖形如矩形、圓形燈繼承自該類。在Shape中定義一個變數shapeType來保存具體的圖形的類型。
定義一個繪圖類GraphicEditor,在執行具體的繪圖方法(如畫一個矩形)時,根據傳入的shapeType來執行對應圖形的繪製方法。
類圖設計如下:

功能初步實現了,但是有什麼缺陷嗎?讓我們來給項目適當的“鬆鬆土”:現在我們想要畫一個三角形,如何實現呢?
也很簡單:再定義一個類Triangle繼承自Shape,並且在GraphicEditor修改方法,加入對三角形的類型判斷,具體的代碼如下:

public class Ocp {
    public static void main(String[] args) {
        GraphicEditor graphicEditor = new GraphicEditor();
        graphicEditor.drawShape(new Rectangle());
        graphicEditor.drawShape(new Circle());
        graphicEditor.drawShape(new Triangle());
    }
}

//這是一個用於繪圖的類 [使用方]
class GraphicEditor {
    //接收Shape對象,然後根據type,來繪製不同的圖形
    public void drawShape(Shape s) {
        if (s.shapeType == 1)
            drawRectangle(s);
        else if (s.shapeType == 2)
            drawCircle(s);
        else if (s.shapeType == 3)
            drawTriangle(s);
    }

    //繪製矩形
    public void drawRectangle(Shape r) {
        System.out.println(" 繪製矩形 ");
    }

    //繪製圓形
    public void drawCircle(Shape r) {
        System.out.println(" 繪製圓形 ");
    }

    //繪製三角形
    public void drawTriangle(Shape r) {
        System.out.println(" 繪製三角形 ");
    }
}

class Shape {
    int shapeType;
}

class Rectangle extends Shape {
    public Rectangle() {
        super.shapeType = 1;
    }
}

class Circle extends Shape {
    public Circle() {
        super.shapeType = 2;
    }
}

//新增畫三角形
class Triangle extends Shape {
    Triangle() {
        super.shapeType = 3;
    }
}

OK,新的需求也實現了,現在,發現問題了嗎?
我們每次遇見新需求之外,除了定義新的圖形類,還要對類GraphicEditor進行修改。
根據前面提到的“開閉原則”中提到的,應該對修改關閉,對擴展開放,我們不應該修改類GraphicEditor,這樣會嚴重影響代碼的穩定性和可維護性。
現在,我們嘗試按照“開閉原則”來實現這個功能。
根據“開閉原則”,我們應該封裝變化,在這裡,我們在Shape中定義一個抽象的繪圖方法,併在各自實現類內進行具體實現。在類GraphicEditor中,只定義一個接受參數為抽象(Shape)的方法,使得類不再去受到類型影響,滿足了“開閉原則”。
具體的代碼如下:

public class Ocp {
    public static void main(String[] args) {
        //使用看看存在的問題
        GraphicEditor graphicEditor = new GraphicEditor();
        graphicEditor.drawShape(new Rectangle());
        graphicEditor.drawShape(new Circle());
        graphicEditor.drawShape(new Triangle());
    }
}

//這是一個用於繪圖的類 [使用方]
class GraphicEditor {
    //接收Shape對象,然後根據type,來繪製不同的圖形
    public void drawShape(Shape s) {
        s.draw();
    }


}

abstract class Shape {
    int shapeType;
    //定義一個抽象的畫圖方法
    public abstract void draw();
}

class Rectangle extends Shape {
    public Rectangle() {
        super.shapeType = 1;
    }

    @Override
    public void draw() {
        System.out.println("繪製矩形");
    }
}

class Circle extends Shape {
    public Circle() {
        super.shapeType = 2;
    }

    @Override
    public void draw() {
        System.out.println("繪製圓形");
    }
}

//新增畫三角形
class Triangle extends Shape {
    Triangle() {
        super.shapeType = 3;
    }

    @Override
    public void draw() {
        System.out.println("繪製三角形");
    }
}

在改進的代碼中,我們將畫圖方法進行抽象,定義在基類Shape中,並通過子類各自實現對應的畫圖方法。並且,對於類GraphicEditor而言,只需定義一個接受基類作為參數的方法即可,代碼變得整潔、易於維護。

使用註意事項

在實際使用中,需要註意以下幾個方面:

1.抽象約束

這點的含義包含三個意思:
1.通過介面或者抽象類約束擴展,對擴展進行邊界限定,不允許出現在介面或者抽象類中沒有定義的public方法;
2.參數類型,要儘量使用介面或者抽象類,不應該使用實現類。
3.抽象層作為約束,應該儘量保持穩定,一旦確定不容修改。

2.元數據控制模塊行為

在實際開發中,要儘量使用註解或者配置文件來控製程序的行為,減少重覆開發。比如搭建ssm框架中,使用註解或者配置文件來註入bean。

3.約定優於配置

對於大家普遍遵循的章程或者約定,我們要嚴格遵守,這樣能減少配置文件的編寫。比如MyBatis框架對xml文件的掃描,預設會去和介面同名的包下去查找,只要我們遵循這一約定, 就無需格外配置。

4.封裝變化

對變化的封裝包括兩點:
1.相同的變化,應該封裝到一個介面或者抽象類中;
2.不同的變化,應該封裝到不同的介面或者抽象類中,不應該有兩個不同的變化封裝在一個介面或者抽象類中。

一句話總結

開閉原則,是一切設計模式的基礎,可以說其他原則和設計模式都是為了實現開閉i原則。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 08.05自我總結 一.顯示方式 1.display 常用的三種方式 2.vertical align 文本垂直對齊方式: |` top bottom` ...
  • 想瞭解原型和原型鏈,我覺得首先我們得知道javascript里有一個Object 與 Function,它倆都是構造函數,當然函數也是一個對象。我們列印Object 與 Function看一下, 那麼這個Object 與 Function 之間有何關係與區別呢?首先您只需要記住: 1. 所有普通對象 ...
  • 在使用ajax發送非同步請求時,遇到一個問題,就是在回調函數中接受到了非同步響應的數據,但是在前端頁面中的表單中卻不回顯該數據。 前端頁面在 第一次載入數據的同時 發送非同步請求,但是 這個 非同步響應 是在 頁面載入完數據之後 到達的,因此在 非同步響應 到達之後(這時表單數據已載入完畢) 需要第二次將數... ...
  • 大家好,我是一葉,經過一段時間對vue的學習,我打算把vue做一個系列,把踩過的坑和大家分享一下。 現在開始第一章:vue引用並封裝echarts 在文章開始前,我先舔波echarts(真香)。阿裡的G2和百度的echarts都是很不錯的,echarts上手難度小,並且用戶多,文檔多,生態環境較好, ...
  • 這個系列博客專註電腦各種理論知識 如果專註於代碼的優美、或者專註於業務,或許這些知識並不是很重要 如果剛剛編程入門,或許這些知識不太能看懂 但是專註於技術且工作一定時間的話,這些可能還是很有用的 很多都是大學的電腦專業知識,以及軟考高級職稱架構師、分析師中的知識點。 我儘量將知識點與應用實踐向結 ...
  • 阻塞與非阻塞 阻塞:A程式調用B程式,A等待返回結果,等待中A程式線程被占用,不進行其它操作。 非阻塞:A程式調用B程式,A程式線程不被占用。 阻塞--》非阻塞 的最常用解決方案:緩存,A將請求依次裝入緩存中,A不管與B是否執行完成,由B隨後依次處理,返回給A。 同步與非同步 同步:一個線程依次執行所 ...
  • 一、小案例分析 1、功能需求: 實現一個發送信息的功能,要便於擴展與維護。(1)發送信息的工具有很多,比如簡訊、微信、郵件、QQ等。(2)選擇某個工具進行信息發送。 2、小菜雞去實現: (1)定義一個發送工具的父類(介面),並將各種發送工具作為子類(實現類)。(2)定義一個選擇發送工具的類,用於調用 ...
  • 架構雜談《九》 微服務與輕量級通信機制 微服務架構是一種架構模式,它提倡將單一應用程式劃分成一組小的服務,服務之間胡亮協調、互相配合,為用戶提供最終價值。在微服務架構中,服務與服務之間通信時,通常是通過輕量級的通信機制,實現彼此間的互通互聯、互相協作。所謂輕量級通信機制,通常是指與語言無關、與平臺無 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...