中介者模式及在NetCore中的使用MediatR來實現
在現實生活中,常常會出現好多對象之間存在複雜的交互關係,這種交互關係常常是「網狀結構」,它要求每個對象都必須知道它需要交互的對象。例如,每個人必須記住他(她)所有朋友的電話;而且,朋友中如果有人的電話修改了,他(她)必須告訴其他所有的朋友修改,這叫作「牽一髮而動全身」,非常複雜。
如果把這種「網狀結構」改為「星形結構」的話,將大大降低它們之間的「耦合性」,這時只要找一個「中介者」就可以了。如前面所說的「每個人必須記住所有朋友電話」的問題,只要在網上建立一個每個朋友都可以訪問的「通訊錄」就解決了。這樣的例子還有很多,例如,你剛剛參力口工作想租房,可以找「房屋中介」;或者,自己剛剛到一個陌生城市找工作,可以找「人才交流中心」幫忙。
在軟體的開發過程中,這樣的例子也很多,例如,在 MVC 框架中,控制器(C)就是模型(M)和視圖(V)的中介者;還有大家常用的 QQ 聊天程式的「中介者」是 QQ 伺服器。所有這些,都可以採用「中介者模式」來實現,它將大大降低對象之間的耦合性,提高系統的靈活性。
中介者模式的適用場景
一般在以下情況下可以考慮使用中介者模式:
- 一組定義良好的對象,現在要進行複雜的相互通訊。
- 想通過一個中間類來封裝多個類中的行為,而又不想生成太多的子類。
模式的定義與特點
中介者(Mediator)模式的定義:定義一個中介對象來封裝一系列對象之間的交互,使原有對象之間的耦合鬆散,且可以獨立地改變它們之間的交互。中介者模式又叫調停模式,它是迪米特法則的典型應用。
中介者模式是一種對象行為型模式,其主要優點如下。
- 降低了對象之間的耦合性,使得對象易於獨立地被複用。
- 將對象間的一對多關聯轉變為一對一的關聯,提高系統的靈活性,使得系統易於維護和擴展。
其主要缺點是:當同事類太多時,中介者的職責將很大,它會變得複雜而龐大,以至於系統難以維護。
廣義中介者
在實際開發中,經常會簡化中介者模式,來是開發變得簡單,比如有如下的簡化。
- 通常會去掉同事對象的父類,這樣可以讓任意的對象,只需要有交互,就可以成為同事
- 通常不定義Mediator介面,把具體的中介者對象實現成為單例
- 同事對象不再持有中介者,而是在需要的時候直接獲取中介者對象並調用;中介者也不再持有同事對象,而是在具體處理方法裡面去創建,或獲取,或從數據傳入需要的同事對象。
經過這樣的簡化、變形的情況稱為廣義中介者。
簡而概之(偽程式碼演示)
中介者模式就是通過一個中介者,來讓多個交錯縱橫的引用的類相互解耦,通過訪問中介者類的方法就可以訪問其他類的方法,每個類之間是相互平等的。比如下面的程式碼,我們有一個學生類,一個教師類,我們在控制器中有兩個Action來處理這兩個類的新增,那我們的實現應該是如下的:
public class Student{//屬性...} public class Teacher{//屬性...}
服務類
public class StudentService { public void Add(Student entity){//添加方法} } public class TeacherService { public void Add(Teacher entity){//添加方法} }
控制器的實現,可以看到有多個服務類被實例化。
public class AddController : Controller { StudentService studentService = new StudentService(); TeacherService teacherService = new TeacherService(); public IActionResult Student(Student entity) { studentService.Add(entity); return Content("添加成功"); } public IActionResult Teacher(Teacher entity) { teacherService.Add(entity); return Content("添加成功"); } }
如果我們用了中介者模式,那就可以免去這些服務類的實例化,直接使用中介者類執行對應的添加方法就行了,下面就進行簡單的實現。
中介者模式的簡單實現
下面只是通過簡單的例子來體現中介者模式的思想,不用過於糾結是否合理,我們可以在實際項目中按自己的需求來實現,就想上面描述的廣義的中介者模式。
學生類和教師類以及對應的服務類
public class Base{} public class Student : Base{//屬性...} public class Teacher : Base{//屬性...} public class StudentService { public string Add(Student entity){return "已添加學生";} } public class TeacherService { public string Add(Teacher entity){return "已添加教師";} }
中介者類
public interface IMediator{string Add(Base entity);} public class Mediator : IMediator { private StudentService studentService = new StudentService(); private TeacherService teacherService = new TeacherService(); public string Add(Base entity) { if(entity is Student) return studentService.Add((Student)entity); else if (entity is Teacher) return teacherService.Add((Teacher)entity); return "添加失敗"; } }
控制器中使用中介者類
public class AddController : Controller { private IMediator iMediator = new Mediator(); public IActionResult Student(Student entity) { return Content(iMediator.Add(entity)); } public IActionResult Teacher(Teacher entity) { return Content(iMediator.Add(entity)); } }
訪問學生新增介面就會出現如下結果:
在NetCore中使用MediatR實現中介者模式
通過 .NET CORE 自帶的 IoC 注入
引用 MediatR nuget:install-package MediatR
引用IOC擴展 nuget:installpackage MediatR.Extensions.Microsoft.DependencyInjection //擴展包
在Startup類中的ConfigureServices方法中註冊MediatR服務,下面這句話可以掃描繼承IRequestHandler介面的實現對象並添加到IOC的容器中
services.AddMediatR(typeof(Startup).GetTypeInfo().Assembly);
為我們的學生類和教師類繼承IRequest介面,表示該對象是處理器的一個對象。
public class Student : IRequest<string>{//屬性...} public class Teacher : IRequest<string>{//屬性...}
為我們的服務類添加IRequestHandler介面創建處理器對象。
public class StudentService : IRequestHandler<Student,string> { public Task<string> Handle(Student entity, CancellationToken cancellationToken) { return Task.FromResult("已添加學生"); } } public class TeacherService : IRequestHandler<Teacher, string> { public Task<string> Handle(Teacher entity, CancellationToken cancellationToken) { return Task.FromResult("已添加教師"); } }
在控制器中使用,通過調用中介者的Send方法,自動匹配對應使用該處理器對象的處理器方法。
public class AddController : Controller { private readonly IMediator _mediator; public AddController(IMediator mediator) { _mediator = mediator; } public IActionResult Student(Student entity) { return Content(_mediator.Send(entity).Result); } public IActionResult Teacher(Teacher entity) { return Content(_mediator.Send(entity).Result); } }
這樣我們的改造就完成了。繼承IRequest介面有一個泛型表示處理器返回參數類型,與IRequestHandler介面的第二個參數和IRequestHandler介面定義的方法Handle方法的返回值對應,可以不定義表示返回void。IRequestHandler介面的第一個參數必須為繼承IRequest介面的類。