• MVC模式中如何区分应用程序逻辑(Controller层)和业务逻辑(Model层)?


    现在的大部分框架都是 MVC 模式,但 MVC 三个部分怎么配合,这里做了一点总结:
    基本原则:业务逻辑代码应该写在 M 里面,而应用程序逻辑应该写在 C 里面。V 只是单纯的展示数据。
    举个简单例子吧:用户往购物车添加一个商品
    用户点击商品的“添加到购物车”按钮,引起一次请求。服务器开始处理该请求,过程:
    1、检查当前用户是否有权限(比如是否已经登录、用户帐户状态、是否可以购物等)
    2、检查要添加的商品ID是否有效、
    3、检查要添加的商品库存是否足够
    4、将商品加入购物车,并保存购物车状态
    5、反馈信息
    在上述流程中:
    1: 是应用程序逻辑(一般由框架实现):因为和“添加商品到购物车”这个业务没有直接关系
    2: 业务逻辑:不能购买不存在的商品,这是业务进行的基本条件
    3: 业务逻辑:商品库存决定了是否可以购买此商品,这是业务进行的基本条件
    4: 业务逻辑
    5: 应用程序逻辑
    用代码表示的,可能像下面这样:
    // Cart控制器
    class Controller_Cart
    {
        function actionAddGoods()
        {
            $goods_id = (int)$_GET['goods_id'];
            Cart::instance()->add($goods_id)->save();
            
            echo '添加成功';
        }
    }


    // Cart 模型
    class Cart
    {
        /**
         * 购物车中的所有项目
         */
        public $items = array();
       
        /**
         * 单子模式,返回购物车对象的唯一实例
         */
        static function instance()
        {
            ...
        }
       
        function add($goods_id, $quantity = 1)
        {
            $goods = Goods::find($goods_id)->get();
            // 检查 id 和库存数
            if ($goods->id && $quantity > $goods->remaining)
            {
                // 添加商品到购物车
                $this->items[] = array($goods, $quantity);
            }
            else
            {
                throw new CartExecption('无效的商品 ID');
            }
            return $this;
        }
    }

    这个代码不完整,但是演示了最重要的部分,就是应用程序逻辑和业务逻辑的分离。
    如果这个流程走下去,用户要结算了,那么代码如下:
    class Controller_Cart
    {
        function actionCheckOut()
        {
            Cart::instance()->checkout();
            
            echo '成功';
        }
    }
    class Cart
    {
        function checkout()
        {
            // 开启一个数据库事务
            ....
            
            try
            {
                // 创建一个新的订单对象
                // $this->owner 是当前购物车的所有者(用户)
                $order = new Order($this->owner);
                
                // 将购物车中的所有商品添加到订单中
                foreach ($this->items as $item)
                {
                    list($goods, $quantity) = $item;
                    $order->add($goods, $quantity);
                }
                // 保存订单
                $order->save();
                
                // 清空购物车
                $this->items = array();
            }
            catch (Exception $ex)
            {
                // 出错了,回滚事务
                ....
                   
                // 再重新抛出异常
                throw $ex;
            }
            
            // 返回新建的订单
            return $order;
        }
    }


    class Order extends Model
    {
        public $items;
       
        function add($goods, $quantity)
        {
            $this->items[] = array($goods, $quantity);
            return $this;
        }
       
        function save()
        {
            foreach ($this->items as $item)
            {
                list($goods, $quantity) = $item;
                // 保存订单时,减少订单中每一个商品的库存数
                $goods->decrRemaining($quantity);
            }
            
            // 调用父类的保存
            parent::save();
            
            return $this;
        }
    }

    结算的代码很容易理解:
    1、调用购物车的 checkout() 方法
    2、开启数据库事务,这样当保存订单失败时(例如库存数不够)则回滚,确保数据库内容没有受影响
    3、将购物车中的所有商品添加到订单
    4、调用订单对象的 save() 方法
      4.1、遍历订单的所有项目,减少商品的库存(如果此时失败,商品的 decrRemaining() 方法会抛出异常)
      4.2、调用模型父类的 save() 方法
    5、清空购物车,返回新建的订单对象


    整个流程我们假定创建订单就等同于客户确认订单,此时减少库存。也有可能是后台确认订单配货后才减少库存,这和卖家的经营策略有关。

    这两个例子里面,业务逻辑都在模型中实现,控制器(也就是封装应用程序逻辑的层)仅仅完成处理输入数据、调用业务方法、反馈结果等任务。

  • 相关阅读:
    使用T4模板生成POCO类
    MiniProfiler工具介绍
    程序集和反射(C#)
    按自己的想法去理解事件和泛型(C#)
    WebAPI性能优化之压缩解压
    那些年困扰我们的委托(C#)
    HTML5笔记2——HTML5音/视频标签详解
    HTML5笔记1——HTML5的发展史及标签的改变
    工作中常用的js、jquery自定义扩展函数代码片段
    记一次.NET代码重构
  • 原文地址:https://www.cnblogs.com/yeagen/p/2700950.html
Copyright © 2020-2023  润新知