原文bloc-architecture
Bloc 架構(gòu)
使用Bloc允許我們將應(yīng)用分為三層:
- Data 數(shù)據(jù)層
- Data Provider 數(shù)據(jù)提供者
- Repository 數(shù)據(jù)倉庫
- Business Logic 業(yè)務(wù)邏輯層
- Presentation 表現(xiàn)層
感覺類似于
MVC
毒返?
Data 對應(yīng) Model;
Business Logic 對應(yīng) Controller;
Presentation 對應(yīng) View;
我們將從最底層(距離用戶界面最遠)開始饿幅,向上講解它們的工作方式微猖。
數(shù)據(jù)層 (Data Layer)
數(shù)據(jù)層負責(zé)從一個或者多個數(shù)據(jù)源中檢索/操作數(shù)據(jù)
數(shù)據(jù)層可以分為兩部分:
- 倉庫 (Repository)
- 數(shù)據(jù)提供者 (Data Provider)
數(shù)據(jù)層是應(yīng)用程序的最底層眉尸,并與數(shù)據(jù)庫域蜗,網(wǎng)絡(luò)請求和其他異步數(shù)據(jù)源進行交互巨双。
數(shù)據(jù)提供者 (Data Provider)
DataProvider 負責(zé)提供元數(shù)據(jù)。DataProvider應(yīng)該是萬能通用的霉祸。
DataProvider
通常會公開一些簡單的API來執(zhí)行CRUD操作筑累。例如會提供一些createData
,readData
丝蹭,updateData
慢宗,和deleteData
方法來作為數(shù)據(jù)層的一部分。
class DataProvider {
Future<RawData> readData() async {
// 從數(shù)據(jù)庫或者網(wǎng)絡(luò)獲取數(shù)據(jù)等...
}
}
倉庫 (Repository)
repository
負責(zé)將一個或者多個data provider
包裝在一起奔穿,與Bloc 層
(業(yè)務(wù)邏輯層)通信镜沽。
class Repository {
final DataProviderA dataProviderA;
final DataProviderB dataProviderB;
Future<Data> getAllDataThatMeetsRequirements() async {
final RawDataA dataSetA = await dataProviderA.readData();
final RawDataB dataSetB = await dataProviderB.readData();
final Data filteredData = _filterData(dataSetA, dataSetB);
return filteredData;
}
}
如你所見,repository層可以與多個data provider
交互贱田,并在將結(jié)果交給業(yè)務(wù)邏輯層之前對數(shù)據(jù)執(zhí)行轉(zhuǎn)換缅茉。
業(yè)務(wù)邏輯層 Bloc (Business Logic) Layer
bloc
層 負責(zé)使用新狀態(tài)(state
)響應(yīng)表現(xiàn)層中的事件(event)。bloc層可以依賴于一個或多個repositiry
來檢索構(gòu)建應(yīng)用程序狀態(tài)所需的數(shù)據(jù)男摧。
將bloc
層視為連接用戶界面(prsentation layer
)和 數(shù)據(jù)層 (data layer
) 的橋梁蔬墩。bloc
層接受用戶輸入生成的事件(event
),然后與repository
通信耗拓,以便為表示層構(gòu)建新的狀態(tài)(state
)使用拇颅。
個人認為就是: 輸入
event
==> 輸出state
class BusinessLogicComponent extends Bloc {
final Repository repository;
Stream mapEventToState(event) async * {
if(event is AppStarted) {
yield await repository.getAllDataThatMeetsRequirements();
}
}
}
Bloc 與 Bloc 通信 (Bloc-to-Bloc Communication)
每個bloc都有一個狀態(tài)流,其他bloc可以訂閱這個狀態(tài)流乔询,以便對bloc內(nèi)的變化作出反應(yīng)樟插。
Bloc 可以依賴于其他 Bloc 來對其狀態(tài)變化做出反應(yīng)。在下面的示例中哥谷, MyBloc
依賴于 OtherBloc
岸夯,并對OtherBloc
中響應(yīng)狀態(tài)改變的事件進行dispatch
。為了避免內(nèi)存泄漏们妥,需要在MyBloc
中重寫dispose
方法并關(guān)閉StreamSubscription
class MyBloc extends Bloc {
final OtherBloc otherBloc;
StreamSubscription otherBlocSubscription;
MyBloc(this.otherBloc) {
otherBlocSubscription = otherBloc.state.listen((state) {
// React to state changes here. 在這里響應(yīng)狀態(tài)的變化
// Dispatch events here to trigger changes in MyBloc. 在這里分發(fā)事件來觸發(fā)MyBloc改變
});
}
@override
void dispose() {
otherBlocSubscription.cancel();
super.dispose();
}
}
表現(xiàn)層 Presentation Layer
presentation 職責(zé)是弄清如何基于一個或多個bloc狀態(tài)來渲染自己猜扮。此外,它還應(yīng)該處理用戶輸入和應(yīng)用程序生命周期事件监婶。
大多數(shù)應(yīng)用程序流將從一個AppStart
事件開始旅赢,該事件觸發(fā)應(yīng)用程序獲取一些數(shù)據(jù)以呈現(xiàn)給用戶。
在這種情況下惑惶,presentation層將分發(fā)一個AppStart
事件煮盼。
此外,presentation層必須根據(jù)bloc層的狀態(tài)確定在屏幕上顯示的內(nèi)容带污。
class PresentationComponent {
final Bloc bloc;
PresentationComponent() {
bloc.dispatch(AppStarted());
}
build() {
// render UI based on bloc state
}
}