4 回答

TA貢獻1824條經驗 獲得超8個贊
在提供程序的編程中很常見- 即。對其消費者myFunctionA()一無所知。處理這個問題的唯一正確方法是預先定義一個 API 并且永遠不要更改它;) myFunctionB()
我看不到對消費者進行版本控制的目的——這個原因必須是“下游” myFunctionB()——即。的消費者,myFunctionB()作者myFunctionB()無法控制...在這種情況下,它myFunctionB()本身成為提供者,作者將不得不處理這個問題(可能使用與您相同的模式)...但這不是您的要處理的問題。
至于您的提供者 myFunctionA():如果您不能預先為數據本身定義接口/API - 即。你知道數據的結構必須改變(以非向后兼容的方式),但你不知道如何......那么你將需要以一種或另一種方式對某些東西 進行版本控制。
你比大多數人領先幾英里,因為你看到了這一點,并從一開始就計劃好了。
避免myFunctionB()在某些時候對消費者進行更改的唯一方法是以向后兼容的方式對提供者進行所有更改。myFunctionA()您描述的更改不是向后兼容的,因為myFunctionB()在不修改的情況下不可能知道如何處理新輸出myFunctionA()。
您提出的解決方案聽起來應該可行。但是,至少有兩個缺點:
它要求您保留不斷增長的遺留功能列表,以防有任何消費者請求他們的數據。這將變得非常難以維護,并且從長遠來看可能是不可能的。
根據將來需要進行哪些更改,可能根本無法再生成輸出myFunctionA__v1_0()- 在您的示例中page_types,您在系統(tǒng)中添加了幾個的可能性 - 在這種情況下,您可能只需重寫v1_0以使用第一個和傳統(tǒng)的消費者會很高興。但是,如果您決定page_types從系統(tǒng)中完全放棄 的概念,則必須計劃完全刪除v1_0一種或另一種方式。因此,您需要建立一種與消費者溝通的方式。
處理這個問題的唯一正確方法仍然是預先定義一個 API 并且永遠不要更改它。
由于我們已經確定:
您將不得不進行向后不兼容的更改
您對消費者一無所知,并且您無權在需要時更改它們
我建議不要為您的數據定義一個不可變的 API,而是定義一個不可變的 API,允許您在消費者應該或必須升級時與他們進行通信。
這聽起來可能很復雜,但不一定是:
接受提供者中的版本參數:
這個想法是讓消費者明確告訴提供者返回哪個版本。
提供者可能如下所示:
function myFunctionA(string $version) {
$page_types = ['TypeA', 'TypeB'];
$page = new stdClass();
$page->title = 'Page title';
switch ($version) {
case '1.0':
$page->error = 'Version 1.0 no longer available. Please upgrade!';
break;
case '1.1':
$page->page_type = $page_types[0];
$page->warning = 'Deprecated version. Please upgrade!';
break;
case '2.0':
$page->page_types = $page_types;
break;
default:
$page->error = 'Unknown version: ' . $version;
break;
}
return $page;
}
因此,提供者接受一個參數,該參數將包含消費者可以理解的版本——通常是消費者上次更新時最新的版本。
提供者盡最大努力提供所請求的版本
如果不可能有一個“合同”來通知消費者($page->error將存在于返回值上)
如果可能,但有可用的較新版本,則另一個“合同”已到位以通知消費者這一點($page->warning將存在于返回值上)。
并在消費者中處理一些案例:
消費者需要發(fā)送它期望的版本作為參數。
function myFunctionB() {
//The consumer tells the provider which version it wants:
$page = myFunctionA('2.0');
if ($page->error) {
//Notify developers and throw an error
pseudo_notify_devs($page->error);
throw new Exception($page->error);
} else if ($page->warning) {
//Notify developers
pseudo_notify_devs($page->warning);
}
do_stuff_with($page);
}
舊版本的第二行myFunctionB()- 或完全不同的消費者myFunctionC()可能會要求舊版本:
$page = myFunctionA('1.1');
這使您可以隨時進行向后兼容的更改,而無需消費者做任何事情。您可以盡最大努力在可能的情況下仍然支持舊版本,從而在舊版使用者中提供“優(yōu)雅”的降級。
當您必須進行重大更改時,您可以繼續(xù)支持舊版本一段時間,然后最終將其完全刪除。
元信息
我不相信這會有用......但您可以使用過時的版本為消費者添加一些元信息:
function myFunctionA(string $version) {
# [...]
if ($page->error || $page->warning) {
$page->meta = [
'current_version' => '3.0',
'API_docs' => 'http://some-url.fake'
]
}
return $page;
}
然后可以在消費者中使用它:
pseudo_notify_devs(
$page->error .
' - Newest version: ' . $page->meta['current_version'] .
' - Docs: ' . $page->meta['API_docs']
);
......如果我是你,我會小心不要讓事情過于復雜......總是親吻

TA貢獻1815條經驗 獲得超10個贊
依賴注入在 OOP 上下文中更相關。但是,我在這里要做的主要事情是停止考慮返回可用的東西,并開始考慮這兩種方法如何協(xié)同工作以及它們的合同是什么。
弄清楚 myFunctionA() 的邏輯輸出是什么,將該合約編碼為一個對象,然后將您擁有的數據轉換為該格式。這樣,即使您在 myFunctionA() 中獲取數據的方式發(fā)生了變化,您也只需更新該一次轉換。
只要您遵守該合同(可以通過自定義對象表示)、myFunctionB() 和其他希望根據合同接收數據的方法,您就不必再更改這些方法。
所以我在這里的主要收獲是開始考慮你需要的數據,而不是在你收到它的結構中傳遞它,而是以它對你的應用程序最有意義的方式傳遞。

TA貢獻1772條經驗 獲得超6個贊
首先,這不是依賴注入。
你myFunctionA()可以被稱為生產者,因為它提供數據,它應該被證明是一個Data Structure. 您myFunctionB()可以被稱為消費者,因為它使用提供的數據myFunctionA。
因此,為了使您的生產者和消費者獨立工作,您需要在它們之間添加另一層,調用Converter. 該Converter層會將Data Structure提供的內容轉換為可以理解的Producer眾所周知的Data StructureConsumer
我真的建議您閱讀本書Clean Code第 6 章:對象和數據結構。這樣你就可以完全理解上面的概念,關于Data Structure
例子
假設我們有Data Structurecall Hand,有屬性右手和左手。
class Hand {
private $rightHand;
private $leftHand
// Add Constructor, getter and setter
}
myFunctionA()將提供對象Hand,Hand是一個Data Structure
function myFunctionA() {
$hand = Hand::createHand(); //Assume a function to create new Hand object
return $hand;
}
假設我們還有另一個Data Structurecall Leg, Leg 將能夠通過 myFunctionB() 消費;
class Leg {
private $rightLeg;
private $leftLeg
// Add Constructor, getter and setter
}
然后,我們需要有一個轉換器,在中間,從手轉換為腿,并使用myFunctionB()
class Converter {
public static function convertFromHandToLeg($hand) {
$leg = makeFromHand($hand); //Assume a method to convert from Hand to Leg
return $leg;
}
}
myFunctionB(Converter::convertFromHandToLeg($hand))
因此,每當您編輯 的響應時myFunctionA(),意味著您將要編輯Data Structure的Hand。您只需要編輯Converter以確保它繼續(xù)Hand正確轉換Leg。你不需要觸摸myFunctionB,反之亦然。
當您有另一個Producer會Hand像您在問題中提到的那樣提供時,這將非常有幫助myFunctionC(),myFunctionD()...而且您還有許多其他Consumer會Leg像myFunctionH()...一樣消耗的myFunctionG()東西
希望這有幫助
- 4 回答
- 0 關注
- 145 瀏覽
添加回答
舉報