1. 程式人生 > >iOS VIPER架構實踐(三):面向介面的路由設計

iOS VIPER架構實踐(三):面向介面的路由設計

路由是實現模組間解耦的一個有效工具。如果要進行元件化開發,路由是必不可少的一部分。目前iOS上絕大部分的路由工具都是基於URL匹配的,優缺點都很明顯。這篇文章裡將會給出一個更加原生和安全的設計,這個設計的特點是:

  • 路由時用protocol尋找模組
  • 可以對模組進行固定的依賴注入和執行時依賴注入
  • 支援不同模組間進行介面適配和轉發,因此無需和某個固定的protocol關聯
  • 充分解耦的同時,增加型別安全
  • 支援移除已執行的路由
  • 封裝UIKit介面跳轉方法,可以一鍵跳轉和移除
  • 支援storyboard,支援其他任意模組
  • 可以檢測介面跳轉時的大部分錯誤

如果你想要一個能夠充分解耦、型別安全、有依賴注入功能的路由器,那這個就是目前所能找到的最佳方案。

這個路由工具是為了實踐VIPER模式而設計的,目的是為VIPER提供依賴注入功能,不過它也可以用於MVC、MVP、MVVM,沒有任何限制。

Router的作用

首先,我們需要梳理清楚,為什麼我們需要Router,Router能帶來什麼好處,解決什麼問題?我們需要一個什麼樣的Router?

路由缺失時的情況

沒有路由時,介面跳轉的程式碼就很容易產生模組間耦合。

iOS中執行介面跳轉時,用的是UIViewController上提供的跳轉方法:

[sourceViewController.navigationController pushViewController:destinationViewController animated:YES]
;
[sourceViewController presentViewController:destinationViewController animated:YES completion:nil];

如果是直接匯入destinationViewController的標頭檔案進行引用,就會導致和destinationViewController模組產生耦合。類似的,一個模組引用另一個模組時也會產生這樣的耦合。因此我們需要一個方式來獲取destinationViewController,但又不能對其產生直接引用。

這時候就需要路由提供的"尋找模組"的功能。以某種動態的方式獲取目的模組。

那麼路由是怎麼解決模組耦合的呢?在上一篇VIPER講解裡,路由有這幾個主要職責:

  • 尋找指定模組,執行具體的路由操作
  • 宣告模組的依賴
  • 宣告模組的對外介面
  • 對模組內各部分進行依賴注入

通過這幾個功能,就能實現模組間的完全解耦。

尋找模組

路由最重要的功能就是給出一種尋找某個指定模組的方案。這個方案是鬆耦合的,獲取到的模組在另一端可以隨時被另一個相同功能的模組替換,從而實現兩個模組之間的解耦。

尋找模組的實現方式其實只有有限的幾種:

  • 用一個字串identifier來標識某個對應的介面(URL Router、UIStoryboardSegue)
  • 利用Objective-C的runtime特性,直接呼叫目的模組的方法(CTMediator)
  • 用一個protocol來和某個介面進行匹配(蘑菇街的第二種路由和阿里的BeeHive),這樣就可以更安全的對目的模組進行傳參

這幾種方案的優劣將在之後逐一細說。

宣告依賴和介面

一個模組A有時候需要使用其他模組的功能,例如最通用的log功能,不同的app有不同的log模組,如果模組A對通用性要求很高,log方法就不能在模組A裡寫死,而是應該通過外部呼叫。這時這個模組A就依賴於一個log模組了。App在使用模組A的時候,需要知道它的依賴,從而在使用模組A之前,對其注入依賴。

當通過cocoapods這樣的包管理工具來配置不同模組間的依賴時,一般模組之間是強耦合的,模組是一一對應的,當需要替換一個模組時會很麻煩,容易牽一髮而動全身。如果是一個單一功能模組,的確需要依賴其他特定的各種庫時,那這樣做沒有問題。但是如果是一個業務模組中引用了另一個業務模組,就應該儘量避免互相耦合。因為不同的業務模組一般是由不同的人負責,應該避免出現一個業務模組的簡單修改(例如調整了方法或者屬性的名字)導致引用了它的業務模組也必須修改的情況。

這時候,業務模組就需要在程式碼裡宣告自己需要依賴的模組,讓app在使用時提供這些模組,從而充分解耦。

示例程式碼:

@protocol ZIKLoginServiceInput <NSObject>
- (void)loginWithAccount:(NSString *)account
                password:(NSString *)password
                 success:(void(^_Nullable)(void))successHandler
                   error:(void(^_Nullable)(void))errorHandler;
@end
@interface ZIKNoteListViewController ()
//筆記介面需要登入後才能檢視,因此在標頭檔案中宣告,讓外部在使用的時候設定此屬性
@property (nonatomic, strong) id<ZIKLoginServiceInput> loginService;
@end

這個宣告依賴的工作其實是模組的Builder的職責。一個介面模組大部分情況下都不止有一個UIViewController,也有其他一些Manager或者Service,而這些角色都是有各自的依賴的,都統一由模組的Builder宣告,再在Builder內部設定依賴。不過在上一篇文章的VIPER講解裡,我們把Builder的職責也放到了Router裡,讓每個模組單獨提供一個自己的Router。因此在這裡,Router是一個離散的設計,而不是一個單例Router掌管所有的路由。這樣的好處就是每個模組可以充分定製和控制自己的路由過程。

可以宣告依賴,也就可以同時宣告模組的對外介面。這兩者很相似,所以不再重複說明。

Builder和依賴注入

執行路由的同時用Builder進行模組構建,構建的時候就對模組內各個角色進行依賴注入。當你呼叫某個模組的時候,需要的不是某個簡單的具體類,而是一個構建完畢的模組中的某個具體類。在使用這個模組前,模組需要做一些初始化的操作,比如VIPER裡設定各個角色之間的依賴關係,就是一個初始化操作。因此使用路由去獲取某個模組中的類,必定需要通過模組的Builder進行。很多路由工具都缺失了這部分功能。

你可以把依賴注入簡單地看成對目的模組傳參。在進行介面跳轉和使用某個模組時,經常需要設定目的模組的一些引數,例如設定delegate回撥。這時候就必須呼叫一些目的模組的方法,或者傳遞一些物件。由於每個模組需要的引數都不一樣,目前大部分Router都是使用字典包裹引數進行傳遞。但其實還有更好、更安全的方案,下面將會進行詳解。

你也可以把Router、Builder和Dependency Injector分開,不過如果Router是一個離散型的設計,那麼都交給各自的Router去做也很合理,同時能夠減少程式碼量,也能夠提供細粒度的AOP。

現有的Router

梳理完了路由的職責,現在來比較一下現有的各種Router方案。關於各個方案的具體實現細節我就不再展開看,可以參考這篇詳解的文章:iOS 元件化 —— 路由設計思路分析

URL Router

目前絕大多數的Router都是用一串URL來表示需要開啟的某個介面,程式碼上看來大概是這樣:

//註冊某個URL,和路由處理進行匹配儲存
[URLRouter registerURL:@"settings" handler:^(NSDictionary *userInfo) {
 UIViewController *sourceViewController = userInfo[@"sourceViewController"];
 //獲取其他引數
 id param = userInfo[@"param"];
 //獲取需要的介面
 UIViewController *settingViewController = [[SettingViewController alloc] init];
 [sourceViewController.navigationController pushViewController: settingViewController animated:YES];
}];
//呼叫路由
[URLRouter openURL:@"myapp://noteList/settings?debug=true" userInfo:params completion:^(NSDictionary *info) {

}];

傳遞一串URL就能開啟noteList介面的settings介面,用字典包裹需要傳遞的引數,有時候還會把UIKit的push、present等方法進行簡單封裝,提供給呼叫者。

這種方式的優點和缺點都很突出。

優點

極高的動態性

這是動態性最高的方案,甚至可以在執行時隨時修改路由規則,指向不同的介面。也可以很輕鬆地支援多級頁面的跳轉。

如果你的app是電商類app,需要經常做活動,app內的跳轉規則經常變動,那麼就很適合使用URL的方案。

統一多端路由規則

URL的方案是最容易跨平臺實現的,iOS、Andorid、web、PC都按照URL來進行路由時,也就可以統一管理多端的路由規則,降低多端各自維護和修改的成本,讓不懂技術的運營人員也可以簡單快速地修改路由。

和上一條一樣,這也是一個和業務強相關的優點。如果你有統一多端的業務需求,使用URL也很合適。

適配URL scheme

iOS中的URL scheme可以跨程序通訊,從app外開啟app內的某個指定頁面。當app內的頁面都能使用URL開啟時,也就直接相容了URL scheme,無需再做額外的工作。

缺點

不適合通用模組

URL Router的設計只適合UI模組,不適合其他功能性模組的元件。功能性模組的呼叫並不需要如此強的動態特性,除非是有模組熱更新的需求,否則一個模組的呼叫在一個版本里應該總是穩定不變的,即便要進行模組間解耦,也不應該用這種方式。

安全性差

字串匹配的方式無法進行編譯時檢查,當頁面配置出錯時,只能在執行時才能發現。如果某個開發人員不小心在字串里加了一個空格,編譯時也無法發現。你可以用巨集定義來減少這種出錯的機率。

維護困難

沒有高效地宣告介面的方式,只能從文件裡查詢,編寫時必須仔細對照字串及其引數型別。

傳參通過字典來進行,引數型別無法保證,而且也無法準確地知道所呼叫的介面需要哪些引數。當目的模組進行了介面升級,修改了引數型別和數量,那所有用到的地方都要一一修改,並且沒有編譯器的幫助,你無法知道是否遺漏了某些地方。這將會給維護和重構帶來極大的成本。

針對這個問題,蘑菇街的選擇是用另一個Router,用protocol來獲取目的模組,再進行呼叫,增加安全性。

Protocol Router

這個方案也很容易理解。把之前的字串匹配改成了protocol匹配,就能獲取到一個實現了某個protocol的物件。

開源方案裡只看到了BeeHive實現了這樣的方式:

id<ZIKLoginServiceInput> loginService = [[BeeHive shareInstance] createService:@protocol(ZIKLoginServiceInput)];

優點

安全性好,維護簡單

再對這個物件呼叫protocol中的方法,就十分安全了。在重構和修改時,有了編譯器的型別檢查,效率更高。

適用於所有模組

Protocol更加符合OC和Swift原生的設計思想,任何模組都可以使用,而不侷限於UI模組。

優雅地宣告依賴

模組A需要用到登入模組,但是它要怎麼才能宣告這種依賴關係呢?如果使用Protocol Router,那就只需要在標頭檔案裡定義一個屬性:

@property (nonatomic, string) id<ZIKLoginServiceInput> *loginService;

如果這個依賴是必需依賴,而不是一個可選依賴,那就新增到初始化引數裡:

@interface ModuleA ()
- (instancetype)initWithLoginService:(id<ZIKLoginServiceInput>)loginService;
@end

問題是,如果這樣的依賴很多,那麼初始化方法就會變得很長。因此更好的做法是由Builder進行固定的依賴注入,再提供給外部。目前BeeHive並沒有提供依賴注入的功能。

缺點

動態性有限

你可以維護一份protocol和模組的對照表,使用動態的protocol來嘗試動態地更改路由規則,也可以在Protocol Router之上封裝一層URL Router專門用於動態性的需求。

需要額外適配URL Scheme

使用了Protocol Router就需要再額外處理URL Scheme了。不過這樣也是正常的,解析URL Scheme本來就應該放到另一個單獨的模組裡。

Protocol是否會導致耦合?

很多談到這種方案的文章都會指出,和URL Router相比,Protocol Router會導致呼叫者引用目的模組的protocol,因此會產生"耦合"。我認為這是對"解耦"的錯誤理解。

要想避免耦合,首先要弄清楚,我們需要什麼程度的解耦。我的定義是:模組A呼叫了模組B,模組B的介面或者實現在做出簡單的修改時,或者模組B被替換為相同功能的模組C時,模組A不需要進行任何修改。這時候就可以認為模組A和模組B是解耦的。

業務設計的互相關聯

有些時候,表達出兩個模組之間的關聯是有意義的。

當一個介面A需要展示一個登入介面時,它可能需要向登入介面傳遞一個"提示語"引數,用於在登入介面顯示一串提示。這時候,介面A在呼叫登入介面時,是要求登入介面能夠顯示這個自定義提示語的,在業務設計中就存在兩個模組間的強關聯性。這時候,URL Router和Protocol Router沒有任何區別,包括下面將要提到的Target-Action路由方式,都存在耦合,但是Protocol Router通過簡單地改善,是可以把這部分耦合去除的。

URL Router:

[URLRouter openURL:@"login" userInfo:@{@"message":@"請登入檢視筆記詳情"}];

Protocol Router:

@protocol LoginViewInput <NSObject>
@property (nonatomic, copy) NSString *message;
@end

//獲取登入介面進行設定
UIViewController<LoginViewInput> *loginViewController = [ProtocolRouter destinationForProtocol:@protocol(LoginViewInput)];
loginViewController.message = @"請登入檢視筆記詳情";

由於字典傳參的原因,URL Router只不過是把這種介面上的關聯隱藏到了字典key裡,它在引數字典裡使用@"message"時,就是在隱式地使用LoginViewInput的介面。

這種業務設計上導致的模組之間互相關聯是不可避免的,也是不需要去隱藏的。隱藏了反而會引來麻煩。如果登入介面的屬性名字變了,從NSString *message改成了NSString *notifyString,那麼URL Router在register的時候也必須修改傳參時的程式碼。如果register是由登入介面自己執行和處理的,而不是由App Context來處理的,那麼此時引數key是固定為@"notifyString"的,那就會要求所有呼叫者的傳參key也修改為notifyString,這種修改如果缺少編譯器的幫助會很危險,目前是用巨集來減少這種修改導致的工作量。而Protocol Router在修改時就能充分利用編譯器進行檢查,能夠保證100%安全。

因此,URL Router並不能做到解耦,只是隱藏了介面關聯而已。一旦遇到了需要修改或者重構的情況,麻煩就出現了,在替換巨集的時候,你還必須仔細檢查有沒有哪裡有直接使用字串的key。只是簡單地修改名字還是可控的,如果是需要增加引數呢?這時候就根本無法檢查哪裡遺漏了引數傳遞了。這就是字典傳參的壞處。

關於這部分的討論,也可以參考Peak大佬的文章:iOS元件化方案

Protocol Router在這種情況下也需要作出修改,但是它能幫助你安全高效地進行重構。而且只要稍加改進,也可以完全無需修改。解決方法就是把Protocol分離為Required InterfaceProvided Interface

Required Interface 和 Provided Interface

模組的介面其實是有Required InterfaceProvided Interface的區別的。Required Interface就是呼叫者需要用到的介面,Provided Interface就是實際的被呼叫者提供的介面。

在UML的元件圖中,就很明確地表現出了這兩者的概念。下圖中的半圓就是Required Interface,框外的圓圈就是Provided Interface

元件圖

那麼如何實施Required InterfaceProvided Interface?上一篇文章裡已經討論過,應該由App Context在一個adapter裡進行介面適配,從而使得呼叫者可以繼續在內部使用Required Interface,adapter負責把Required Interface和修改後的Provided Interface進行適配。

示例程式碼:

@protocol ModuleARequiredLoginViewInput <NSObject>
@property (nonatomic, copy) NSString *message;
@end

//Module A中的呼叫程式碼
UIViewController<ModuleARequiredLoginViewInput> *loginViewController = [ZIKViewRouterToView(LoginViewInput) makeDestination];
loginViewController.message = @"請登入檢視筆記詳情";
//Login Module Provided Interface
@protocol ProvidedLoginViewInput <NSObject>
@property (nonatomic, copy) NSString *notifyString;
@end
//App Context 中的 Adapter,用Objective-C的category或者Swift的extension進行介面適配
@interface LoginViewController (ModuleAAdapte) <ModuleARequiredLoginViewInput>
@property (nonatomic, copy) NSString *message;
@end
@implementation LoginViewController (ModuleAAdapte)
- (void)setMessage:(NSString *)message {
 self.notifyString = message;
}
- (NSString *)message {
 return self.notifyString;
}
@end

用category、extension、NSProxy等技術相容新舊介面,工作全部由模組的使用和裝配者App Context完成。如果LoginViewController已經有了自己的message屬性,這時候就說明新的登入模組是不可相容的,必須有某一方做出修改。當然,介面適配能做的事情是有限的,例如一個介面從同步變成了非同步,那麼這時候兩個模組也是不能相容的。

因此,如果模組需要進行解耦,那麼它的介面在設計的時候就應該十分仔細,儘量不要在引數中引入太多其他的模組依賴。

只有存在Required InterfaceProvided Interface概念的設計,才能做到徹底的解耦。目前的路由方案都缺失了這一部分。

Target-Action

CTMediator的方案,把對模組的呼叫封裝到Target-Action中,利用了Objective-C的runtime特性,省略了Target-Action的註冊和繫結工作,直接通過CTMediator中介者呼叫目的模組的方法。

@implementation CTMediator (CTMediatorModuleAActions)
- (UIViewController *)CTMediator_viewControllerForDetail
{
    UIViewController *viewController = [self performTarget:kCTMediatorTargetA
                                                    action:kCTMediatorActionNativFetchDetailViewController
                                                    params:@{@"key":@"value"}
                                         shouldCacheTarget:NO
                                        ];
    if ([viewController isKindOfClass:[UIViewController class]]) {
        // view controller 交付出去之後,可以由外界選擇是push還是present
        return viewController;
    } else {
        // 這裡處理異常場景,具體如何處理取決於產品
        return [[UIViewController alloc] init];
    }
}
@end

-performTarget:action:params:shouldCacheTarget:方法通過NSClassFromString,獲取目的模組提供的Target類,再呼叫Target提供的Action,實現了方法呼叫:

@implementation CTMediator
- (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget
{

    NSString *targetClassString = [NSString stringWithFormat:@"Target_%@", targetName];
    NSString *actionString = [NSString stringWithFormat:@"Action_%@:", actionName];
    Class targetClass;

    NSObject *target = 
            
           

相關推薦

iOS VIPER架構實踐()面向介面路由設計

路由是實現模組間解耦的一個有效工具。如果要進行元件化開發,路由是必不可少的一部分。目前iOS上絕大部分的路由工具都是基於URL匹配的,優缺點都很明顯。這篇文章裡將會給出一個更加原生和安全的設計,這個設計的特點是: 路由時用protocol尋找模組可以對模

中小型研發團隊架構實踐要點--轉

架構圖 tput 驗證 簡單 put 目的 flux 商務 限流 來自微信公眾號聊聊架構 作者|張輝清 編輯|雨多田光 如果你正好處在中小型研發團隊…… 中小型研發團隊很多,而社區在中小型研發團隊架構實踐方面的探討卻很少。中

LAMP原理架構解析()LAMP編譯安裝

mariadbCentos7.3編譯安裝LAMP目錄:編譯環境LAMP編譯安裝一.環境準備 征信數據庫數據事件不一致導致數據(RAC集群)混亂,PLSQL查詢時間和數據庫時間不一致,嚴重影響業務本文出自 “每天進步一點點,自律” 博客,請務必保留此出處http://wbxue.blog.51cto.

C語言面向物件程式設計面向介面程式設計(4)

 Java 中有 interface 關鍵字,C++ 中有抽象類或純虛類可以與 interface 比擬,C 語言中也可以實現類似的特性。     在面試 Java 程式設計師時我經常問的一個問題是:介面和抽象類有什麼區別。  &n

架構系列使用Keepalived+Nginx+tomcat實現叢集部署

在前面的一篇文章《架構系列二:使用Nginx+tomcat實現叢集部署》,介紹了通過Nginx配置Tomct叢集,當其中一個Tomcat服務停止後,Nginx可自動識別並選擇另一個伺服器響應使用者請求,達到了Tomcat叢集的效果,那如果Nginx伺服器停掉後,

實踐使用谷歌物體檢測API訓練自己的資料集

一.環境安裝: ubuntu 1:TensorFlow環境二選一: 親測用使用公開資料CPU需要在i5下跑一晚上,GPU只要30分鐘,建議安裝TensorFlow 1.00 pip install tensorflow # For CPU pip in

C#進階系列——MEF實現設計上的“鬆耦合”(終結篇面向介面程式設計)

序:忙碌多事的八月帶著些許的倦意早已步入尾聲,金秋九月承載著抗戰勝利70週年的喜慶撲面而來。沒來得及任何準備,似乎也不需要任何準備,因為生活不需要太多將來時。每天忙著上班、加班、白加班,忘了去憤,忘了去算計所謂的價值。天津爆炸事故時刻警示著我們生命的無常,逝者安息,活著的人生活還得繼續,珍惜生命,遠離傷害。武

iOS音訊學習筆記音訊會話管理

​      使用Audio Session API ,可以指定App需要的音訊行為,比如,當播放音訊時,使得其他應用App靜音或者混和在一起,也可以指定當App的音訊被中斷(例如被電話)時的行為,還可以讓App響應使用者的行為,比如插入或拔出耳機,或者響應那些使用聲音硬體的事件,比如Clock、日曆鬧鐘或者

C語言面向物件程式設計(四)面向介面程式設計

    Java 中有 interface 關鍵字,C++ 中有抽象類或純虛類可以與 interface 比擬,C 語言中也可以實現類似的特性。     在面試 Java 程式設計師時我經常問的一個問題是:介面和抽象類有什麼區別。     很多程式設計書籍也經常說要面向介面

Java設計第一原則面向介面程式設計

在專案中的意義:     在傳統的專案開發過程中,由於客戶的需求經常變化,如果不採用面向介面程式設計,那麼我們必須不停改寫現有的業務程式碼。改寫程式碼可能產生新的BUG,而且改寫程式碼還會影響到呼叫該業務的類,可能全都需要修改,影響系統本身的穩定性。而且為了將改寫程式碼帶來的影響最小,我們不得不屈服當前的系

記一次介面效能優化實踐總結優化介面效能的八個建議

### 前言 最近對外介面偶現504超時問題,原因是程式碼執行時間過長,超過nginx配置的15秒,然後真槍實彈搞了一次介面效能優化。在這裡結合優化過程,總結了介面優化的八個要點,希望對大家有幫助呀~ - 資料量比較大,批量操作資料入庫 - 耗時操作考慮非同步處理 - 恰當使用快取 - 優化程式邏輯、程式碼

32-高級路由BGP匯總實驗 null 0路由匯總

cto 9.1 via null ffffff router color fig ip route 一、實驗拓撲:二、實驗要求:1、R1部署192.168.8.1~192.168.11.1環回口;2、R1上部署192.168.8.0/22 null 0,並將此路由在BGP進

機器學習實踐心得數據平臺設計與搭建US幸運飛艇平臺出租

git 要花 規範 支持 避免 取數據 用戶 硬件 app 機器學習作為近幾年的一項熱門技術US幸運飛艇平臺出租QQ2952777280【話仙源碼論壇】hxforum.com【木瓜源碼論壇】papayabbs.com,不僅憑借眾多“人工智能”產品而為人所熟知,更是從根本上增

Android實時監控專案第二篇登陸介面設計

在開始核心功能的實現之前,我們先從最簡單的部分開始,設計第一個Activity介面,它主要用來獲取使用者輸入的需要連線的PC端的IP地址。       該Activity對應的XML佈局檔案很簡單,就不詳細解釋了,如下: <TableLayoutxml

MVC系列——MVC原始碼學習打造自己的MVC框架(自定義路由規則)

前言:上篇介紹了下自己的MVC框架前兩個版本,經過兩天的整理,版本三基本已經完成,今天還是發出來供大家參考和學習。雖然微軟的Routing功能已經非常強大,完全沒有必要再“重複造輪子”了,但博主還是覺得自己動手寫一遍印象要深刻許多,希望想深入學習MVC的童鞋自己動手寫寫。好了,廢話就此打住。 MVC原始

【遠端呼叫框架】如何實現一個簡單的RPC框架(五)優化軟負載中心設計與實現

【如何實現一個簡單的RPC框架】系列文章: 1.前言 在部落格【遠端呼叫框架】如何實現一個簡單的RPC框架(一)想法與設計中我們介紹了“服務註冊查詢中心”,負責服務資訊的管理即服務的註冊以及查詢,在目前為止的實現中,我們採用web應用的方式,以

java設計模式面向對象設計的7個原則

ron 依賴倒置原則 步驟 計算機 適應性 抽象類 oops 關閉 sla 在軟件開發中,為了提高軟件系統的可維護性和可復用性,增加軟件的可擴展性和靈活性,程序員要盡量根據7條原則來開發程序,從而提高軟件開發效率,節約軟件開發成本和維護成本。 這7條原則分別是:開閉原則、

基於大中臺架構的電商業務中臺最佳實踐交易中臺技術要點設計之高效能

接著上篇繼續講,接下來主要介紹交易總體設計的技術要點設計,對於電商中臺來說,交易系統是核心中的核心,一開始就需要圍繞高效能,高可用,和高擴充套件三個方面來重點設計。本篇主要介紹高效能設計。 對於高效能的定義,通常可以理解為系統/服務介面響應時間低(rt)且併發量(qps,tps)高. 提

SoC嵌入式軟件架構設計代碼分塊(Bank)設計原則

post 介紹 讀寫 cor 層次 clas rom bank 分配 上一節講述了在沒有MMU的CPU(如80251、MIPS M控制器系列、ARM cortex m系列)上實現虛擬內存管理的集成硬件設計方法。新設計的內存管理管理單元要實現虛擬內存管理還須要

微服務理論與實踐()-微服務架構的基本能力和優缺點

控制臺 並且 提高 str love 速度 ont 寫入 框架 1.微服務架構模式方案 微服務架構采用Scale Cube方法設計應用架構,將應用服務按功能拆分成一組相互協作的服務。每個服務負責一組特定、相關的功能。每個服務可以有自己獨立的數據庫,從而保證與其他服務解耦。