設計模式-迪米特法則

迪米特法則

迪米特法則 (Law of Demeter, LoD),也被稱為“最少知識原則 (Principle of Least Knowledge)”,是面向對象設計中的一個重要原則。

核心思想:一個對象應該對其他對象有盡可能少的了解。

更具體地說,它規定了一個對象 O 中的一個方法 M 應該只調用以下類型對象的方法:

  1. 對象 O 本身 (this/self)。

  2. 作為方法 M 的參數傳遞進來的對象。

  3. 在方法 M 內部創建的對象 (局部對象)。

  4. 對象 O 的直接成員/組件對象 (實例變量/屬性)。

  5. 全局對象 (但應謹慎使用,通常不推薦)。

簡單來說,就是“只和你的直接朋友交談,不要和朋友的朋友交談”。

為什么叫“迪米特法則”?

這個名字來源于 1987 年在美國東北大學 (Northeastern University) 進行的一個名為 "Demeter Project" 的項目。該項目的目標是開發一種更容易維護和演化的面向對象系統。

目的和好處:

遵循迪米特法則的主要目的是降低類之間的耦合度 (Coupling),從而帶來以下好處:

  1. 提高模塊的獨立性:當一個模塊的內部實現發生改變時,由于它只與直接相關的模塊交互,因此對其他模塊的影響會降到最低。

  2. 增強系統的可維護性:修改一個類時,不需要關心太多其他類的內部細節,減少了連鎖反應的風險。

  3. 提高代碼的可復用性:低耦合的模塊更容易被復用到其他系統中。

  4. 降低復雜性:每個類只需要關注與自己直接相關的交互,使得系統結構更清晰。

  5. 更容易進行單元測試:由于依賴關系簡單,更容易對模塊進行隔離測試。

違反迪米特法則的常見表現 (壞味道):

當你在代碼中看到一長串的點號調用,例如 object.getA().getB().getC().doSomething(),這通常是違反迪米特法則的信號。這被稱為“鏈式調用”或“消息鏈 (Message Chains)”。

這種寫法的問題在于:

  • 當前對象不僅依賴于 object,還間接依賴于 A、B、C 的內部結構。

  • 如果 A、B 或 C 的任何一個類的接口發生改變(比如 getB() 方法沒了,或者返回類型變了),當前對象的代碼也可能需要修改,即使它本身并不直接關心 B 或 C 的具體實現。

如何遵循迪米特法則?

當發現違反迪米特法則的情況時,可以考慮以下重構方法:

  1. 封裝和委托 (Encapsulation and Delegation):

    • 如果對象 O 需要調用 object.getA().getB().doSomething(),可以考慮在 object 類中添加一個新方法,比如 doSomethingRelatedToObjectB(),這個新方法內部去處理與 A 和 B 的交互,然后 O 只需要調用 object.doSomethingRelatedToObjectB() 即可。

    • 這樣,object 封裝了與 A 和 B 的交互細節,O 只需要與它的直接朋友 object 對話。

    例子:

    • 不好的設計 (違反 LoD):

      class Wallet {private Money money;public Wallet(Money money) { this.money = money; }public Money getMoney() { return money; }
      }
      ?
      class Money {private int amount;public Money(int amount) { this.amount = amount; }public int getAmount() { return amount; }public void setAmount(int amount) { this.amount = amount; }
      }
      ?
      class Person {private Wallet wallet;public Person(Wallet wallet) { this.wallet = wallet; }
      ?public void pay(int amountToPay) {// 違反了 LoD:Person 通過 Wallet 拿到了 Money,然后操作 Moneyint currentAmount = wallet.getMoney().getAmount();if (currentAmount >= amountToPay) {wallet.getMoney().setAmount(currentAmount - amountToPay);System.out.println("支付成功: " + amountToPay);} else {System.out.println("余額不足");}}
      }
    • 好的設計 (遵循 LoD):

      class Wallet { // Wallet 作為 Money 的直接朋友private Money money;public Wallet(Money money) { this.money = money; }
      ?// Wallet 負責處理支付邏輯,而不是暴露 Money 對象public boolean spend(int amountToSpend) {return money.deduct(amountToSpend);}
      ?public int getCurrentBalance() {return money.getAmount();}
      }
      ?
      class Money {private int amount;public Money(int amount) { this.amount = amount; }public int getAmount() { return amount; }
      ?public boolean deduct(int amountToDeduct) { // Money 自己負責扣款if (this.amount >= amountToDeduct) {this.amount -= amountToDeduct;return true;}return false;}
      }
      ?
      class Person { // Person 的直接朋友是 Walletprivate Wallet wallet;public Person(Wallet wallet) { this.wallet = wallet; }
      ?public void pay(int amountToPay) {// Person 只和它的直接朋友 Wallet 交互if (wallet.spend(amountToPay)) {System.out.println("支付成功: " + amountToPay);} else {System.out.println("余額不足,當前余額: " + wallet.getCurrentBalance());}}
      }

      在這個改進后的例子中,Person 不再需要知道 Wallet 內部是如何管理 Money 的,也不需要直接操作 Money 對象。Person 只與 Wallet 交互,告訴它要支付多少錢。Wallet 負責具體的支付邏輯,它與它的直接朋友 Money 交互。

  2. 移動方法 (Move Method):

    • 如果一個方法過多地使用了另一個類的數據和方法,可以考慮將這個方法移動到那個類中。

需要注意的平衡:

  • 過度應用可能導致問題:如果為了嚴格遵守迪米特法則而創建大量僅僅是進行簡單委托的“包裝方法 (Wrapper Methods)”,可能會導致類的接口膨脹,反而增加系統的復雜性。

  • Getter 方法本身不一定違反 LoD:object.getData() 本身不違反迪米特法則,因為 Data 對象是 object 的一個組件(或者是通過參數傳入的,或者是局部創建的)。問題在于你如何使用這個返回的 Data 對象。如果你只是讀取 Data 的狀態(比如 data.getValue()),通常是可以接受的。但如果你接著調用 data.getAnotherObject().doSomething(),那就可能違反了。

  • 數據結構 (Data Structures) vs. 對象 (Objects):對于純粹的數據結構(例如,DTO - Data Transfer Object),其目的就是暴露數據,這時獲取其內部數據是正常的。迪米特法則更多地應用于行為豐富的對象。

總結:

迪米特法則是指導我們設計低耦合、高內聚系統的一個重要原則。它的核心思想是限制對象之間的知識,讓每個對象只與它的直接朋友交互。通過封裝和委托,我們可以有效地應用迪米特法則,從而創建出更易于維護、擴展和理解的軟件系統。但同時也要注意避免過度設計,找到一個合適的平衡點。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/85909.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/85909.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/85909.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

結構性-代理模式

動態代理主要是為了處理重復創建模板代碼的場景。 使用示例 public interface MyInterface {String doSomething(); }public class MyInterfaceImpl implements MyInterface{Overridepublic String doSomething() {return "接口方法dosomething";} }public class M…

Unity大型項目資源框架

?? Unity大型項目資源管理:低端機檢測后自動切換資源框架(大廠風格) ?? 框架目標 ? 啟動時檢測機型性能,判定設備等級 ? 同一資源有高配/中配/低配不同壓縮格式 ? 根據設備等級,加載對應資源包(AB) ? 支持動態切換(可用來切換特效/貼圖分辨率/模型LOD) ? 保證…

MATLAB仿真:偏振光在光纖通信中的應用研究_可復現,有問題請聯系博主

MATLAB仿真:偏振光在光纖通信中的應用研究 1. 研究概述 本文通過MATLAB仿真研究偏振光在光纖通信中的關鍵技術,包括偏振態生成、傳輸特性和檢測方法,重點分析偏振模色散(PMD)的影響機制,并設計偏振控制優化方案。 %% 主程序框架 clc; clear; close all; addpath(Polar…

CTA-861-G-2017中文pdf版

CTA-861-G標準(2016年11月發布)規范未壓縮高速數字接口的DTV配置,涵蓋視頻格式、色彩編碼、輔助信息傳輸等,適用于DVI、HDMI等接口,還涉及EDID數據結構及HDR元數據等內容。

C++核心編程_繼承方式

繼承的語法&#xff1a;class 子類 : 繼承方式 父類 繼承降屬性權限&#xff0c;不可升屬性權限 繼承方式一共有三種&#xff1a; 公共繼承 保護繼承 私有繼承 #include <iostream> #include <string> using namespace std;class Base1 { public:int m_A; p…

Dockerfile常用指令介紹

Dockerfile常用指令介紹 Dockerfile是一個文本文件&#xff0c;用于定義Docker鏡像的構建過程。下面介紹一些最常用的Dockerfile指令及其用法&#xff1a; 基礎指令 FROM - 指定基礎鏡像 FROM python:3.9-slim這是Dockerfile的第一個指令&#xff0c;用于指定構建鏡像的基礎鏡…

Spring中@Primary注解的作用與使用

在 Spring 框架中&#xff0c;Primary 注解用于解決依賴注入時的歧義性&#xff08;Ambiguity&#xff09;問題。當 Spring 容器中存在多個相同類型的 Bean 時&#xff0c;通過 Primary 標記其中一個 Bean 作為默認的首選注入對象。 核心作用&#xff1a; 解決多個同類型 Bean …

本地優先的狀態管理與工具選型策略

本地優先&#xff1a;合理把控狀態共享邊界 在 React 應用開發過程中&#xff0c;開發者容易陷入一個認知誤區——過度追求狀態的全局化。許多新手開發者在項目初期就急于引入 Redux、Zustand 或 Jotai 等狀態管理工具&#xff0c;將一些本應屬于組件內部的瑣碎狀態&#xff0…

OpenCV CUDA模塊圖像處理-----對圖像執行 均值漂移過程(Mean Shift Procedure)函數meanShiftProc()

操作系統&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 編程語言&#xff1a;C11 算法描述 執行一個均值漂移過程&#xff08;mean-shift procedure&#xff09;&#xff0c;并將處理后的點的信息&#xff08;它們的顏色和位置&#xff0…

硬件I2C和軟件I2C的區別

硬件I2C和軟件I2C的區別 一、硬件I2C 1、硬件IC的局限性及學習意義 盡管硬件IC外設在STM32等微控制器中提供了標準化的通信支持&#xff0c;但在實際應用中&#xff0c;其穩定性可能存在問題。例如&#xff0c;某些情況下外設會因事件檢測異常而進入死鎖狀態&#xff0c;僅能…

推薦12個wordpress企業網站模板

WordPress企業網站模板是一種專為企業網站設計的WordPress主題&#xff0c;旨在幫助企業創建專業、美觀且易于管理的網站。這些模板通常具備響應式設計、SEO優化、多語言支持等功能&#xff0c;能夠滿足不同行業和企業的需求。 WordPress企業網站模板的適用場景 企業官網&…

68道Hbase高頻題整理(附答案背誦版)

簡述什么是Hbase數據庫&#xff1f; Hbase是一個高可靠性、高性能、面向列、可伸縮的分布式存儲系統&#xff0c;它利用HBase技術在HDFS上提供了類似于Bigtable的能力。換句話說&#xff0c;Hbase是Apache Hadoop生態系統中的一部分&#xff0c;可以為大數據應用提供快速的隨機…

PyTorch——卷積操作(2)

二維矩陣 [[ ]] 這里面conv2d(N,C,H,W)里面的四個是 N就是batch size也就是輸入圖片的數量&#xff0c;C就是通道數這只是一個二維張量所以通道為1&#xff0c;H就是高&#xff0c;W就是寬&#xff0c;所以是1 1 5 5 卷積核 reshape 第一個參數是batch size樣本數量 第二個參數…

Linux之MySQL安裝篇

1.確保Yum環境是否能正常使用 使用yum環境進行軟件的安裝 yum -y install mysql-server mysql2.確保軟件包已正常完成安裝 3.設置防火墻和selinux配置 ## 關閉防火墻 systemctl stop firewalld## 修該selinux配置 vim /etc/selinux/config 將seliuxenforcing修改為sel…

Devops系列---python基礎篇二

1、列表 1.1 概念 格式&#xff1a; 名稱 [ “元素1”,“元素2”,…] #定義一個列表 computer ["主機","鍵盤","顯示器","鼠標"]類型方法用途查index(“元素”)查看元素索引位置count(“元素”)統計元素出現的次數reverse()倒序排…

LeetCode - 234. 回文鏈表

目錄 題目 快慢雙指針步驟 讀者可能的錯誤寫法 正確的寫法 題目 234. 回文鏈表 - 力扣&#xff08;LeetCode&#xff09; 快慢雙指針步驟 找到鏈表的中點&#xff08;find_mid函數&#xff09;&#xff1a; 使用快慢指針&#xff0c;慢指針每次走一步&#xff0c;快指針…

UniApp 全生命周期鉤子詳解

&#x1f449; 整理不易&#xff0c;如果本文對你有幫助&#xff0c;歡迎點個【贊 &#x1f44d;】【收藏 ?】【關注 &#x1f9e1;】 后續我們還將繼續分享實用的 UniApp 教程&#xff0c;比如&#xff1a; 文件上傳全局請求封裝狀態管理動態路由等… &#x1f4ee; 有任何…

探索NautilusTrader:下一代開源算法交易平臺的革命性突破

在金融科技的浪潮中,量化交易領域正經歷一場由開源技術驅動的變革。NautilusTrader(https://github.com/nautechsystems/nautilus_trader)作為一款高性能、生產級的算法交易平臺,正以其創新的設計理念和強大的技術架構重塑開發者的策略研發流程。 一、核心定位:打破回測與…

QT開發技術【ffmpeg + QAudioOutput】音樂播放器

一、 介紹 使用ffmpeg 4.2.2 在數字化浪潮席卷全球的當下&#xff0c;音視頻內容猶如璀璨繁星&#xff0c;點亮了人們的生活與工作。從短視頻平臺上令人捧腹的搞笑視頻&#xff0c;到在線課堂中知識淵博的專家授課&#xff0c;再到影視平臺上扣人心弦的高清大片&#xff0c;音…

[論文閱讀] (38)基于大模型的威脅情報分析與知識圖譜構建論文總結(讀書筆記)

《娜璋帶你讀論文》系列主要是督促自己閱讀優秀論文及聽取學術講座&#xff0c;并分享給大家&#xff0c;希望您喜歡。由于作者的英文水平和學術能力不高&#xff0c;需要不斷提升&#xff0c;所以還請大家批評指正&#xff0c;非常歡迎大家給我留言評論&#xff0c;學術路上期…