为 ServiceCollection 实现装饰器模式

为 ServiceCollection 实现装饰器模式

Intro

在二十四种设计模式中,有一个模式叫做装饰器模式

一般用来动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活,有更好的扩展性,我们也可以借助 Decorator 来实现一个切面逻辑,实现 AOP 的一些功能

使用场景

装饰模式是为已有功能动态地添加更多功能的一种方式

当系统需要新功能的时候,是向旧的类中添加新的代码,这些新加的代码通常装饰了原有类的核心职责或主要行为,但是往往会在主类中加入新的字段/方法/逻辑,从而增加了主类的复杂度, 而这些新加入的东西仅仅是为了满足一些只在某种特定情况下才会执行的特殊行为的需要

装饰模式提供了一个很好的方案,它把每个要装饰的功能放在单独的类中,并让这个类包装它要装饰的对象, 当需要执行特殊行为时,就可以在运行时根据需要有选择地、按顺序地使用装饰功能包装对象了。

装饰模式的优点是把类中的装饰功能从类中搬移去除,这样可以简化原有的类,这样做就有效地把类的核心职责和装饰功能区分开了,而且可以去除相关类中重复的装饰逻辑。

Sample

直接来看示例效果吧:

首先我们定义了一个抽象接口 IJob,接口里有一个 Name 属性和一个 Execute 方法

然后定义了一个实现 Sleepy 实现了我们的 IJob 接口,

最后添加了一个装饰器 JobDecorator 也实现了我们的 IJob 接口

private interface IJob
{string Name { get; }void Execute();
}private sealed class Sleepy : IJob
{public string Name => nameof(Sleepy);public void Execute(){Console.WriteLine("Sleeping...");}
}private sealed class JobDecorator : IJob
{private readonly IJob _job;public JobDecorator(IJob job){_job = job;}public string Name => $"??? {_job.Name}";public void Execute(){Console.WriteLine("Before execute");_job.Execute();Console.WriteLine("After execute");}
}

接着我们看使用的示例吧:

var services = new ServiceCollection();
services.AddSingleton<IJob, Sleepy>();
services.Decorate<IJob, JobDecorator>();
using var sp = services.BuildServiceProvider();var job = sp.GetRequiredService<IJob>();
Console.WriteLine(job.Name);
job.Execute();

输出结果如下:

4ce113e1c990dececb6aafe615b606c8.png

output

可以看到我们在原有实现的基础上加入了我们装饰器的逻辑,我们的 BeforeAfter 也都执行了

Implement

Decorate 方法实现如下:

/// <summary>/// Register decorator for TService/// </summary>/// <typeparam name="TService">service type</typeparam>/// <typeparam name="TDecorator">decorator type</typeparam>/// <param name="services">services</param>/// <returns>services</returns>public static IServiceCollection Decorate<TService, TDecorator>(this IServiceCollection services)where TService : classwhere TDecorator : class, TService{return services.Decorate(typeof(TService), typeof(TDecorator));}/// <summary>/// Register service decorator/// </summary>/// <param name="services">services</param>/// <param name="serviceType">serviceType</param>/// <param name="decoratorType">decoratorType</param>/// <returns>services</returns>/// <exception cref="InvalidOperationException">throw exception when serviceType not registered</exception>public static IServiceCollection Decorate(this IServiceCollection services, Type serviceType, Type decoratorType){var service = services.FirstOrDefault(x => x.ServiceType == serviceType);if (service == null){throw new InvalidOperationException("The service is not registed, service need to be registered before decorating");}var objectFactory = ActivatorUtilities.CreateFactory(decoratorType, new[] { serviceType });var decoratorService = new ServiceDescriptor(serviceType, sp => objectFactory(sp, new object?[]{sp.CreateInstance(service)}), service.Lifetime);services.Replace(decoratorService);return services;}

实现逻辑主要是将原来这个 service 的注册从原来的服务替换成 Decorator,Decorator 也实现了服务类型,是可以替换掉服务接口的注册的,实现比较简单,有些情况可能会有问题,主要是实现思路的分享

More

Github 上有一个关于 Decorator 的 issue,感兴趣的看一下:https://github.com/dotnet/runtime/issues/36021

另外有一个开源项目 https://github.com/khellang/Scrutor 已经实现了基于 ServiceCollection 的 Decorator 支持,我们可以借助它来实现 Decorator 模式(原本以为只有服务注册的扩展,偶然间发现还有 decorator 的实现)

使用示例:

var collection = new ServiceCollection();// First, add our service to the collection.
collection.AddSingleton<IDecoratedService, Decorated>();// Then, decorate Decorated with the Decorator type.
collection.Decorate<IDecoratedService, Decorator>();// Finally, decorate Decorator with the OtherDecorator type.
// As you can see, OtherDecorator requires a separate service, IService. We can get that from the provider argument.
collection.Decorate<IDecoratedService>((inner, provider) => new OtherDecorator(inner, provider.GetRequiredService<IService>()));var serviceProvider = collection.BuildServiceProvider();// When we resolve the IDecoratedService service, we'll get the following structure:
// OtherDecorator -> Decorator -> Decorated
var instance = serviceProvider.GetRequiredService<IDecoratedService>();

感兴趣的可以看一下实现,大体上和上面的思路是一样的

References

  • https://greatrexpectations.com/2018/10/25/decorators-in-net-core-with-dependency-injection

  • https://github.com/dotnet/runtime/issues/36021

  • https://github.com/khellang/Scrutor

  • https://github.com/WeihanLi/WeihanLi.Common/blob/cdeac51a1fe65cddc98fe3d1fd1e43917e19aee4/samples/DotNetCoreSample/ServiceDecoratorTest.cs#L7

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/285603.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

手把手教你启用Win10的Linux子系统 Ubuntu

1、打开“开发者选项” 2、启用“执行 Linux 程序的windows 子系统” 3、应用商店下载安装Ubuntu 附&#xff1a;ubuntu的一些命令及查看已安装软件包的命令 // 系统 # uname -a # 查看内核/操作系统/CPU信息 # head -n 1 /etc/issue # 查看操作系统版本 # cat…

jquery的height()和javascript的height总结,js获取屏幕高度

jquery的height()和javascript的height总结&#xff0c;js获取屏幕高度在javascript和jquery中&#xff0c;都有对各种高度的写法&#xff0c;在这里&#xff0c;我们就着重讲一下窗口、文档等高度的理解。(宽度和高度差不多&#xff01;)jquery的各种高度首先来说一说$(docume…

【地理信息系统GIS】教案(七章全)第五章:空间数据查询与空间分析

目录 第一节 空间数据查询第二节 缓冲区分析第三节 叠置分析第四节 网络分析第五节 DEM建立及分析第一节 空间数据查询 1.1 空间数据查询 1、空间数据查询的含义 从空间数据库中找出所有满足属性约束条件和空间约束条件的地理对象。 空间数据查询的一般过程 2、空间数据查询…

Understanding G1 GC Logs--转载

原文地址&#xff1a;https://blogs.oracle.com/poonam/entry/understanding_g1_gc_logs Understanding G1 GC Logs By Poonam-Oracle on Jun 18, 2012 The purpose of this post is to explain the meaning of GC logs generated with some tracing and diagnostic options fo…

Dart语言精简入门介绍

Dart语言精简入门介绍 1、介绍 Dart 在设计时应该是同时借鉴了 Java 和 JavaScript和kotlin 面向对象 JIT&AOT&#xff1a;JIT&#xff08;Just in Time&#xff09;优点&#xff1a;即时编译&#xff0c;开发期间更快编译&#xff0c;更快的重载&#xff1b;缺点&#…

WPF 窗口居中 变更触发机制

本文经原作者授权以原创方式二次分享&#xff0c;欢迎转载、分享。原文作者&#xff1a;唐宋元明清原文地址&#xff1a;https://www.cnblogs.com/kybs0/p/7420767.html窗口居中 & 变更触发机制解决&#xff1a;1&#xff09;单实例窗口&#xff0c;窗口每次隐藏后再显示时…

[转]5分钟实现Android中更换头像功能

5分钟实现Android中更换头像功能 写在前面&#xff1a; 更换头像这个功能在用户界面几乎是100%出现的。通过拍摄照片或者调用图库中的图片&#xff0c;并且进行剪裁&#xff0c;来进行头像的设置。 功能相关截图如下&#xff1a; 下面我们直接看看完整吧&#xff1a; public cl…

Excel VBA窗体上打印系统时间print now出错原因及解决方案

如图所示,需要在窗体上显示当前系统时间: 首先,我们看一下now函数的原型: Now 函数   语法:Now   说明:返回一个 Variant (Date),根据计算机系统设置的日期和时间来指定日期和时间。   示例: Private Sub CommandButton1_Click()Dim a As Varianta = NowMsgBox…

(第九周)团队项目14

项目名&#xff1a;食物链教学工具 组名&#xff1a;奋斗吧兄弟 组长&#xff1a;黄兴 组员&#xff1a;李俞寰、杜桥、栾骄阳、王东涵 代码地址&#xff1a;HTTPS: https://git.coding.net/li_yuhuan/FoodChain.git SSH: gitgit.coding.net:li_yuhuan/FoodChain.git SCRUM会议…

为什么 C# 访问 null 字段会抛异常?

一&#xff1a;背景 1. 一个有趣的话题最近在看 硬件异常 相关知识&#xff0c;发现一个有意思的空引用异常问题&#xff0c;拿出来和大家分享一下&#xff0c;为了方便讲述&#xff0c;先上一段有问题的代码。namespace ConsoleApp2 {internal class Program{static Person pe…

Android项目实战(十五):自定义不可滑动的ListView和GridView

不可滑动的ListView (RecyclweView类似) public class NoScrollListView extends ListView {public NoScrollListView(Context context, AttributeSet attrs) {super(context,attrs);}public void onMeasure(int widthMeasureSpec, int heightMeasureSpec){ int mExpandSpec …

C语言试题一百之输入某年某月某日,判断这一天是这一年的第几天

✅作者简介:大家好我是码莎拉蒂,CSDN博客专家🥇🥇🥇 📃个人主页:个人主页 🔥系列专栏:C语言试题200例 💬推荐一款模拟面试、刷题神器👉 点击跳转进入网站 1、题目 题目:输入某年某月某日,判断这一天是这一年的第几天? 分析: 以 3 月 5 日为例,应该先把…

[转]Java学习路线图(完整详细2019版)

一门永不过时的编程语言——Java 软件开发。 Java编程语言占比&#xff1a; 据官方数据统计&#xff0c;在全球编程语言工程师的数量上&#xff0c;Java编程语言以1000万的程序员数量位居首位。 而且很多软件的开发都离不开Java编程&#xff0c;因此其程序员的数量最多。而在…

【CASS精品教程】Win7+CAD2008+CASS9.1(含CASS3D)完美安装教程(附完整软件安装包下载)

本文讲解win764位系统上安装CAD2008+CASS9.1(含CASS3D)免费版安装,文末附完整软件下载地址,亲测可用!!! 文章目录 1. CAD2008安装2. CASS9.1安装3. 软件下载地址1. CAD2008安装 双击安装包中的Setup.exe,开始安装。 点击【安装产品】。

(十一)Jmeter另一种调试工具 HTTP Mirror Server

之前我介绍过Jmeter的一种调试工具Debug Sampler&#xff0c;它可以输出Jmeter的变量、属性甚至是系统属性而不用发送真实的请求到服务器。既然这样&#xff0c;那么HTTP Mirror Server又是做什么用的呢&#xff1f; 一、HTTP Mirror Server的作用&#xff1a; 它可以在本地临…

C语言九十八之实现企业发放的奖金根据利润提成。利润(I)低于或等于 10 万元时,奖金可提 10%;利润高 于 10 万元,低于 20 万元时,低于 10 万元的部分按 10%提成,高于 10 万元的

✅作者简介:大家好我是码莎拉蒂,CSDN博客专家🥇🥇🥇 📃个人主页:个人主页 🔥系列专栏:C语言试题200例 💬推荐一款模拟面试、刷题神器👉 点击跳转进入网站 1、题目 企业发放的奖金根据利润提成。利润(I)低于或等于 10 万元时,奖金可提 10%;利润高 于 10 万…

[转]大数据环境搭建步骤详解(Hadoop,Hive,Zookeeper,Kafka,Flume,Hbase,Spark等安装与配置)

大数据环境安装和配置&#xff08;Hadoop2.7.7&#xff0c;Hive2.3.4&#xff0c;Zookeeper3.4.10&#xff0c;Kafka2.1.0&#xff0c;Flume1.8.0&#xff0c;Hbase2.1.1&#xff0c;Spark2.4.0等&#xff09; 系统说明搭建步骤详述 一、节点基础配置 二、Hadoop安装和配置三、…

C# Any()和AII()方法

我们常常需要的另一类查询是确定数据是否满足某个条件&#xff0c;或者确保所有数据都满足某个条件。例如&#xff0c;需要确定某个产品是否已经脱销(库存为 0)&#xff0c;或者是否发生了某个交易。LINQ 提供了两个布尔方法&#xff1a;Any()和 All()&#xff0c;它们可以快速…

树状数组 + 位运算 LA 4013 A Sequence of Numbers

题目传送门 题意&#xff1a;n个数&#xff0c;两种操作&#xff0c;一是每个数字加x&#xff0c;二是查询& (1 << T) 1 的个数 分析&#xff1a;因为累加是永远的&#xff0c;所以可以离线处理。树状数组点是c[16][M] 表示数字x%(1 << j) 后的数字pos&#x…

【地理信息系统GIS】教案(七章全)第七章:3S技术综合应用

文章目录 第一节 3S技术概述第二节 GIS与RS的综合应用第三节 GIS与GPS的综合应用第四节 网络GIS的综合应用第一节 3S技术概述 1.什么是“3S” 技术? 遥感(Remote Sensing ,RS); 地理信息系统(Geographical information System ,GIS); 全球定位系统(Global Positio…