代码改变世界

关于业务逻辑和对象行为的思考

2014-04-28 22:23  兜兜里没有药  阅读(1162)  评论(3编辑  收藏  举报

  为什么今天写这篇博客呢?
  主要是因为以前使用三层模式时,经常会出现一种问题,就是BLL业务逻辑层没有什么业务逻辑,经常被用作表现层和DAL数据层的过度,总感觉有跟没有都一样......,Model也只是用数据传输的载体,怎么就觉得跟OOP思想扯不上关系,在我理解的OOP思想里,对象应该是有生命力的才对啊!!

  后来学习了领域驱动设计,使用EntityFramework CodeFirst重量级ORM,在看资料的过程中,心理可美滋滋的了,应为我觉得这才是我心目中的OOP思想,领域对象有属性,有行为,有事件等等,很具有生命力;随着项目运用,慢慢开始偏离轨道了,如果人生就是一个茶几,我就是上面的杯具,为毛还是和三层时的一样,对象的生命力在哪里,((‵□′)).....

  作为一个想成为架构师的码农来说,不行,绝对不行,我必须反思,反省,反.......

  下面是经过左思右想的一点点成果,和大家分享下,希望能得到一些建议。

  首先先用以前的写法写一个非常简单下单处理,代码如下:

  对象模型:

  1.User用户模型

 1     public class User
 2         : EntityBase
 3     {
 4         public string UserName { get; set; }
 5 
 6         public string Email { get; set; }
 7 
 8         public string Telephone { get; set; }
 9 
10         public Account Account { get; set; }
11     }

  2.Account账户模型

1     public class Account
2         : EntityBase
3     {
4         public Guid UserId { get; set; }
5 
6         public double Amount { get; set; }
7 
8         public DateTime ActiveLastTime { get; set; }
9     }

  3.Product产品模型

 1     public class Product
 2         : EntityBase
 3     {
 4         public string Name { get; set; }
 5 
 6         public double Price { get; set; }
 7 
 8         public double Description { get; set; }
 9 
10         public int Number { get; set; }
11 
12         public bool IsPutaway { get; set; }
13     }

  4.Order订单模型

    public class Order
        : EntityBase
    {
        public string OrderNo { get; set; }

        public Guid ProductId { get; set; }

        public string ProductName { get; set; }

        public double Price { get; set; }

        public Guid UserId { get; set; }

        public DateTime CreateTime { get; set; }
    }

  生成订单的处理逻辑

 1     public class OrderAppService
 2     {
 3         public void Create(Order order)
 4         {
 5             if (order == null)
 6                 throw new ArgumentNullException("order");
 7 
 8             order.OrderNo = NewOrderNo();
 9             order.CreateTime = DateTime.Now;
10 
11             //进行创建订单
12         }
13 
14         private string NewOrderNo()
15         {
16             return "";
17         }
18     }

  表现层进行生成订单

 1         public JsonResult CreateOrder(Guid productId)
 2         {
 3             //这里数据获取就省略了哈...
 4             Web.Domain.UserModule.User user = null;
 5             Web.Domain.ProductModule.Product product = null;
 6 
 7             if (product.Number > 0)
 8                 return Json(new { success = false, error = string.Format("\"{0}\" 该产品库存不足,无法购买。", product.Name) });
 9 
10             if (!product.IsPutaway)
11                 return Json(new { success = false, error = string.Format("\"{0}\" 该产品还没有上架或者暂时下架,如有问题请咨询客服人员。", product.Name) });
12 
13             if (user.Account.Amount < product.Price)
14                 return Json(new { success = false, error = "您的金额不足,请先进行充值,再购买。" });
15 
16             Order order = new Order
17             {
18                 ProductId = product.Id,
19                 ProductName = product.Name,
20                 Price = product.Price,
21                 UserId = user.Id
22             };
23 
24             try
25             {
26                 Web.Application.OrderModule.OrderAppService service = new Web.Application.OrderModule.OrderAppService();
27                 service.Create(order);
28             }
29             catch
30             {
31                 return Json(new { success = false, error = "系统出错了,请稍后在进行购买。" });
32             }
33             return Json(new { success = true });
34         }

  OK,以前的写法就是这样,不知道有没有人和我写的差不多,(●'◡'●)....

  下面是我根据自己的思路做的一些改进,同时引进前面一篇《提升Boolean和out相结合的用户体验》中的Can状态对象。

  首先意识到的是表现层的CreateOrder下有很多订单处理的业务逻辑,应该放置在业务逻辑OrderAppService中,代码等下贴出。

  再观察业务逻辑中的3个if,我觉得应该以拟人的方式进行思考,如下,

  1.product.Number < 0 : 我们应该问产品:"你现在还有库存吗?" 产品做出回答:"没有!";产品做出的回答属于产品的一个行为,那我觉得应该给它定义一个行为:Can HasNumber();

  2.!product.IsPutaway : 问:"你现在什么状态我能购买吗?" 答:"下架中,不能购买!";虽然这里只有上下架的状态,后期可能会拓展,所有也给他定义一个行为:Can IsEnabled();

  3.个人觉得这两个if都是询问产品能不能买,可能以后还会有其他限制,所有我觉得应该让产品直接回答我们能不能购买,让它自己一次性检查:Can CanBuying();

  4.User.Account.Amount < product.Price:这个应该问用户:”我需要这么多的钱你有吗?“,用户说:”没有!“,Can HasMoney(double amount);

  下面来看看改造后的模型:

    public class Product
        : EntityBase
    {
        public string Name { get; set; }

        public double Price { get; set; }

        public double Description { get; set; }

        public int Number { get; set; }

        public bool IsPutaway { get; set; }

        /// <summary>
        /// 有没有库存
        /// </summary>
        /// <returns></returns>
        public Can HasNumber()
        {
            if (this.Number < 0)
                return string.Format("\"{0}\" 该产品库存不足,无法购买。", this.Name);

            return true;
        }

        /// <summary>
        /// 当前状态是否支持购买
        /// </summary>
        /// <returns></returns>
        public Can IsEnabled()
        {
            if (!this.IsPutaway)
                return string.Format("\"{0}\" 该产品还没有上架或者暂时下架,如有问题请咨询客服人员。", this.Name);

            return true;
        }

        /// <summary>
        /// 当前产品是否可以购买
        /// </summary>
        /// <returns></returns>
        public Can CanBuying()
        {
            var hasNumber = this.HasNumber();
            if (!hasNumber)
                return hasNumber;

            var isEnabled = this.IsEnabled();
            if (!isEnabled)
                return isEnabled;

            return true;
        }
    }


    public class User
        : EntityBase
    {
        public string UserName { get; set; }

        public string Email { get; set; }

        public string Telephone { get; set; }

        public Account Account { get; set; }

        /// <summary>
        /// 是否有指定额度的钱
        /// </summary>
        /// <param name="amount">指定额度的钱</param>
        public Can HasMoney(double amount)
        {
            if (this.Account == null)
                throw new MemberAccessException("账户未被实例化");

            return this.Account.HasMoney(amount);
        }
    }


    public class Account
        : EntityBase
    {
        public Guid UserId { get; set; }

        public double Amount { get; set; }

        public DateTime ActiveLastTime { get; set; }

        /// <summary>
        /// 是否有指定额度的钱
        /// </summary>
        /// <param name="amount">指定额度的钱</param>
        public Can HasMoney(double amount)
        {
            if (this.Amount < amount)
                return "您的金额不足,请先进行充值,再购买。";
            return true;
        }
    }
    public class Order
        : EntityBase
    {
        public Order(Guid productId, string productName, double price, Guid userId)
        {
            this.ProductId = productId;
            this.ProductName = productName;
            this.Price = price;
            this.UserId = userId;

            this.CopyToNewOrder();
        }

        public string OrderNo { get; private set; }

        public Guid ProductId { get; set; }

        public string ProductName { get; set; }

        public double Price { get; set; }

        public Guid UserId { get; set; }

        public DateTime CreateTime { get; private set; }

        /// <summary>
        /// 拷贝为新创建的订单
        /// </summary>
        public void CopyToNewOrder()
        {
            this.OrderNo = this.NewOrderNo();
            this.CreateTime = DateTime.Now;
        }

        private string NewOrderNo()
        {
            return "";
        }
    }

  然后我觉得应该学习下CQRS查询与命令分离的思路,为业务逻辑创建判断逻辑的统一存储如下:

 1     public static class OrderAppLogic
 2     {
 3         /// <summary>
 4         /// 是否可以创建订单
 5         /// </summary>
 6         /// <param name="user">用户</param>
 7         /// <param name="product">产品</param>
 8         /// <returns></returns>
 9         public static Can CanCreateOrder(User user, Product product)
10         {
11             Can canCreateOrder = true;
12 
13             if(!(canCreateOrder = product.CanBuying()))
14                 return canCreateOrder;
15 
16             if(!(canCreateOrder = user.HasMoney(product.Price)))
17                 return canCreateOrder;
18 
19             return canCreateOrder;
20         }
21     }

  OrderAppService 就挺简单的了

 1         public Can Create(User user, Product product)
 2         {
 3             if (user == null)
 4                 throw new ArgumentNullException("user");
 5 
 6             if (product == null)
 7                 throw new ArgumentNullException("product");
 8 
 9             Can flag = true;
10 
11             if (!(flag = OrderAppLogic.CanCreateOrder(user, product)))
12                 return flag;
13 
14             Order order = new Order(product.Id, product.Name, product.Price, user.Id);
15 
16             //进行创建
17 
18             return flag;
19         }

  表现层代码就更简单了

 1         public JsonResult CreateOrder(Guid productId)
 2         {
 3             //这里数据获取就省略了哈...
 4             Web.Domain.UserModule.User user = null;
 5             Web.Domain.ProductModule.Product product = null;
 6 
 7             try
 8             {
 9                 Web.Application.OrderModule.OrderAppService service = new Web.Application.OrderModule.OrderAppService();
10                 var result = service.Create(user, product);
11                 return Json(new { success = (bool)result, error = result.Error });
12             }
13             catch
14             {
15                 return Json(new { success = false, error = "系统出错了,请稍后在进行购买。" });
16             }
17         }

  目录结构:

  

  好了,终于写完了,大家要是如果有什么建议和意见,欢迎积极评论!如果您觉得还不错,帮忙点击下推荐呗!!