zoukankan      html  css  js  c++  java
  • 使用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. }
      
    6. 
      

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

    隐藏行号 复制代码 代码
    1. public class SourcedAggregationRoot : IVersionControllable
      
    2. {
      
    3.     private long version;
      
    4.     private long branch;
      
    5. 
      
    6.     #region IVersionControllable Members
      
    7. 
      
    8.     public long Version
      
    9.     {
      
    10.         get { return this.version; }
      
    11.         internal set { this.version = value; }
      
    12.     }
      
    13. 
      
    14.     public long Branch
      
    15.     {
      
    16.         get { return this.branch; }
      
    17.         internal set { this.branch = value; }
      
    18.     }
      
    19. 
      
    20.     #endregion
      
    21. }
      
    22. 
      

    在上面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<TSourcedAggregationRoot>
      
    2.     where TSourcedAggregationRoot : SourcedAggregationRoot
      
    3. {
      
    4.     private TSourcedAggregationRoot obj;
      
    5. 
      
    6.     public SetterAdapter(TSourcedAggregationRoot obj)
      
    7.     {
      
    8.         this.obj = obj;
      
    9.     }
      
    10. 
      
    11.     public SetterAdapter<TSourcedAggregationRoot> SetVersion(long version)
      
    12.     {
      
    13.         this.obj.Version = version;
      
    14.         return this;
      
    15.     }
      
    16. 
      
    17.     public SetterAdapter<TSourcedAggregationRoot> SetBranch(long branch)
      
    18.     {
      
    19.         this.obj.Branch = branch;
      
    20.         return this;
      
    21.     }
      
    22. 
      
    23.     public TSourcedAggregationRoot Unwrap
      
    24.     {
      
    25.         get { return this.obj; }
      
    26.     }
      
    27. }
      
    28. 
      

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

    隐藏行号 复制代码 代码
    1. SourcedAggregationRoot vc = new SourcedAggregationRoot();
      
    2. // Now Version = 0, Branch = 0
      
    3. vc = new SetterAdapter<SourcedAggregationRoot>(vc)
      
    4.     .SetBranch(100)
      
    5.     .SetVersion(12)
      
    6.     .Unwrap;
      
    7. // Now Version = 12, Branch = 100
      
    8. 
      

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

    欢迎大家针对这样的问题发表自己的观点!

    【点击此处下载本文的示例代码】

  • 相关阅读:
    vue 的模板编译—ast(抽象语法树) 详解与实现
    Vue 组件(component)之 精美的日历
    nvm 装 nodejs 重启终端失效的解决方法
    vue 2 仿IOS 滚轮选择器 从入门到精通 (一)
    np.stack() 与 tf.stack() 的简单理解
    PHP 之 Ci框架下隐藏index.php
    Boosting 简单介绍
    Adaboost算法流程及示例
    Python 之 解码汉字乱码(如果gbk、utf8都试过不行,可以试试这个)
    Linux 之 tar和nc传文件
  • 原文地址:https://www.cnblogs.com/daxnet/p/1796581.html
Copyright © 2011-2022 走看看