
軟件架構是指,設計軟件的人為軟件賦予的形狀,這個形狀是指系統(tǒng)如何被劃分為組件(Components),各個組件如何排列(Arrangement),組件之間如何溝通(Communication)。
這是Uncle Bob 的 《Clean Architecture》中對架構的定義。
文中Demo
架構的意義:為了能夠將創(chuàng)建和維護軟件的成本最小化。
編寫代碼中至關重要的是,需要使每一部分容易被識別,賦有一個特定而明顯的目的,并與其他部分在邏輯關系中完美契合。這就是我們所說的軟件架構。好的架構不僅讓一個產品成功投入使用,還可以讓產品具有可維護性,并讓人不斷頭腦清醒的對它進行維護!而不能研發(fā)一時爽,維護火葬場!
架構設計模式簡介
目前主流的幾種架構模式:
- MVC
- MVP
- MVVM
- VIPER
MVP、MVVM、VIPER都是從MVC演變而來,都是為了解決開發(fā)過程中的實際問題而提出來的,各有優(yōu)缺點和適用的場景,本質目的都是不斷地從ViewController中把邏輯拆分出去。
從功能來區(qū)分的話,可以從三個層面劃分上面的4種架構設計模式:
-
Model層: 負責數(shù)據(jù)訪問,又可以分為以下兩類:
- 業(yè)務處理:日常開發(fā)中DAO、Service都可以算作是Model層衍生出來的業(yè)務請求模塊,負責用于處理用戶提交的請求。
- 數(shù)據(jù)承載:用于專門承載業(yè)務數(shù)據(jù)的實體類,比如開發(fā)中定義的Student、User等各種Entity.
- View層: 負責視圖的展示。
- Controller/Presenter/ViewModel:Model和View之間的中介,一般負責在用戶操作View時更新Model,以及當Model變化時更新View。
一個好的架構或者架構模式應該滿足以下三點:
- 清晰的職責劃分
- 可測試
- 易用性
MVVM

view都是以容器的方式封裝起來的,作為viewController的成員變量,view所產生的交互事件以代理的方式回調給viewController,viewController持有viewModel ,而viewModel以block或代理的方式把業(yè)務邏輯的處理結果交給viewController。
職責劃分:
- View:負責控件初始化,設置UI數(shù)據(jù)(不負責網(wǎng)絡數(shù)據(jù)和業(yè)務數(shù)據(jù)),交互事件代理給viewController。
- viewController:負責視圖創(chuàng)建、組合,協(xié)調邏輯,view事件的回調處理,數(shù)據(jù)綁定。
- viewModel:負責業(yè)務邏輯處理,其中又涉及到網(wǎng)絡數(shù)據(jù)或業(yè)務數(shù)據(jù)轉化成UI數(shù)據(jù)的預排版;負責數(shù)據(jù)增刪改查封裝者,注意這里只是封裝,具體的實現(xiàn)邏輯不在這一層。
MVVM的優(yōu)點:
- 開發(fā)人員可以專注于業(yè)務邏輯(viewModel)。
- 從而更容易針對viewModel單元測試。
- 可重用性。
MVVM缺點:
- 數(shù)據(jù)綁定不便調試bug
- 沒有統(tǒng)一的路由管理,界面間的鏈接邏輯分散在各控制器中,導致控制器間存在一定的耦合和依賴。
- 沒有統(tǒng)一的狀態(tài)管理
后兩點在MVC,MVP,VIPER中都存在。所以下文中重點探討以下兩點以及相應的實現(xiàn)方案:
- 路由管理
- 狀態(tài)管理
使用App Coordinator 統(tǒng)一管理應用路由
App Coordinator 是 Soroush Khanlou 在 2015 年的NSSpain演講上提出的一個模式,其本質上是 Martin Fowler 在《 Patterns of Enterprise Application Architecture 》中描述的 Application Controller 模式在 iOS 開發(fā)上的應用。其核心理念如下:
- 抽象出一個 Coordinator 對象概念
- 由該 Coordinator 對象負責 ViewController 的創(chuàng)建和viewModel等配置
- 由該 Coordinator 對象來管理所有的 ViewController 跳轉
- Coordinator 可以派生子 Coordinator 來管理不同的 Feature Flow。
傳統(tǒng)的開發(fā)模式下,頁面間的跳轉是通過 navigationController 的 push() 方法,這種方法固然便捷,但是實現(xiàn)跳轉存在頁面間耦合。Coordinator的誕生就是為了解決這一問題。
引入 Coordinator后跳轉邏輯對頁面不可見,由 Coordinator 管理,其提供了 navigationController 的接口并持有 Controller,跳轉邏輯隱藏在了 Coordinator 中。Coordinator 獨立與 MVVM 之外,是一個附加層??梢岳斫鉃?Coordinator 是每個組件對外暴露的接口,頁面間的交互,只能通過 Coordinator,它同樣依賴于 RxSwift。
經(jīng)過這層抽象之后,一個復雜 App 的路由對應關系就會如下:

從圖中可以看出,UI 和業(yè)務邏輯被拆分開,各自有了自己清晰的職責。ViewController 的初始化及配置,ViewController 之間的深層鏈接邏輯全部都轉移到 App Coordinator 的體系中去了,ViewController 則徹底變成了一個個獨立的個體,ViewController間沒了依賴關系,其只負責:
- 裝配子視圖
- 數(shù)據(jù)綁定
- 把界面上的 user action 轉換為業(yè)務上的 user intents(用戶意圖),然后轉入 App Coordinator 中進行業(yè)務處理。
還可以在 App Coordinator 的具體實現(xiàn)和 ViewController 之間抽象一層 Protocols,把 UI 和業(yè)務邏輯的實現(xiàn)徹底抽離開。經(jīng)過這層抽象之后,路由關系變化如下:

經(jīng)過 App Coordinator 統(tǒng)一處理路由之后,App 可以得到如下好處:
- ViewController 變得非常簡單,成為了一個概念清晰的,獨立的 UI 組件。這極大的增加了其可復用性。
- UI 和業(yè)務邏輯的抽離也增加了業(yè)務代碼的可復用性,在多屏時代,當你需要為當前應用增加一個 iPad 版本時,只需要重新做一套 iPad UI 對接到當前 iPhone 版的 App Coordinator 中就完成了。
具體實現(xiàn)可參看Demo。
基于狀態(tài)管理的單向數(shù)據(jù)流架構
為什么需要統(tǒng)一的狀態(tài)管理?
一個 iOS 應用本質上就是一個狀態(tài)機,從一個狀態(tài)的UI由 User Action 或者 API異步調用返回的 Data Action 觸發(fā)達到下一個狀態(tài)的 UI。為了準確的控制應用功能,開發(fā)者需要能夠清楚的知道:
應用的當前 UI 是由哪些狀態(tài)決定的?
User Action 會影響哪些應用狀態(tài)?如何影響的?
Data Action 會影響哪些應用狀態(tài)?如何影響的?
各應用的狀態(tài)通常分散在 Model中,甚至有些狀態(tài)直接保存在 View Controller 中,在跟蹤狀態(tài)時經(jīng)常需要跨越多個 Model,很難獲取到一個全貌的應用狀態(tài)。有時用戶操作產生的Action可能導致多個 Model的狀態(tài)發(fā)生改變,這時跟蹤狀態(tài)就變得比較困難。
通常我們需要在ViewModel中通過大量的變量來維護一個比較復雜的頁面在運行期間的各種狀態(tài)和邏輯,通過RxSwift將ViewModel中的狀態(tài)變量直接綁定給視圖就隱藏了通知視圖的過程,這樣我們只需要專注于數(shù)據(jù)本身,不用再去管UI層的邏輯,但是濫用這個特性也會帶來麻煩,大量的可觀察變量和綁定操作會讓邏輯變得含糊不清,修改一個變量的時候可能會導致一系列難以預料的連鎖反應,這樣代碼反而會變得更加難以維護,這時統(tǒng)一管理狀態(tài)顯得很有必要了。
那什么是單向數(shù)據(jù)流,多向數(shù)據(jù)流呢?
MVC,MVVM,MVP、VIPER這些架構設計方案,都是基于MVC演變而來,本質目的都是從ViewController中把邏輯拆分出去,對控制器進行瘦身和更細粒度的職責劃分.
他們都有各自的特點,但是都有同一個核心: 通過多向數(shù)據(jù)流將代碼按照單一職責原則來劃分代碼。在多向數(shù)據(jù)流中,數(shù)據(jù)在各個模塊中傳遞。
但多向數(shù)據(jù)流的代碼在閱讀和debug上都可能變成一場災難,一個改變可能會帶來一系列的連鎖反應,跟蹤狀態(tài)改變也比較困難。而單向數(shù)據(jù)流就能讓程序的運行更加具有可預測性,也能夠減少閱讀這些代碼的痛苦。
多向數(shù)據(jù)流并不一定是你想要的,相反單向數(shù)據(jù)流才是我們更喜歡的數(shù)據(jù)傳遞方式。
所謂的單向綁定和雙向綁定所描述的都是視圖(View)和數(shù)據(jù)(Model)之間的關系:
比方說有一個展示消息的頁面,首先需要從網(wǎng)絡加載最新的消息,在MVC中我們可以這樣寫:
class NormalMessageViewController: UIViewController {
var msgList: [MsgItem] = [] // 數(shù)據(jù)源
// 網(wǎng)絡請求
func request() {
// 1. 開始請求前播放loading動畫
self.startLoading()
MessageProvider.request(.news) { (result) in
switch result {
case .success(let response):
if let list = try? response.map([MsgItem].self) {
// 2. 請求結束后更新model
self.msgList = list
}
case .failure(_):
break
}
// 3. model更新后同步更新UI
self.stopLoading()
self.tableView.reloadData()
}
}
// ...
}
還可以將不需要的消息從列表中刪除:
extension NormalMessageViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// 1. 更新model
self.msgList.remove(at: indexPath.row)
// 2. 刷新UI
self.tableView.reloadData()
}
}
// ...
}
在request方法中我們通過網(wǎng)絡請求修改了數(shù)據(jù)msgList,一旦msgList發(fā)生改變必須刷新UI;在tableView上刪除消息時,視圖層直接對數(shù)據(jù)進行操作然后刷新UI。視圖層即會響應數(shù)據(jù)改變的事件,又會直接訪問和修改數(shù)據(jù),這就是一個雙向綁定的關系:

雖然在這個例子中看起來非常簡單,但是當頁面比較復雜的時候UI操作和數(shù)據(jù)操作混雜在一起會讓邏輯變得混亂??吹竭@里單向綁定的含義就很明顯了,它去掉了View -> Model的這一層關系,視圖層不能直接對數(shù)據(jù)進行修改,它只能通過某種機制向數(shù)據(jù)層傳遞事件,并在數(shù)據(jù)改變的時候刷新UI。
Redux是一種基于狀態(tài)管理的單向數(shù)據(jù)流架構。
為了構造單向數(shù)據(jù)流,Redux引入了一系列概念,這是Redux中所描述的數(shù)據(jù)流:

核心概念:
View
顧名思義,View就是視圖,用戶在視圖上的操作事件不會直接修改模型,而是會被映射成一個個Action。Action
Action表示一個對數(shù)據(jù)操作的請求,Action會被發(fā)送到Store中,這是對模型數(shù)據(jù)進行修改的唯一辦法。
Demo中用到ReSwift,它是一個輕量級的Redux框架。
在ReSwift中有一個名為Action的協(xié)議(僅作標記用的空協(xié)議),對于Model中數(shù)據(jù)的每個操作,比如說設置一個值,都需要有一個對應的Action:
/// 設置數(shù)據(jù)的Action
struct ActionSetMessage: Action {
var news: [MsgItem] = []
}
/// 移除某項數(shù)據(jù)的Action
struct ActionRemoveMessage: Action {
var index: Int
}
用struct類型來表示一個Action,Action所攜帶的數(shù)據(jù)保存在其成員變量中。
- Store和State
就像上面所提到的,State表示了應用中的Model數(shù)據(jù),而Store則是存放State的地方;在Redux中Store是一個全局的容器,所有組件的狀態(tài)都被保存在里面;Store接受一個Action,然后修改數(shù)據(jù)并通知視圖層更新UI。
定義 State 的方式可以從業(yè)務上建模,也可以根據(jù) UI 需求來建模,建議根據(jù) UI 需求來建模,這樣的 State 更容易和 UI 進行綁定。
如下所示,每一個頁面和組件都有各自的狀態(tài)以及用來儲存狀態(tài)的Store:
// State
struct ReduxMessageState: StateType {
var newsList: [MsgItem] = []
}
// Store,直接使用ReSwift的Store類型來初始化即可,初始化時要指定reducer和狀態(tài)的初始值
let newsStore = Store<ReduxMessageState>(reducer: reduxMessageReducer, state: nil)
Store通過一個dispatch方法來接收Action,視圖調用這個方法來向Store傳遞Action:
messageStore.dispatch(ActionRemoveMessage(index: 0))
- Reducer
Reducer是一個比較特殊的函數(shù),Redux強調了數(shù)據(jù)的不可變性(Immutable),簡單來說就是一個數(shù)據(jù)模型在創(chuàng)建之后就不可被修改,那當我們要修改Model某個屬性時要怎么辦呢?答案就是創(chuàng)建一個新的Model,Reducer的作用就體現(xiàn)在這里:
簽名如下:
(_ action: Action, _ state: StateType?) -> StateType
接受一個表示動作的action和一個表示當前狀態(tài)的state,然后計算并返回一個新的State,隨后這個新的State會被更新到Store中:
// Store.swift中的實現(xiàn)
open func _defaultDispatch(action: Action) {
guard !isDispatching else {
raiseFatalError("...")
}
isDispatching = true
let newState = reducer(action, state) // 1. 通過reducer計算出新的state
isDispatching = false
state = newState // 2. 直接將新的state賦值到當前的state上
}
應用中所有數(shù)據(jù)模型的更新操作最終都通過Reducer來完成,為了保證這一套流程可以正常的完成,Reducer必須是一個純函數(shù):它的輸出只取決于輸入的參數(shù),不依賴任何外部變量,同樣也不能包含任何異步的操作。
Demo中的Reducer是這樣寫的:
func reduxMessageReducer(action: Action, state: ReduxMessageState?) -> ReduxMessageState {
var state = state ?? ReduxMessageState()
// 根據(jù)不同的Action對數(shù)據(jù)進行相應的修改
switch action {
case let setMessage as ActionSetMessage: // 設置列表數(shù)據(jù)
state.newsList = setMessage.news
case let remove as ActionRemoveMessage: // 移除某一項
state.newsList.remove(at: remove.index)
default:
break
}
// 最后直接返回修改后的整個State結構體
return state
}
最后在視圖中實現(xiàn)StoreSubscriber協(xié)議newState接收State改變的通知并更新UI即可。
Redux將View -> Model這一層關系分解成了View -> Action -> Store -> Model,每一個模塊只負責一件事情,數(shù)據(jù)始終沿著這條鏈路單向傳遞。
在這個機制下, 一個 App 的狀態(tài)轉換如下:
從業(yè)務操作 -> 產生 Action -> Reducer 接收 Action 和當前 App State 產生新的 AppState -> 更新當前 State -> 通知 UI AppState 有更新 -> UI 顯示新的狀態(tài) -> 下一個業(yè)務操作…
在這個狀態(tài)轉換的過程中,需要注意,業(yè)務操作會有兩類:
- 無異步調用的操作,如點擊界面把界面數(shù)據(jù)存儲到 App State 上;這類操作處理起來非常簡單,按照上面提到的狀態(tài)轉換流程走一圈即可。

- 有異步調用的操作。如點擊查詢,調用 API,數(shù)據(jù)返回之后再存儲到 App State 上。這類操作就需要通過 Action Creators 來處理異步調用并分發(fā)新的 Action。

經(jīng)過 ReSwift 統(tǒng)一管理應用狀態(tài)之后,App 開發(fā)可以得到如下好處:
- 統(tǒng)一管理應用狀態(tài),包括統(tǒng)一的機制和唯一的狀態(tài)容器,這讓應用狀態(tài)的改變更容易預測,也更容易調試。
- 清晰的邏輯拆分,清晰的代碼組織方式,讓團隊的協(xié)作更加容易。
- 函數(shù)式的編程方式,每個組件都只做一件小事并且是獨立的小函數(shù),這增加了應用的可測試性。
- 單向數(shù)據(jù)流,數(shù)據(jù)驅動 UI 的編程方式。
Vuex - ReactorKit
ReactorKit是另一個在Redux思想的實現(xiàn),它的一些設計理念與Vuex十分相似,Vuex則是其專門為Vue提出的狀態(tài)管理模式,其在Redux之上進行了一些優(yōu)化。
與ReSwift不同的是ReactorKit的實現(xiàn)本身便于基于RxSwift,所以不必再考慮如何與Rx結合,下面是ReactorKit中數(shù)據(jù)的流程圖:

大體流程與Redux類似,不同的是Store變成了Reactor,這是ReactorKit引入的一個新概念,它不要求在全局范圍統(tǒng)一管理狀態(tài),而是每個組件管理各自的狀態(tài),所以每個視圖組件都有各自所對應的Reactor,而ReSwift隨著開發(fā)的深入,會擁有日益增大的狀態(tài)樹。
具體的代碼請看Demo中的ReactorKit文件夾,各個部分的含義如下:
- Reactor:
現(xiàn)在用ReactorKit來重寫上面的那個例子,首先需要為這個頁面創(chuàng)建一個實現(xiàn)了Reactor協(xié)議的類型MessageReactor,一個Reactor需要定義State、Action、Mutation這三個部分,首先比起Redux這里多了一個Mutation的概念,在Redux中由于Action直接與Reducer中的操作對應,所以Action只能用來表示同步的操作。ReactorKit將這個概念更加細化,拆分成了兩個部分:Action和Mutation:
Action:視圖層觸發(fā)的動作,可以表示同步和異步(比如網(wǎng)絡請求),它最終會被轉換成Mutation再被傳遞到Reducer中;
Mutation:只能表示同步操作,相當于Redux模式中的Action,最終被傳入Reducer中參與新狀態(tài)的計算;
- mutate():
mutate()是Reactor中的一個方法,用來將用戶觸發(fā)的Action轉換成Mutation,mutate()的存在使得Action可以表示異步操作,因為無論是異步還是同步的Action最后都會被轉換成同步的Mutation:
func mutate(action: MessageReactor.Action) -> Observable<MessageReactor.Mutation> {
switch action {
case .request:
// 1. 異步:網(wǎng)絡請求結束后將得到的數(shù)據(jù)轉換成Mutation
return service.request().map { Mutation.setMessageList($0) }
case .removeItem(let index):
// 2. 同步:直接用just包裝一個Mutation
return .just(Mutation.removeItem(index))
}
}
值得一提的是,這里的mutate()方法返回的是一個Observable<Mutation>類型的實例,得益于Rx強大的描述能力,我們可以用一致的方式來處理同步和異步代碼。
- reduce():
reduce()方法與Redux中的Reducer一樣,唯一不同的是這里接受的是一個Mutation類型,但本質是一樣的:
func reduce(state: MessageReactor.State, mutation: MessageReactor.Mutation) -> MessageReactor.State {
var state = state
switch mutation {
case .setMessageList(let news):
state.newsList = news
case .removeItem(let index):
state.newsList.remove(at: index)
}
return state
}
- Service
圖中還有一個與mutate()產生交互的Service對象,Service指的是實現(xiàn)具體業(yè)務邏輯的地方,Reactor會通過各個Service對象來執(zhí)行具體的業(yè)務邏輯,比如說網(wǎng)絡請求:
protocol MessageServiceType {
/// 網(wǎng)絡請求
func request() -> Observable<[MsgItem]>
}
final class MessageService: MessageServiceType {
func request() -> Observable<[MsgItem]> {
return MessageProvider
.rx
.request(.news)
.mapModel([MsgItem].self)
.asObservable()
}
}
看到這里Reactor的本質基本上已經(jīng)明了:Reactor實際上是一個中間層,它負責管理視圖的狀態(tài),并作為視圖和具體業(yè)務邏輯之間通訊的橋梁。
此外ReactorKit希望我們的所有代碼都通過函數(shù)響應式(FRP)的風格來編寫,這從它的API設計上可以看出:Reactor類型中沒有提供如dispatch這樣的方法,而是只提供了一個Subject類型的變量action:
var action: ActionSubject<Action> { get }
在Rx中Subject既是觀察者又是可觀察對象,常常扮演一個中間橋梁的角色。視圖上所有的Action都通過Rx綁定到action變量上,而不是通過手動觸發(fā)的方式:比方說我們想在viewDidLoad的時候發(fā)起一個網(wǎng)絡請求,常規(guī)的寫法是這樣的:
override func viewDidLoad() {
super.viewDidLoad()
service.request() // 手動觸發(fā)一個網(wǎng)絡請求動作
}
而ReactorKit所推崇的函數(shù)式風格是這樣的:
// bind是統(tǒng)一進行事件綁定的地方
func bind(reactor: MessageReactor) {
self.rx.viewDidLoad // 1. 將viewDidLoad作為一個可觀察的事件
.map { Reactor.Action.request } // 2. 將viewDidLoad事件轉成Action
.bind(to: reactor.action) // 3. 綁定到action變量上
.disposed(by: self.disposeBag)
// ...
}
bind方法是視圖層進行事件綁定的地方,我們將VC的viewDidLoad作為一個事件源,將其轉換成網(wǎng)絡請求的Action之后綁定到reactor.action上,這樣當VC的viewDidLoad被調用時該事件源就會發(fā)出一個事件并觸發(fā)Reactor中網(wǎng)絡請求的操作。
這樣的寫法是更加FRP,一切都是事件流,但是實際用起來并不是那么完美。首先我們需要為用到的所有UI組件提供Rx擴展(上面的例子使用了RxViewController這個庫);其次這對reactor實例初始化的時機有更加嚴格的要求,因為bind方法是在reactor實例初始化的時候自動調用的,所以不能在viewDidLoad中初始化,否則會錯過viewDidLoad事件。
- 優(yōu)點:
相比ReSwift簡化了一些流程,并且以組件為單位來管理各自的狀態(tài),相比起來更容易在現(xiàn)有工程中引入;
與RxSwfit很好的結合在了一起,能提供較為完善的函數(shù)響應式(FRP)開發(fā)體驗; - 缺點:
因為核心思想還是Redux模式,所以模板代碼過多的問題還是無法避免。
三層架構
三層架構是一個分層式的軟件體系架構設計理念。
把軟件架構分為三層;
1:UI層 (user interface layer) 界面層
2:BLL層 (business logic layer) 業(yè)務邏輯層
3:DAL層 (data access layer) 數(shù)據(jù)訪問層
UI層:就是展現(xiàn)給客戶的界面,用于展示用戶輸入以及服務端返回的數(shù)據(jù);交互式操作界面中,用戶輸入的數(shù)據(jù)和想要的數(shù)據(jù)展示。
業(yè)務邏輯層: 橋梁層,用戶輸入的數(shù)據(jù)通過業(yè)務邏輯層的處理發(fā)給數(shù)據(jù)層;數(shù)據(jù)層返回的數(shù)據(jù)通過業(yè)務邏輯層發(fā)送給界面展示。常做的操作是驗證、計算、業(yè)務規(guī)則等。
數(shù)據(jù)訪問層:主要管理數(shù)據(jù),實現(xiàn)對數(shù)據(jù)的增刪改查等操作。把業(yè)務邏輯層提交的用戶輸入的數(shù)據(jù)保存,把業(yè)務邏輯層請求的數(shù)據(jù)返回給業(yè)務邏輯層。
三層架構的重要指導原則就是:高內聚、低耦合。其最大目的就是:解耦。
如何解耦?定義協(xié)議。
MVC,MVP,MVVM都架構可以看作是對UI層的一種細分。
依賴注入DI
把有依賴關系的類放到外部容器中,解析出這些類的實例。
原則:外部創(chuàng)建對象的依賴,而不是自身主動創(chuàng)建。
參考文章:
Coordinators Redux
RXSwift+MVVM+Coordinator
Taming Great Complexity: MVVM, Coordinators And RxSwift
MVVM-C with Swift
MVVMC – ADAPTING THE MVVM DESIGN PATTERN AT RUNTASTIC
MVVMC – ADAPTING THE MVVM DESIGN PATTERN AT RUNTASTIC中文版
用 ReSwift 實現(xiàn) Redux 架構
基于 ReSwift 和 App Coordinator 的 iOS 架構
談談RxSwift和狀態(tài)管理
Redux
iOS應用架構談 開篇
iOS應用架構談 view層的組織和調用方案
iOS應用架構談 網(wǎng)絡層設計方案
深入分析MVC、MVP、MVVM、VIPER
三層架構
依賴注入
Swinject