Context+ContextScope——这是否可以看作一种设计模式?

相信大家对TransactionScope都比较熟悉。通过TransactionScope,我们可以很容易地将一组操作纳入同一个事务中;或者说TransactionScope创建一个基于事务的上下文范围,在这个范围内共享一个相同的环境事务(Ambient

Transaction)。我个人觉得这体现了一种可以重用的模式,即本篇文章介绍的Context+ContextScope模式,这种模式旨在一定范围内创建一个可以共享的上下文信息。

我们通过一个简单的例子来对Context+ContextScope模式进行简单说明。比如在一个安全级别比较高的GUI应用中,我们需要对用户的每一个UI操作进行安全审核(Auditing,比如记录下当前操作的意图、操作时间和用户名等),我们将这个被审核的UI操作称为“活动(Activity)”。如果我们能够将针对这个UI操作的执行(事件的触发、对业务逻辑的执行以及对数据库的访问)纳入同一个基于活动的上下文中,那么审核就可以直接从当前的环境上下文中获取到相应的审核信息了。[源代码从这里下载]

一、ActivityContext

为此,我们创建了如下一个名为ActivityContext的类型表示针对Activity的上下文。ActivityContext具有三个实例属性,其中ActivityName和DateTime表示活动的名称和开始时间,而字典类型的Properties
属性用于维护Activity相关的额外属性。静态Current属性表示当前的环境上下文(Ambient
Context),它返回的是静态字段current。值得一提的,在该静态字段上应用了ThreadStaticAttribute特性,意味着静态字段仅仅限于当前的线程。这也说明了Context+ContextScope模式主要应用于同步环境,如果需要对异步环境进行支持,可以做一些额外处理。

   1: public class ActivityContext: IDisposable
   2: {
   3:     [ThreadStatic]
   4:     private static ActivityContext current;
   5:  
   6:     public string ActivityName { get; private set; }
   7:     public DateTime StartTime { get; private set; }
   8:     public IDictionary<string, object> Properties { get; private set; }
   9:  
  10:     internal ActivityContext(string activityName)
  11:     {
  12:         this.ActivityName = activityName;
  13:         this.StartTime = DateTime.Now;
  14:         this.Properties = new Dictionary<string, object>();
  15:     }
  16:  
  17:     public static ActivityContext Current
  18:     {
  19:         get { return current; }
  20:         internal set{current = value;}
  21:     }
  22:  
  23:     public void Dispose()
  24:     {
  25:         foreach (var property in this.Properties.Values)
  26:         {
  27:             IDisposable disposable = property as IDisposable;
  28:             if (null != disposable)
  29:             {
  30:                 disposable.Dispose();
  31:             }
  32:         }
  33:     }
  34: }

二、ActivityContextScope

Context+ContextScope的核心不在于Context而在于ContextScope,即我们需要控制上下文的范围。对于我们的安全审核场景来说,我们需要针对用于的UI操作(比如点击某个按钮)创建ActivityContext,该上下文的生命周期仅限于针对UI事件的响应过程。为此我们创建一个ActivityContextScope类型用于创建ActivityContext并控制其生命周期。如下面的代码片断所示,ActivityContextScope除了接受一个表示创建活动的名称之外,还具有一个ContextScopeOperation枚举类型的参数。

   1: public class ActivityContextScope: IDisposable
   2: {
   3:     private ActivityContext current = ActivityContext.Current;
   4:     private ActivityContext newContext;
   5:  
   6:     public ActivityContextScope(string activityName, ContextScopeOption contextScopeOption = ContextScopeOption.Required)
   7:     {
   8:         switch (contextScopeOption)
   9:         {
  10:             case ContextScopeOption.Required:
  11:                 {
  12:                     if (null == current)
  13:                     {
  14:                         ActivityContext.Current = newContext = new ActivityContext(activityName);
  15:                     }
  16:                     break;
  17:                 }
  18:             case ContextScopeOption.RequiresNew:
  19:                 {
  20:                     ActivityContext.Current = newContext = new ActivityContext(activityName);
  21:                     break;
  22:                 }
  23:             case ContextScopeOption.Suppress:
  24:                 {
  25:                     ActivityContext.Current = null;
  26:                     break;
  27:                 }
  28:         }
  29:     }
  30:  
  31:     public void Dispose()
  32:     {
  33:         ActivityContext.Current = current;
  34:         if (null != newContext)
  35:         {
  36:             newContext.Dispose();
  37:         }
  38:     }
  39: }
  40:  
  41: public enum ContextScopeOption
  42: {
  43:     Required,
  44:     RequiresNew,
  45:     Suppress
  46: }

考虑在创建ActivityContextScope的时候,当前环境上下文可能已经存在,那么是重用现成的上下文还是创建新的上下文,可以通过ContextScopeOperation枚举来控制。该枚举类型的Required和RequiredNew选项分别表示重用现有上下文和创建新的上下文。另一个选项Supress表示创建一个“无环境上下文”的范围,比如TransactionScope通过类似的机制将不需要纳入事务的操作(比如Logging)从环境事务中剥离出来。基于ContextScopeOperation的ActivityContext创建机制体现在ActivityContextScope的构造函数中。ActivityContextScope实现了IDisposable接口,在实现的Dispose方法中我们将通过ActivityContext的静态属性Current表示的环境上下文恢复到ActivityContextScope创建之前的状态。

三、ActivityContextScope的使用

我们通过如下一个简单的实例来演示ActivityContextScope的使用。在Main方法中我们在一个基于“Activty1”的ActivityContextScope中调用Activty1方法。在Activty1方法中,我们在一个基于“Activty2”的ActivityContextScope中调用Activty2方法。两次创建ActivityContextScope都采用默认的ContextScopeOperation(Required)。在方法Activty2中,我们在一个基于“Activty3”的ActivityContextScope中调用Activty3方法,创建ActivityContextScope时选择RequiredNew选项。而在Activty3方法中,我们针对Supress选项创建ActivityContextScope并调用Activity4方法。方法Activty1、Activty2、Activty3和Activty4中均调用DisplayCurrentContext将当前的ActivityContext信息打印出来。

   1: class Program
   2: {
   3:     static void Main(string[] args)
   4:     {
   5:         using (ActivityContextScope contextScope = new ActivityContextScope("Activty1"))
   6:         {
   7:             Activty1();
   8:         }
   9:     }
  10:  
  11:     static void DisplayCurrentContext(string methodName)
  12:     {
  13:         if (null != ActivityContext.Current)
  14:         {
  15:             Console.WriteLine("{0}: Current ambient activity is {1}", methodName, ActivityContext.Current.ActivityName);
  16:         }
  17:         else
  18:         {
  19:             Console.WriteLine("{0}: No ambient activity.", methodName);
  20:         }
  21:     }
  22:  
  23:     private static void Activty1()
  24:     {
  25:         DisplayCurrentContext("Activty1");
  26:         using (ActivityContextScope contextScope = new ActivityContextScope("Activty2"))
  27:         {
  28:             Activty2();
  29:         }
  30:     }
  31:  
  32:     private static void Activty2()
  33:     {
  34:         DisplayCurrentContext("Activty2");
  35:         using (ActivityContextScope contextScope = new ActivityContextScope("Activty3", ContextScopeOption.RequiresNew))
  36:         {
  37:             Activty3();
  38:         }
  39:     }
  40:  
  41:     private static void Activty3()
  42:     {
  43:         DisplayCurrentContext("Activty3");
  44:         using (ActivityContextScope contextScope = new ActivityContextScope("", ContextScopeOption.Suppress))
  45:         {
  46:             Activty4();
  47:         }
  48:     }
  49:  
  50:     private static void Activty4()
  51:     {
  52:         DisplayCurrentContext("Activty4");
  53:     }
  54: }

上面这段程序执行之后会在控制台上生成如下的输出结果,我们可以看到当前环境上下文是严格按照我们创建ActivityContextScope的方式来控制的。

   1: Activty1: Current ambient activity is Activty1
   2: Activty2: Current ambient activity is Activty1
   3: Activty3: Current ambient activity is Activty3
   4: Activty4: No ambient activity.

作者:蒋金楠
微信公众账号:大内老A
微博:www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

原文链接

时间: 2025-01-19 18:33:35

Context+ContextScope——这是否可以看作一种设计模式?的相关文章

谈谈23种设计模式在Android项目中的应用

前言 本文将结合实际谈谈23种设计模式,每种设计模式涉及 定义:抽象化的定义与通俗的描述,尽量说明清楚其含义与应用场景 示例:如果项目中有使用过该模式,则会给出项目中的代码,否则会给出尽可能简单好理解的java代码 Android:该设计模式在Android源码框架中哪些地方有使用到 重构:项目中是否存在可以用该模式进行重构的地方,如果有会给出重构前与重构后的代码或者思路 用这种方式进行介绍设计模式,旨在结合每天都在接触的Android实际项目开发更好地理解设计模式,拉近与设计模式的距离,同时在

iOS 中的 21 种设计模式

iOS 中的 21 种设计模式 对象创建原型(Prototype) 使用原型实例指定创建对象的种类,并通过复制这个原型创建新的对象. 1 2 NSArray *array = [[NSArray alloc] initWithObjects:@1, nil]; NSArray *array2 = array.copy; array 就是原型了,array2 以 array 为原型,通过 copy 操作创建了 array2. 当创建的实例非常复杂且耗时,或者新实例和已存在的实例值相同,使用原型模式

23种设计模式全解析

一.设计模式的分类 总体来说设计模式分为三大类: 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接模式.组合模式.享元模式. 行为型模式,共十一种:策略模式.模板方法模式.观察者模式.迭代子模式.责任链模式.命令模式.备忘录模式.状态模式.访问者模式.中介者模式.解释器模式. 其实还有两类:并发型模式和线程池模式.用一个图片来整体描述一下:   二.设计模式的六大原则 总原则:开闭原则(Open

Java开发中的23种设计模式详解(转)

Java开发中的23种设计模式详解(转) 设计模式(Design Patterns)                                   --可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性. 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样.项目中合

Java开发中的23种设计模式详解(推荐)_java

设计模式(Design Patterns)                                   --可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性. 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样.项目中合理的运用设计模式可以完美的解决很多问题,每

Java技术_每天掌握一种设计模式(003)_使用场景及简单实例(创建型:工厂方法)

Java技术_每天掌握一种设计模式(001)_设计模式概念及分类 Java技术_每天掌握一种设计模式(002)_使用场景及简单实例(创建型:单例模式) Java技术_每天掌握一种设计模式(003)_使用场景及简单实例(创建型:工厂方法) Java技术_每天掌握一种设计模式(004)_使用场景及简单实例(创建型:抽象工厂) Java技术_每天掌握一种设计模式(005)_使用场景及简单实例(创建型:建造模式) Java技术_每天掌握一种设计模式(006)_使用场景及简单实例(创建型:原型模式) 1.

参透Node中exports的7种设计模式

前言 这篇文章试着要整理,翻译Export This: Interface Design Patterns for Node.js Modules这篇非常值得一读的文章. 但因为这篇文章有些时日了,部分示例已经不符合现况.故这是一篇加上小弟收集汇整而成的更新翻译. 旅程的开始 当你在Node中加载一个模块,我们到底会取回什么?当我们撰写一个模块时我们又有哪些选择可以用来设计程序的界面? 在我第一次学习Node的时候,发现在Node中有太多的方式处理这个问题,由于Javascript本身非常弹性,

从追MM谈Java的23种设计模式

Normal 0 7.8 磅 0 2 false false false EN-US ZH-CN X-NONE MicrosoftInternetExplorer4 从追MM 谈Java 的23 种设计模式   1 .FACTORY- 追MM 少不了请吃饭了,麦当劳的鸡翅和肯德基的鸡翅都是MM 爱吃的东西,虽然口味有所不同,但不管你带MM 去麦当劳或肯 德基,只管向服务员说" 来四个鸡翅" 就行了.麦当劳和肯德基就是生产鸡翅的Factory.   工厂模式:客户类和工厂类分开.消费者任

概述:3种设计模式

     什么是设计模式?     设计模式的每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心.这样,你就能一次又一次地使用该方案而不必做重复劳动.     设计模式让我们站在了巨人的肩膀上,告诉我们不是解决任何问题都是要从头开始的. 3种设计模式 首先来看设计模式是如何分类的:  我们根据根据两条准则对设计模式进行分类: 1,目的准则: 模式是用来完成什么工作的. 模式依据其目的可分为:创建型,结构型,行为型. 其中:    创建型模式与对象的创建有关:    结构