5

什麼是一些常見的最佳實踐方法(或功能模塊等),設計用於平衡信息隱藏在程序界面的願望和抽象的適當水平,在引進的固有問題隱藏依賴關係?信息隱藏與隱藏的依賴

更具體,假設我編寫一個稱爲getEmployeePhoneNbr(EMPLOYEEID)過程。在內部,該過程通過查詢由employeeId關閉的數據庫表來實現。我想隱藏這些實現細節,但是現在該過程依賴於外部文件,這會在環境發生變化時阻止其使用。

任何時候過程使用外部資源(文件,數據庫等)時都會發生同樣的情況。在程序中硬編碼使用該資源感覺不對,但我不確定替代方案是什麼。

請注意,我不是在面嚮對象語言的工作;盡我所能,我最感興趣的是可以廣泛應用於任何類型語言的回覆。

謝謝, 馬特

+1

你的語言是否支持函數指針? – 2009-08-11 10:22:31

+0

函數指針上的+1。圍繞這個問題的任何解決方案几乎都會歸結爲函數指針。 – kyoryu 2009-08-12 03:09:03

+0

我正在使用SAS,它(在我看來)有大量的語言缺陷;在SAS中沒有函數指針(事實上,直到最新版SAS沒有用戶定義的函數 - 必須使用SAS(不可否認非常豐富)的宏函數進行補償。因此,當我提到「模塊「在SAS的背景下,我的意思是SAS宏。 – 2009-08-13 12:52:33

回答

0

你可以提供某種情境/環境的對象。說:

type Environment = record 
     DatabaseHandle: ...; 
     ... 
    end; 

    Employee = record 
     ID: integer; 
     Name: string; 
     ... 
    end; 


function OpenEnvironment (var Env: Environment): boolean; 
begin 
    ... 
end; 

procedure CloseEnvironment (var Env: Environment); 
begin 
    ... 
end; 

function GetEmployeeById (var Env: Environment; ID: integer; var Employee: Employee): boolean; 
begin 
    ... load employee using the data source contained in environment ... 
end; 

(Pseudo-Pascal)。優點是,您可以使用環境結構來存儲擴展的錯誤信息和其他全局狀態,這樣可以避免PITA,它是Unix的errno或Window的GetLastError。這種方法的另一個優點是,所有的API都可以重入,並且通過使用每個線程的專用環境,作爲結果,線程安全。

這種方法的缺點是,您將不得不將其他參數傳遞給所有的API。

1

這是一個非常棘手的問題需要解決,您的實現語言是否是面向對象的或沒有(在任何情況下對象的方法通常可以不管是否編程語言支持它們作爲一種語言結構,應用,所以我所描述我就對象而言)

解決方案,你想做什麼就能做的是equivilantly把所有的數據存儲。事實上,這幾乎是不可能的,你必須選擇一個範例並接受它的限制。例如,可以將抽象的設計基於RDBMS範例(連接/查詢/獲取)並嘗試封裝對相同接口的文件的訪問。

我成功地使用了一種方法,以避免嵌入數據檢索(在你的情況下)的僱員「對象」,因爲這創建了一個耦合,是關閉之間的員工抽象的程序和存儲和檢索數據。

相反,我創建一個單獨的對象,負責檢索數據構建Employee對象,進而構建從數據Employee對象。我現在可以從任何數據源構建一個Employee,只要我可以將數據轉換爲適當的通用結構。 (我有關聯數組的語言支持的優點,它簡化了大量傳遞元組的過程,如果開發語言難以或不可能做到這一點,則可能會遇到麻煩)。

這也使得應用程序更容易測試,因爲我可以在我的單元測試中直接構造Employee「object」而不必擔心創建數據源(或者上次存在的數據是否仍然存在) 。在複雜的設計中,這種設置和拆卸可以解釋大部分測試代碼。另外,如果需要創建1000個員工「對象」,我可以重複使用我的代碼而不必查詢我的數據源(文件,數據庫,卡片索引等)1000次(換句話說,它整齊地解決着名的ORM N + 1查詢問題)。因此,總而言之,從業務邏輯中分離數據完全作爲你描述的隱藏依賴關係存在一些非常令人討厭的陷阱。恕我直言,這是一個反模式,封裝特定數據的檢索內的「對象」的建設或功能內從一些存儲的數據檢索屬性。

0

你可能想在這裏使用一個三層的方法,你的第一層是你的客戶,在一個消費getEmployeePhoneNbr(僱員)...... 第二層爲數據訪問層,並第三層將是數據實現層,數據訪問層將使用它來訪問具體的信息源。

數據實現層。

此層包含:

  1. 一種數據結構,表示可以由數據層訪問的資源的位置。
  2. 一個API來創建一個新的結構,它是相應的函數來配置它。

數據訪問層

包含:

  1. 的指針的數據結構將被用作數據源。
  2. 一個公共的簡單API,包含您必須訪問數據的所有調用,例如getEmployeePhoneNbr(employeeId),getEmployeeName(employeeId)....所有這些調用將在內部使用指向數據結構的指針來訪問特定數據

使用這種方法,您只需要爲數據訪問層提供正確的數據實現結構,因此如果它發生變化,您只需要在一個地方進行更改。

3

通常使用依賴倒置原理(又名DIP)解決您遇到的問題。原文可以在here找到。

這篇文章主要是面向對象的,但你也可以使用命令式語言(你可以使用命令式語言來做OO,它只是更難)。

原則是最好給客戶端對象引用一個執行某些所需處理(例如數據庫訪問)的對象,而不是將該對象編碼或聚合到客戶端對象中。

在功能級別上,您可以將其翻譯爲高級功能低級別數據/功能。

非OO語言中最好的方法是傳遞一個結構或函數指針,用於定義更高級別函數使用的數據/函數。

0

將資源依賴關係置於查找函數中。如果一些資源相關,我會創建一個具有簡單功能的模塊來檢索它們。我可以避免時親自避免提及這些參考。途中的代碼沒有商業知識或使用它們。

相反的:

getEmployeePhoneNbr(employeeId) 
    dbName = "employeedb" 
    ... SQL, logic, etc. 

或者:

getEmployeePhoneNbr(employeeId, dbName) 
    ... SQL, logic, etc. 

我會做到以下幾點:

getEmployeePhoneNbr(employeeId) 
    dbName = getEmployeeDbName() 
    ... SQL, logic, etc. 

這樣你就可以改變getEmployeeDbName()和每一個相關的功能和模塊將受益。