如何合理地創建對象實例以降低程序類之間關係的耦合度

2020-12-22 楊教授工作室

軟體項目實訓及課程設計指導——如何合理地創建對象實例以降低程序類之間關係的耦合度

1對象創建是面向對象OOP編程技術中不可缺少的一件事情

創建類的對象實例是在所有的面向對象OOP編程開發中軟體應用系統的開發人員所必須要面對的問題,軟體應用系統中的業務活動是由各個對象實例之間的相互交互而構成的。但頻繁地創建對象實例不僅會降低軟體應用系統的整體運行的性能,也增加了不必要的程序類之間的耦合關係。

如下示圖中的Employee類與其中的TimeCard類之間就存在有「一對多」 形式的關聯關係,因為在Employee類的對象實例中耦合有TimeCard類的對象實例。

因此,如何正確和合理地創建出類的對象實例?什麼時候應該創建類的對象實例?如何保證所創建出的類的對象實例能夠適時地被銷毀?

其實上面的核心問題也就是軟體應用系統的開發人員如何能夠更高效地創建、並且松藕合、達到程序模塊的可擴展性?繼續採用如下的對象實例的創建形式嗎?

OracleDAO oneOracleDAO=new OracleDAO();

2合理地進行對象的創建以降低程序類之間關係的耦合度

兩個程序類之間如果存在或者出現有包括控制關係、調用關係、數據傳遞關係等情形時,這兩個程序類之間也就產生了耦合關係。而其中的調用關係是通過對目標對象實例中的對外的功能服務方法實現的(public方法),因此軟體應用系統的設計人員有必要合理地進行對象實例的創建以降低程序類之間關係的耦合度。

如下示圖中的Employee類與其中的Calculater類之間就存在有「依賴」關係,因為Calculater類以對象實例的方式參與到Employee類的功能方法calcSalary(Calculater strate)的參數中,也就是Employee類的功能方法calcSalary在執行時依賴於Calculater類的對象實例——Employee類與Calculater類之間就存在有「依賴」關係。

3、完善GRASP創建者設計模式的不足

通用職責分配軟體模式(GRASP)中的創建者設計模式只是指導軟體應用系統的設計人員決定創建對象的職責具體是由哪個目標程序類來承擔,但並沒有明確地告訴開發人員應該採用什麼形式和技術手段完成創建對象的具體工作。

作者在本文及後續相關的文章中試圖指導讀者在軟體應用系統的項目開發中如何合理地創建對象實例以降低程序類之間關係的耦合度,進一步提高對軟體應用系統中程序類之間關係的設計能力。

4、面向對象程式語言中的對象實例的傳統創建方式——利用new語句創建對象實例

(1)應用 new語句創建對象實例

在支持面向對象OOP編程技術的各種語言中(如Java語言)都提供有採用new語句並在程序類中提供公有(public)的構造方法實現對象實例創建的語法支持。比如,在Java程序設計語言的程序代碼中要創建出代表用戶信息的實體類UserInfoPO的對象實例時,可以採用下面的語句:

UserInfoPO oneUserInfoPO=new UserInfoPO();

使用new操作符創建對象實例一般需要三個基本的過程:首先為對象實例分配一定的內存空間,然後再調用對象所在類的構造方法,最後返回所創建出的對象實例的引用。

而且在Java程序設計語言中的對象只有實例化後,JVM虛擬機系統才真正地創建出它並為它分配出合適的內存空間;如果未實例化對象就直接使用它,則該對象將為空對象並拋出NullPointerException類型的異常。

在如下示圖所示的程序示例中,由於對StudentInfo類的對象只是定義了(StudentInfo oneStudent=null;),但並沒有對它進行對象實例化(oneStudent=new StudentInfo();)就直接使用StudentInfo類的對象oneStudent,從而產生出NullPointerException空指針類型的異常錯誤(參見如下示圖中的控制臺中的異常信息)。

當然,如果一個對象已經進行了對象實例化,但又採用了oneStudent=null;這樣的語句進行賦值,該對象實例已經被刪除後如果再繼續使用該對象,也同樣會產生出NullPointerException空指針類型的異常錯誤,讀者可以參見如下示圖中的程序代碼示例(注意其中黑體標識的語句)以及在控制臺中顯示的異常信息。

(2)了解對象的生命周期

Java程序設計語言中的對象生存期主要分為:創建、使用和銷毀(刪除)三個階段。當對象失除其作用域時,JVM虛擬機系統自動在後臺清除這些對象——如在某方法中創建了一個對象實例,當該方法返回時,也就無法再引用該對象實例。

當然,軟體應用系統的開發人員也可強行提前清除某一對象,這只需要將它置為null 或者調用該對象中的close()方法。比如,對於物理資料庫系統的資料庫連接對象、文件IO對象等在使用完畢後,應該及時地釋放該對象所佔用的系統資源、並清除該對象。

5、常規的應用new語句創建對象實例方式所存在的問題

常規採用new操作符語句進行對象實例創建的方式存在一定的問題,首先需要在對象創建的語句中直接指定目標類的名稱。一旦目標類名稱發生變化,就需要修改該new語句中的目標類名稱;其次在不同的方法中需要該對象實例時,都需要利用new語句對該對象進行創建。不僅產生了大量重複的對象實例創建的語句,而且也將對象實例創建的職責分散到不同的功能方法中。一旦對象實例的創建過程和要求發生變化時,就需要修改分散到不同的功能方法中的對象實例的創建語句,增加了代碼維護的工作量。

另外,有些程序類的對象實例是不應該重複地創建的——比如資料庫連接Connection對象等;其次,某些程序類只允許產生出單例的對象實例。對於對象實例創建時的這些基本的要求,採用普通的new操作符語句是做不到的。

因此,如何能夠更高效地創建對象實例、並且鬆耦合以達到程序模塊的可擴展性和可維護性?

6、利用工廠設計模式分離對象實例的創建邏輯和對象實例的使用邏輯

為了能夠降低程序類之間關係的耦合度,軟體應用系統的開發人員有必要合理和有效地創建對象——也就是需要分離對象實例的創建邏輯和對象的使用邏輯。對象的使用方只需要了解對象對外的功能方法的接口,而不需要自行負責對該對象的創建工作。封裝對象的創建過程和邏輯、並將對象的創建職責由某個特定的功能類(如工廠類)承擔——這是對面向對象OOP設計方法中的「單一職責設計原則」的具體應用。

一旦能夠分離對象實例的創建邏輯和對象實例的使用邏輯,也就能夠降低由於對象實例的創建而產生的程序類之間關係的耦合度。當被創建的目標對象所在的程序類的代碼發生變化時,不需要修改分散到不同功能方法中的對象創建的new操作符語句,而只需要集中修改承擔對象實例創建職責的工廠類中的對象實例的創建方法。

下圖所示的黑體部分所標識的代碼,表示在示例項目銀行帳戶信息管理系統中,利用資料庫連接對象的工廠類ConnectDBFactory中的newConnectDBBean方法創建出對應的對象實例的程序代碼。

如下程序為資料庫連接對象的工廠類ConnectDBFactory類的完整程序代碼示例,在ConnectDBFactory工廠類中應用了觀察者設計模式。其中的工廠方法newConnectDBBean接收需要創建對象的類名稱參數,並返回目標對象所在的接口類型的對象實例。

package com.px1987.webbank.factory;

import com.px1987.webbank.config.ClassNameConfig;

import com.px1987.webbank.dao.*;

import com.px1987.webbank.dao.inter.ConnectDBInterface;

import com.px1987.webbank.exception.*;

import java.util.Observable;

import java.util.logging.*;

public class ConnectDBFactory extends Observable {

public ConnectDBInterface newConnectDBBean(String connectDBBeanClassName) throws WebBankException {

ConnectDBInterface connectDBBean=null;

Class oneClass=null;

try {

oneClass = Class.forName(connectDBBeanClassName);

}

catch (ClassNotFoundException e){

int logImpleKind=Integer.parseInt(ClassNameConfig.getProperty("logImpleKind"));

this.addObserver(LogInfoFactory.newLogInstance(logImpleKind));

this.setChanged(); //注意要設置變化點

notifyObservers(e); //當出現異常時將通知各個觀察者

throw new WebBankException("不能正確地獲得"+connectDBBeanClassName+"類");

}

try {

connectDBBean = (ConnectDBInterface)oneClass.newInstance();

}

catch (InstantiationException e){

int logImpleKind=Integer.parseInt(ClassNameConfig.getProperty("logImpleKind"));

this.addObserver(LogInfoFactory.newLogInstance(logImpleKind));

this.setChanged(); //注意要設置變化點

notifyObservers(e); //當出現異常時將通知各個觀察者

throw new WebBankException("不能正確地創建"+connectDBBeanClassName+"類的對象實例");

}

catch (IllegalAccessException e) {

int logImpleKind=Integer.parseInt(ClassNameConfig.getProperty("logImpleKind"));

this.addObserver(LogInfoFactory.newLogInstance(logImpleKind));

this.setChanged(); //注意要設置變化點

notifyObservers(e); //當出現異常時將通知各個觀察者

throw new WebBankException("不能正確地創建"+connectDBBeanClassName+"類的對象實例");

}

return connectDBBean;

}

}

如何正確地應用Web MVC架構模式分離表示層和模型處理層耦合關係

軟體項目實訓及課程設計指導—Web表示層典型功能實現的應用實例

項目實訓及課程設計——如何合理地設計軟體應用系統的Web表示層

如何合理地設計軟體應用系統中數據訪問服務層內的各個功能程序類

如何應用數據訪問服務層分離系統中的業務層和持久層之間耦合關係

相關焦點

  • 課程設計指導——如何應用Java反射技術靈活地創建程序類對象實例
    軟體項目實訓及課程設計指導——如何應用Java反射技術靈活地創建程序類的對象實例1、如何應用屬性配置文件實現對系統中的配置信息進行讀寫操作Java中的屬性配置文件主要可以作為軟體應用系統及項目的配置文件,比如許多J2EE的開源框架系統中都提供了屬性配置文件作為該應用框架的對外配置文件
  • 如何正確地創建和銷毀軟體應用系統中網絡通訊中的Socket對象實例
    ,客戶端程序需要在客戶機程序中創建一個Socket對象,Socket類有多種不同形式的構造方法。在JDK API中與Socket通信有關的各個接口和功能類都定義在java.net包中,而在對應的伺服器端程序中則需要創建出ServerSocket類的對象實例。
  • 如何正確地創建和銷毀軟體應用系統中JDBC資料庫連接對象實例
    然後在Connection對象實例的基礎上再構造出相應的語句(如Statement類型或者PreparedStatement類型)類對象實例。所有的資料庫訪問操作都是在Connection對象實例的基礎上完成的,它代表對資料庫表中數據訪問的一次會話。因此,軟體應用系統的開發人員有必要正確地創建出Connection類的對象實例。
  • java創建對象的過程詳解(從內存角度分析)
    實例化時候,java虛擬機就會為其分配內存來存放自己及其從父類繼承過來的實例變量。在為這些實例變量分配內存的同時,這些實例變量先會被賦予默認值(零值)。在內存分配完成之後,Java虛擬機才會對新創建的對象賦予我們程式設計師給定的值。
  • Java基礎入門篇之面向對象和類的定義
    一、面向對象1.面向對象是在程序中使用對象來映射現實中的事物,對象的關係來描述事物之間的聯繫。2.面向對象的特點:封裝性:封裝是將類的某些信息隱藏在類內部,不讓外部程序去訪問,需要通過這個類提供的方法來實現對隱藏信息的操作和訪問。封裝也是面向對象的核心思想,把類的屬性和行為封裝起來,不給外界知道具體的細節。繼承性:繼承是類與類的一種關係,而不是對象在繼承。
  • 黑馬程式設計師:java基礎知識清單之如何創建類與對象?
    類:是一組相關屬性和行為的集合,是抽象的。屬性:就是該事物的狀態信息。行為:就是該事物能夠做什麼。對象:是一類事物的具體體現。對象是類的一個實例,是具體的。類與對象的關係:類是對象的模板,對象是類的實體。創建類-格式:成員變量(屬性):變量類型 變量名;成員方法(行為):public 返回值類型 方法名稱(參數){方法體};注意:1、成員變量直接寫在類當中,是在方法外邊。
  • 什麼是JavaScript對象?如何創建並引用?這就告訴你!
    1.1 什麼是類類:就是具有相同的屬性和方法的集合。人類,動物類,家電類等。1.2 什麼是對象對象:就類中的一個具體的實物。人類-具體某一個人(張三丰)。動物類-(一個具體的動物-東北虎),家電類-(具體一個比如說電視。)js 中我們包含哪些對象呢?內置對象(本地對象):Math對象,Number對象,Date對象等,系統給我們提供好的,我們拿過來用就可以了。
  • 軟體項目實訓及課程設計指導——實體類結構和類關係的設計示例
    通常把業務領域中的各種名詞——例如客戶、訂單、商品等信息可以作為應用系統中的實體域對象。在如下示圖中的各個資料庫表所體現出的數據對象都可以設計為對應的實體類,然後在數據訪問邏輯組件中訪問和操作這些實體類的對象實例。
  • EffectiveJava-1-創建和銷毀對象
    可以不用在每次調用時都創建一個新的對象,可以使用預先構建好的對象,或將構建好的對象緩存起來,進行重複利用,適用於經常請求創建相同對象,並且創建對象的代價很高,如常見的單例模式寫法就是對這一點的應用;實例受控的類:能為重複的調用返回相同的類,有助於類控制某個時刻哪些實例應該存在,功能如下:1.確保它是一個Singleton或者是不可實例化的;2.使得不可變的類不會存在兩個相等的實例
  • Python並發編程很簡單,一文幫你搞清如何創建線程類
    ,它就是繼承threading模塊的Thread類創建線程類哦!首先說一下通過繼承threading模塊的Thread類來創建線程類的步驟哦,它主要有3步哦:第一步:首先定義Thread類的子類哦,然後再重寫這個子類的run()方法哦,其實呢,這裡的run()方法就表示線程需要完成的任務哦,所以哈,你當然可以把這個run()方法稱作線程的執行體哦
  • 如何正確地設計J2EE應用系統持久層中的各個組件結構及組件間關係
    軟體項目實訓及課程設計指導——如何正確地設計J2EE應用系統持久層中的各個組件結構及關係1、了解J2EE應用系統數據持久層中的各個組件類的類型在J2EE系統平臺下的軟體應用系統的數據持久層,一般都包含有如下的各個組件類:實體類、數據訪問對象類(包括接口和對應的實現類
  • 如何應用策略設計模式分離JDBC資料庫連接中的外部環境信息
    如下示圖為體現策略設計模式中的各個類之間關係的UML類圖,依據此UML類圖,可以了解到策略設計模式提供了一種替代面向對象設計方法中的繼承的方法,將繼承改變為組合,而且既保持了繼承的優點(實現代碼重用)但又比繼承更具有靈活性(「算法」獨立,可以任意地擴展)。
  • C#類之間存在繼承關係,什麼是繼承?如何實現類與類之間的繼承?
    在C#中,可以簡單地將繼承理解為:大分類和小分類之間存在的關係,小分類具有大分類所有的特徵,但大分類不一定具備所有小分類的特徵。將現實生活當中的繼承與C#語言中的繼承對應起來就是:指一個對象(轎車)直接使用另一對象(汽車)的屬性和方法。2.
  • 如何保證軟體應用系統架構設計結果的可擴展性和可重用性(上篇)
    (2)合理地對軟體應用系統在縱向方向進行分層隔離設計通過合理地對軟體應用系統在縱向方向進行分層隔離設計——如目前的C/S和B/S等架構模式中的各個分層策略,將允許設計人員將複雜的軟體應用系統中所涉及的各個方面的問題分解成多個不同層次的實現。
  • JavaScript開發創建類:向模塊化進軍
    ▲Javascript Web富應用開發圖書推薦  對於靜態的類來說,JavaScript 對象直接量就已經夠用了,但它對使用繼承和實例來創建經典的類往往更有幫助。有必要強調一下:JavaScript 是基於原型的程式語言,並沒有包含內置類的實現。但通過JavaScript 可以輕易地模擬出經典的類。  JavaScript 中的類口碑並不太好,因為「不夠JavaScript」而飽受批評。jQuery 並沒有涉及太多架構方法和繼承模式,這讓JavaScript 開發者確信自己不必考慮太多架構性的東西,甚至覺得類的用處不大或乾脆禁用類。
  • 面向對象總結(都是乾貨喲)
    static標記的變量或方法由整個類(所有實例)共享,如訪問控制權限允許,可不必創建該類對象而直接用類名加『.』調用。static成員也稱類成員或靜態成員,如:類變量、類方法、靜態方法等。這是因為,對於非靜態的方法和變量,需要先創建類的實例對象後才可使用,而靜態方法在使用前不用創建任何對象。靜態方法不能以任何方式引用this和super關鍵字。與上面的道理一樣,因為靜態方法在使用前不用創建任何實例對象,當靜態方法被調用時,this所引用的對象根本就沒有產生。
  • Java並發編程:對象的發布與逸出,夯實你的基礎
    而在某些情況下,我們又需要發布某個對象,但如果在發布時要確保線程安全性,則可能需要同步。發布內部狀態可能會破壞封裝性,並使得程序難以維持不變性條件。例如,如果在對象構造完成之前就發布該對象,就會破壞線程安全性。當某個不應該發布的對象被發布時,這種情況就被稱為逸出( Escape)。現在,我們首先來看看一個對象是如何逸出的。