栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

使用Object Adapter模式维护成员的访问级别

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

使用Object Adapter模式维护成员的访问级别

使用Object Adapter模式维护成员的访问级别

在设计class library或者framework时有可能遇到这样的问题,或许有的朋友已经碰到过这样的问题了。比如,在实现CQRS体系结构模式时,我们通过Versioning和Branching的方式设计Event Sourcing的版本路线(Version Route),至于什么是Versioning和Branching,以及为何需要在Event Sourcing中引入Version Control,本文不做详细讨论。有兴趣的朋友可以去阅读一些有关版本控制的文章。

那么,我们很有可能会在class library中加入下面这个接口:

  1. public interface IVersionControllable
  2.  {
  3.     long Version { get; }
  4.     long Branch { get; }
  5. }


由于Version和Branch的数据是由整个框架内部管理的,只允许客户程序读取这两个值,而不允许客户程序随意修改,因此,在接口中,这两个属性被指定为只读。理所当然,实现了该接口的类,必定需要实现其中的这两个属性:


  1. public class SourcedAggregationRoot : IVersionControllable
  2. {
  3.     private long version;
  4.     private long branch;

  5.     #region IVersionControllable Members

  6.     public long Version
  7.     {
  8.         get { return this.version; }
  9.         internal set { this.version = value; }
  10.     }

  11.     public long Branch
  12.     {
  13.         get { return this.branch; }
  14.         internal set { this.branch = value; }
  15.     }

  16.     #endregion
  17. }

在上面SourcedAggregationRoot的实现中,我们为Version和Branch属性加上了internal setter,目的很明显,就是希望能够在class library中修改version和branch的值,而不允许框架以外的客户程序去修改这两个值。目前看似达到了成员访问级别的控制目的,但实际上事情并没结束。

假设我们现在开发的是一套完整的应用程序框架,既然是框架,就需要支持扩展。假设框架中某组件A(也假设A实现接口IA)会使用以上两个属性去更改SourcedAggregationRoot的版本信息,如果组件A与SourcedAggregationRoot被定义在同一个assembly中,那么,A可以直接使用这两个internal setter来修改SourcedAggregationRoot的version和branch。当然,A组件只是我们的框架所提供的一个默认组件,A的功能是可以被扩展甚至重写的。现在,根据客户需求,A组件的功能需要重写(比如原本是采用的SQL Server DAL,现在要用Oracle DAL了),于是我们就会重新新建一个class library,在这个class library中,新建一个类,实现接口IA,然后填写我们自己的逻辑。在完成某项操作时,也去调用SourcedAggregationRoot的Version和Branch属性,试图更改这两个值。于是,问题来了,由于internal访问级别的限制,我们无法修改Version和Branch,连编译都过不去。

你可能会说,这好办,直接将internal关键字去掉不就ok啦。这样做爽是爽了,但也导致客户程序能够非常轻易地修改version和branch的值,而这两个值本应该由框架进行维护的。Coding上有一个原则就是尽量把代码错误控制在编译时。将Version和Branch属性改为公有可写(public setter)的话,很难保证客户程序不会对其进行误操作。总之一句话,现在希望framework中的组件能够修改version和branch,客户程序不允许对其进行修改。貌似现有的C#访问控制修饰符没法达到这个看似变态的要求。

其实解决方案也很简单,就是在SourcedAggregationRoot所在的assembly中,直接加一个object adapter,然后把adapter设置为public的就可以了:


  1. public class SetterAdapter
  2.     where TSourcedAggregationRoot : SourcedAggregationRoot
  3. {
  4.     private TSourcedAggregationRoot obj;

  5.     public SetterAdapter(TSourcedAggregationRoot obj)
  6.     {
  7.         this.obj = obj;
  8.     }

  9.     public SetterAdapter SetVersion(long version)
  10.     {
  11.         this.obj.Version = version;
  12.         return this;
  13.     }

  14.     public SetterAdapter SetBranch(long branch)
  15.     {
  16.         this.obj.Branch = branch;
  17.         return this;
  18.     }

  19.     public TSourcedAggregationRoot Unwrap
  20.     {
  21.         get { return this.obj; }
  22.     }
  23. }

这样,我们就可以在framework的其它class library或assembly中,使用下面的方式改变SourcedAggregationRoot的version和branch的值了:


  1. SourcedAggregationRoot vc = new SourcedAggregationRoot();
  2. // Now Version = 0, Branch = 0
  3. vc = new SetterAdapter(vc)
  4.     .SetBranch(100)
  5.     .SetVersion(12)
  6.     .Unwrap;
  7. // Now Version = 12, Branch = 100

当然,客户代码同样可以使用SetterAdapter来修改这两个值,但与直接将Version和Branch属性设置为public相比,这种做法要安全的多。我觉得,在做framework设计的时候,每个细节都要仔细斟酌,成员的访问级别设置过高,并不影响整个框架的运行结果,但这样做会打破面向对象的封装特性,把本不应该暴露的信息一览无余地暴露给调用者。在上面的例子中引入SetterAdapter,维护了Version和Branch属性的访问级别。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/231890.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号