使用表达式树,让访问者直接执行“角色”对象的方法

本文介绍了一种使用表达式树改进角色扮演设计模式的方法。通过定义角色接口和访问者对象接口,实现了访问者对角色方法的优雅调用。该方法允许访问者在不暴露角色对象的情况下执行角色方法。

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

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开源技术团队,做最好最轻最快的框架!



    本文转自深蓝医生博客园博客,原文链接:http://www.cnblogs.com/bluedoctor/archive/2013/05/22/3092521.html,如需转载请自行联系原作者



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值