zoukankan      html  css  js  c++  java
  • 领域模型管理基础点汇总(2)

    基础代码复用性技巧之:混入(MiXin)和拦截器类

       通过上一篇的介绍,我们基本能够建立出来一个有效率、可访问的基础架构来实行运行时的领域管理,在前篇的例子中我们为延迟加载功能设置的加载标记必须添加到需要支持懒加载的每个类的子类中。而且,在我们的例子中,如果你观察下加载标记是一次写入的,也就是说在变成true之后就不能再切换回来。我们在Setter方法中,我们有一些代码来实施这个规则——需要懒加载的每个领域模型类的子类中,我们都要为每个领域模型类的子类中,我们都要在setter方法中重复那些代码。

       为了近大限度的实现基础架构的重用,我们就应该创建一个可重用的类来包含这种逻辑。但是,我们已经使用了继承,并且是单继承的平台上,我们如何实现这个懒加载类的重用呢?

      其实,这个问题解决起来并不难,我记得在GOF的设计模式中就提到过此问题:

      组合/聚合复用原则(CARP),尽量使用合成/聚合,尽量不要使用继承。

      对象的继承关系是在编译时就定义好了,所以无法在运行时改变从父类继承的实现。子类的实现于它的父类有着非常紧密的耦合关系,以至于父类实现中的任何变化必然会导致子类发生变化。当你需要复用子类时,如果继承下来的实现不适合解决新问题,则父类必须重写或被其它更适合的类替换。这种依赖关系限制了灵活性并最终限制了复用性。

      到此我们应该找打了解决此问题的答案了:组合模式,而不是采用继承。使用这种模式,EmployeeProxy子类将包含一个内部属性来引用可重用的懒加载的实例:

    这种可重用的类常常被称为混入(mixin),它可以实现被加入到类型中,而不是成为继承层的一部分。使用混入的作用是,让单继承也能获得类似的多继承的效果。

    通过将子类引入成员分离到一个可重用的混入子类,我们已经向真正模块化、一致的、内聚的架构迈进了一大步,这个架构还是低耦合的,高可重用性的,但是组合模式就可以了吗?如果你考虑下我们会有更好的方式,那就是面向切面编程(AOP),因为大部分的基础逻辑代码还是糅合到了领域模型的子类中去,通过AOP方式的使用,我们把拦截代码从子类中分离出来。然后将其放到可重用的类里面。这样做岂不更好,这些拦截类回想混入一样包含在代理子类里面,看架构图:

    Person、Employee和DomainBase于代码的上一个版本保持不变,但是PersonProxy和EmployeeProxy发生了变化,并且引入了两个新的拦截器类和一个新的混入类。

        #region 基础Base
    public class DomainBase : IDirty
    {
    private bool dirty;
    bool IDirty.Dirty
    {
    get
    {
    return dirty;
    }
    set
    {
    dirty = value;
    }
    }
    }
    interface ILazy
    {
    bool Loaded
    {
    get;
    set;
    }
    }
    interface IDirty
    {
    bool Dirty { get; set; }
    }
    public class LazyInterceptor
    {
    public void onPropertyGetSet(object obj)
    {
    ILazy lazy = obj as ILazy;
    if (lazy != null)
    {
    if (!lazy.Loaded)
    {
    lazy.Loaded = true;
    //将懒加载移动到此
    //添加逻辑
    }
    }
    }
    }
    public class DirtyInterceptor
    {
    public void OnPropertySet(
    object obj,
    object oldValue,
    object newValue)
    {
    if(!oldValue.Equals(newValue))
    {
    IDirty dirty=obj as IDirty;
    if(dirty!=null)
    dirty.Dirty=true;
    }
    }

    }
    public class LazyMinxin:ILazy
    {
    private bool loaded;
    bool ILazy.Loaded
    {
    get
    {
    return loaded;
    }
    set
    {
    if(loaded)
    return;
    loaded=value;
    }
    }
    }
    #endregion

    #region 领域对象
    public class Person : DomainBase
    {
    protected string name;
    public virtual string Name
    {
    get { return name; }
    set { name = value; }
    }
    }
    public class Employee : Person
    {
    protected decimal salary;
    public virtual decimal Salary
    {
    get
    {
    return salary;
    }
    set
    {
    salary = value;
    }
    }
    }
    #endregion


    public class PersonProxy : Person
    {
    //新建aop对象
    private DirtyInterceptor dirtyInterceptor = new DirtyInterceptor();
    public override string Name
    {
    get
    {
    return base.Name;
    }
    set
    {
    dirtyInterceptor.OnPropertySet(this, this.name, value);
    base.Name = value;
    }
    }
    }

    public class EmployeeProxy : Employee, ILazy
    {
    private ILazy lazyMixin = new LazyMinxin();
    private LazyInterceptor lazyInterceptor = new LazyInterceptor();
    private DirtyInterceptor dirtyInterceptor = new DirtyInterceptor();

    public override string Name
    {
    get
    {
    lazyInterceptor.onPropertyGetSet(this);
    return base.Name;
    }
    set
    {
    lazyInterceptor.onPropertyGetSet(this);
    dirtyInterceptor.OnPropertySet(this, this.name, value);
    base.Name = value;
    }
    }

    public override decimal Salary
    {
    get
    {
    lazyInterceptor.onPropertyGetSet(this);
    return base.Salary;
    }
    set
    {
    lazyInterceptor.onPropertyGetSet(this);
    dirtyInterceptor.OnPropertySet(this, this.name, value);
    base.Salary = value;
    }
    }

    private bool loaded;
    bool ILazy.Loaded
    {
    get
    {
    return loaded;
    }
    set
    {
    if (loaded)
    return;
    loaded = value;
    }
    }

    }

    通过重构,最终所有的实际的基础架构逻辑都被放进了混入和拦截器类,代理只是成为了一个接收器这些功能的中转器,连接领域模型和混入和拦截器,变成了专注于转发请求到拦截器和混入的轻量级的类。

     我们选择的是在设计时生成代理子类的代码,但是想c#和java这样的语言能让你在运行时生成、编译、执行代码。因此,我们也可以选择运行时生成代理子类,这种做法就是运行时子类化(runtime subclassing)

     整理下我们重构的思路:

     1、我们将肥领域模型重构为带有代理子类的POJO/POCO领域模型(还可选择增加一个基类),同时基础架构代码仍然分布在领域模型中。

    2、所有的实际的基础架构逻辑从代理子类分离出来,并加入到重用的织入和拦截器,在代理类中只留下了样板代码

    如果你真正的研究过面向方面编程(AOP)你会发现,我们上面所应用的方式并不是规范的应用,作为基于垂直体系架构的面向对象思路的补充,AOP已经完整的形成了它自己的规范思路,在java里面已经应用的接近成熟,在.net里面也慢慢的逐近成熟,如果想详细了解里面的思路及运行情况可以参照院子里张逸老师的博客。

    总结
     通过使用代理模式、抽象工厂模式、组合模式这些传统的、知名的面向对象的设计模式来重构我们的肥领域模型,我们得到了一个极好的结合了精艺领域模型和基础架构的应用架构,基础架构充分利用了面向对象的概念和构造。其实,我们已经桥面的避免了陷入充血模型和贫血模型等固定模式的陷阱。

      里面所讨论的模式,得到了一个可以被形容的面向方面的应用架构,使得我们引入了AOP等优秀架构,解决了横切关注点、织入和拦截器等完美的配合了横切面功能的实现,当然所谓编程注重的是一种思维的升华,需要我们在慢慢的编程生涯中体会,体会到前辈们给我们后来人所提炼出来的这些思维盛宴。

    本文非原创,参考资料如下:

    [Evans DDD]

      《领域驱动设计:软件核心复杂性应对之道,Evans Eric著。 ——马萨诸塞州,波士顿:Addison-Wesley出版社,2004。

      [Fowler贫血领域模型]

      Fowler Martin:http://martinfowler.com/bliki/AnemicDomainModel.html  

       [Fowler PoEAA] http://martinfowler.com/bliki/AnemicDomainModel.html   

         《企业应用架构模式》,Fowler Martin著。 ——马萨诸塞州,波士顿:Addison-Wesley出版社,2003。

      [GoF设计模式]

      《设计模式:可复用面向对象软件的基础》,Gamma Erich、Richard Helm、Ralph Johnson、John M. Vlissides合著。——马萨诸塞州,里丁镇:Addison-Wesley出版社,1995。

      [Helander DMM]   Helander Mats:http://www.matshelander.com/wordpress/?p=30   

          [Helander肥领域模型]   Helander Mats:http://www.matshelander.com/wordpress/?p=75

      [Nilsson ADDDP]   

       《领域驱动设计和模式应用,Nilsson Jimmy著。——Addison-Wesley出版社,2006。

     英文原文:Aspects of Domain Model Management

    上传下源码 

  • 相关阅读:
    python运行错误---TabError: Inconsistent use of tabs and spaces in indentation
    python运行错误------Non-UTF-8 code
    opencv错误(Unhandled expection at at 0x0007EEE...)
    fatal error LNK1112: 模块计算机类型“X86”与目标计算机类型“x64”冲突——我的解决方案
    基础术语
    opencv
    图像归一化
    人脸相关数据库
    堆排序
    abp学习(二)
  • 原文地址:https://www.cnblogs.com/zhijianliutang/p/2369875.html
Copyright © 2011-2022 走看看