Appearance
观察者模式
编程的时候,经常会遇到变更信息时,需要同时变更其它数据的情况。
如在用户信息变更后,希望与此用户相关的信息进行相应变更。但在开发用户信息的时候,无法预见有多少业务会与用户相关。
但我们在新增业务时,却比较容易识别出,当前业务与用户信息相关。但如果每开发一个新的业务场景,都去修改一遍用户信息的逻辑,显然是不合适的。
首先是用户信息的逻辑与业务信息耦合度非常高,导致用户信息的变更变得非常复杂;同时,由于每次新增业务都修改用户信息逻辑,可能一个不慎就会影响其它的业务,导致所有业务都可能需要测试一遍,测试任务非常大。
这个时候,观察者模式可以非常好的解决这个问题。
sh
dotnet add package AsDI.Observer --version 2.0.0下面以部门信息变更,修改用户的部门名称字段为例:
基本示例
例如,编写如下代码:
C#
[AllowObserve] //第一步,首先允许部门服务被观察
public interface IDeptService
{
int Update(DepartmentDTO departmentDTO);
}
[Observer] //第二步,定义用户服务是个观察者
public interface IUserService
{
int update(UserDTO userDTO);
/// <summary>
/// 观察部门的变更
/// </summary>
/// <param name="departmentDTO">部门信息</param>
[Observe(typeof(IDeptService), "Update")] //第二步,定义观察内容,示例表示,观察 IDeptService 的 Update 方法
void OnDepartmentUpdate([ParameterOf(0)] DepartmentDTO departmentDTO);
}以上代码,在运行时,当部门的Update方法执行后,IUserService 的 OnDepartmentUpdate 方法将会被自动执行。需要注意的是:
- 当部门服务的Update执行报错时,用户服务的OnDepartmentUpdate方法将不再执行
- 观察者方法的参数,可以通过 [ParameterOf] 来标识参数来源于原方法的哪个参数,这个标识可以对应位置,也可以对应名称,如果参数名与原方法的参数名一致,也可以不标识
- 如果存在方法重载(overload)的情况时,所有同名的方法,如果符合被观察的情况,都会调用观察方法。如果只观察其中的一个方法,可以在 [Observe] 标识中增加参数限定
- 观察者也可以同时是被观察者,观察者也不用单独写一个接口,它可以有自己的其它服务
- 虽然观察者甚至可以观察自身的方法,但尽量不要这么做,反而增加了复杂度
- 观察者与被观察者本质上是先后执行的,所以日志和全局事务,也会有相关性,比如被观察者执行失败,导致事务回滚,观察者也会事务回滚,除非被观察者自己处理了异常
完整示例
以下一个示例,当用户变更时,会修改部门的部门经理的名字;当部门变更时,会修改用户所在部门的名称;
C#
[Observer,AllowObserve]
public interface IDeptService
{
int Update(DepartmentDTO departmentDTO);
/// <summary>
/// 观察用户的变更
/// </summary>
/// <param name="userDTO">用户信息</param>
[Observe(typeof(IDeptService), "Update")]
void OnUserUpdate([ParameterOf(0)] UserDTO userDTO);
}
[Observer,AllowObserve]
public interface IUserService
{
int Update(UserDTO userDTO);
/// <summary>
/// 观察部门的变更
/// </summary>
/// <param name="departmentDTO">部门信息</param>
[Observe(typeof(IDeptService), "Update")]
void OnDepartmentUpdate([ParameterOf(0)] DepartmentDTO departmentDTO);
}
[Service]
public class DeptService : IDeptService
{
private readonly IDeptRepository repository;
public UserService(IDeptRepository repository)
{
this.repository = repository;
}
public int Update(DepartmentDTO departmentDTO){
repository.Update(Dto2Entity(departmentDTO));
}
public void OnUserUpdate(UserDTO userDTO)
{
repository.UpdateLeaderName(userDTO.Id, userDTO.Name);
}
}
[Service]
public class UserService : IUserService
{
private readonly IUserRepository repository;
public UserService(IUserRepository repository)
{
this.repository = repository;
}
public int Update(UserDTO userDTO){
repository.Update(Dto2Entity(userDTO));
}
public void OnDepartmentUpdate(DepartmentDTO departmentDTO)
{
repository.UpdateDeptName(departmentDTO.Id, departmentDTO.DeptName);
}
}