zoukankan      html  css  js  c++  java
  • 使用表达式树,让访问者直接执行“角色”对象的方法

    以前,我们在讨论《业务分析三维度(场景+角色+时间)理论》 的软件设计的时候,对于场景中的访问者,动态附加场景许可的角色,如何通过访问者执行角色方法的问题,采用了下面的实现方式:

    Actor.ActAs<IRole>().Function(Para para);

    这种方式本质上是将Actor转换成为了IRole接口的实例对象,然后进行方法访问的,但这样就暴露了角色对象,比如可以这样继续使用:

    IRole role=Actor.ActAs<IRole>();
    role.Fun1();
    role.Fun2("abc");

    这样看起来的话,role 跟Actor 是2个对象了,总觉得有点割裂。
    今天,我们使用表达式树,来实现一个更优美的方案。

    首先定义角色对象和访问者对象的接口:

     public interface IActor
        {
            string Name { get; }
        }
        public interface IRole
        {
            IActor Actor { get; set; }
        }

    然后定义一个动物角色接口,它拥有走路和吃东西的本能方法;

     public interface IAnimal:IRole
        {
            void Move();
            int Eat(string food);
            
        }

    接着实现一个动物角色类:

     public class Animal : IAnimal
        {
            public IActor Actor { get; set; }
    
            #region IAnimal 成员
    
            public void Move()
            {
                Console.WriteLine("{0} move...",Actor.Name);
            }
    
            public int Eat(string food)
            {
                Console.WriteLine("{0} eat.{1}.", Actor.Name, food);
                return 1;
            }
    
            #endregion
        }

    在实际的角色对象中,它是可以访问“访问者”的方法的,比如这里的ActorName

    下面,是我们的重点,Actor 访问者类的实现:

    class Actor:IActor
        {
            private List<IRole> roles = new List<IRole>();
    
            public string Name { get; private set; }
    
            public Actor(string name)
            {
                this.Name = name;
            }
    
            public void AddRole(IRole role)
            {
                roles.Add(role);
                role.Actor = this;
            }
    
            public TResule ActAs<T, TResule>(Expression<Func<T, TResule>> exp) where T : class
            {
                //在执行前做一些事情
                var lambda = exp.Compile();
                TResule result = lambda.Invoke(GetObject<T>());
                //在执行后做一些事情
                return result;
            }
    
            public T GetObject<T>() where T : class
            {
                foreach (IRole role in roles)
                {
                    if (role is T)
                        return role as T;
                }
                return null;
            }
        }

    我们重点来看ActAs 方法,它将把自己转换成指定的角色,然后调用角色的方法,但是参数是 Expression<Func<T, TResule>> ,这就允许我们以非常友好的方式来编码了,还是看看怎么调用这个代码:

               Actor man = new Actor("zhagnsan");
                man.AddRole(new Animal());//为张三添加动物的本能职责
    
                var result=man.ActAs<IAnimal, int>(a => a.Eat("rice"));
    
                Console.WriteLine("result:{0}",result);

    我们在方法里面,用熟悉的方式,调用了动物角色的吃东西方法。

    这里是程序输出:

    zhagnsan eat.rice.
    result:1

    相比较文章开头的方式, 这里man.ActAs<IAnimal, int> 直接执行了角色对象的方法,而不给外部人员知晓zhangsan 拥有某个角色对象实例的机会,这样就完成了访问者对于自己角色更好的“封装”。也就是,只有自己才可以执行自己角色的方法,这才是符合真实场景的设计

    -----------分界线------------------------

    欢迎加入PDF.NET开源技术团队,做最好最轻最快的框架!

  • 相关阅读:
    计算机入门知识
    iOS学习之-开机引导图
    学习笔记之09-空指针和野指针
    学习笔记之08-self关键字
    学习笔记之07-自定义构造方法和description方法
    学习笔记之06-点语法
    学习笔记之05-第一个OC的类
    学习笔记之04-第一个OC程序解析
    学习笔记之03-第一个OC程序
    hdoj1016 [dfs]
  • 原文地址:https://www.cnblogs.com/bluedoctor/p/3092521.html
Copyright © 2011-2022 走看看