中介者模式及在NetCore中的使用MediatR來實現

樹杈發表於2020-11-15

在現實生活中,常常會出現好多物件之間存在複雜的互動關係,這種互動關係常常是“網狀結構”,它要求每個物件都必須知道它需要互動的物件。例如,每個人必須記住他(她)所有朋友的電話;而且,朋友中如果有人的電話修改了,他(她)必須告訴其他所有的朋友修改,這叫作“牽一髮而動全身”,非常複雜。

如果把這種“網狀結構”改為“星形結構”的話,將大大降低它們之間的“耦合性”,這時只要找一個“中介者”就可以了。如前面所說的“每個人必須記住所有朋友電話”的問題,只要在網上建立一個每個朋友都可以訪問的“通訊錄”就解決了。這樣的例子還有很多,例如,你剛剛參力口工作想租房,可以找“房屋中介”;或者,自己剛剛到一個陌生城市找工作,可以找“人才交流中心”幫忙。

在軟體的開發過程中,這樣的例子也很多,例如,在 MVC 框架中,控制器(C)就是模型(M)和檢視(V)的中介者;還有大家常用的 QQ 聊天程式的“中介者”是 QQ 伺服器。所有這些,都可以採用“中介者模式”來實現,它將大大降低物件之間的耦合性,提高系統的靈活性。

中介者模式的適用場景

一般在以下情況下可以考慮使用中介者模式:

  1. 一組定義良好的物件,現在要進行復雜的相互通訊。
  2. 想通過一箇中間類來封裝多個類中的行為,而又不想生成太多的子類。

模式的定義與特點

中介者(Mediator)模式的定義:定義一箇中介物件來封裝一系列物件之間的互動,使原有物件之間的耦合鬆散,且可以獨立地改變它們之間的互動。中介者模式又叫調停模式,它是迪米特法則的典型應用。

中介者模式是一種物件行為型模式,其主要優點如下。

  1. 降低了物件之間的耦合性,使得物件易於獨立地被複用。
  2. 將物件間的一對多關聯轉變為一對一的關聯,提高系統的靈活性,使得系統易於維護和擴充套件。


其主要缺點是:當同事類太多時,中介者的職責將很大,它會變得複雜而龐大,以至於系統難以維護。

廣義中介者

在實際開發中,經常會簡化中介者模式,來是開發變得簡單,比如有如下的簡化。

  1.  通常會去掉同事物件的父類,這樣可以讓任意的物件,只需要有互動,就可以成為同事
  2.  通常不定義Mediator介面,把具體的中介者物件實現成為單例
  3.  同事物件不再持有中介者,而是在需要的時候直接獲取中介者物件並呼叫;中介者也不再持有同事物件,而是在具體處理方法裡面去建立,或獲取,或從資料傳入需要的同事物件。

經過這樣的簡化、變形的情況稱為廣義中介者。

簡而概之(虛擬碼演示)

中介者模式就是通過一箇中介者,來讓多個交錯縱橫的引用的類相互解耦,通過訪問中介者類的方法就可以訪問其他類的方法,每個類之間是相互平等的。比如下面的程式碼,我們有一個學生類,一個教師類,我們在控制器中有兩個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介面的類。

相關文章