Python开发入门与实战12-业务逻辑层

12. Biz业务层

前面的章节我们把大量的业务函数都放在了views.py里,按照目前这一的写法,当我们编写的系统复杂较高时,我们的views.py将会越来越复杂,大量的业务函数包含其中使其成为一个包罗万象的文件。本章我们将阐述增加一个业务逻辑层来解决view层的复杂度,相当于在model层和view层中增加一个业务逻辑业务层Biz层,接下来我们根据这个思路来重构我们前面章节的代码。

12.1. 增加inventoryBiz类文件

在inventory app添加一个新的inventoryBiz类文件,然后我们把views.里的updatingInventoryIn函数迁移到新的类文件里面来,代码如下:

from inventory.models import *

class InventoryBiz(object):
    #注意这里类函数要增加一个Self参数
    def updatingInventoryIn(self,inStockBill,inventory):
        if (inventory.InventoryId == None):
            inventory.Item = inStockBill.Item
            inventory.Amount = 0

        inventory.Amount = inventory.Amount + inStockBill.Amount

这里注意:我们手动增加的文件编码格式是ANSI的我们需要用notepad打开inventoryBiz.py文件另存为修改该文件的编码格式,否则中文注释会导致python运行环境报错。(updatingInventoryIn采用首字母小写的Camel命名模式,后面我们的方法名都调整为这一命名习惯)

我们调整一下测试代码来满足这次代码重构:

from inventory.InventoryBiz import

 InventoryBiz

...

    #如何构建更新库存的函数,让其可具备测试调用,

    #当前我们把函数放在views.py迁移到InventoryBiz.py里

     biz 

=

 InventoryBiz()

    biz.updatingInventoryIn(inStockBill,inventory)

    #校验测试是否满足当前场景

     self.assertEqual(inventory.Amount ,20)

    inventory = Inventory() #当前没有库存数据,我们创建对象属性都没有赋值

     biz.updatingInventoryIn(inStockBill,inventory)

    self.assertEqual(inventory.Amount ,10)

    self.assertEqual(inventory.Item.ItemId ,inStockBill.Item.ItemId)

Run单元测试,测试通过。这里我们就会发现单元测试对于代码重构来说是多么的重要,代码结构的改变如果影响到原来的测试业务,简单调整一下,然后通过回归测试很快就知道这次调整是否满足原来的设计要求,所以说单元测试对于系统的代码重构来说简直就是一个利器。

接下来,我们重构和调整一下AddInStockBill函数就完成了本次代码调整工作,代码如下:

@transaction.commit_on_success

def AddInStockBill(request):
    if request.method == ‘POST‘:
        form = InStockBillForm(request.POST)
        if form.is_valid():
            cd = form.cleaned_data
            inStockBill = InStockBill()
            inStockBill.InStockBillCode = cd[‘InStockBillCode‘]
            inStockBill.InStockDate = cd[‘InStockDate‘]
            inStockBill.Amount = cd[‘Amount‘]
            inStockBill.Operator = cd[‘Operator‘]
            inStockBill.Item = cd[‘Item‘]
            inventorys = inStockBill.Item.inventory_set.all()
            if (inventorys.count()==0):
                currentInventory = Inventory()
            else:
                currentInventory = inventorys[0]

            biz = InventoryBiz()
            biz.updatingInventoryIn(inStockBill,currentInventory)
            currentInventory.save() #更新库存
              inStockBill.save() #保存入库单数据

              return HttpResponseRedirect(‘/success/‘)

        else:

            form = InStockBillForm()
            return render_to_response(‘InStockAdd.html‘,{‘form‘: form}
                                  ,context_instance = RequestContext(request))

12.2. 继续重构inventoryBiz类

依据高内聚、低耦合的对象设计思路,我们继续重构代码,把AddInStockBill函数中获取当前物料的库存数据的代码迁移到inventoryBiz类中。

def getInventoryByItem(self,item) :
        if (item != None):
            inventorys = item.inventory_set.all()
            if (inventorys.count()==0):
                currentInventory = Inventory()
            else:
                currentInventory = inventorys[0]

        return currentInventory

最后我们的AddInStockBill变成了只负责把客户端传来的数据进行解码,然后委托调用相应biz业务层的方法,进行业务处理,最后把数据持久化到数据库中。自己不再包含业务逻辑代码,这种增加对象内聚的代码重构,便于以后我们快速定位bug和调整业务时修改代码,而不是在复杂的views.py文件中到处去找业务代码,这个也是面向对象编程的核心思想之一。

@transaction.commit_on_success
def AddInStockBill(request):
    if request.method == ‘POST‘:
        form = InStockBillForm(request.POST)
        if form.is_valid():
            cd = form.cleaned_data
            inStockBill = InStockBill()
            inStockBill.InStockBillCode = cd[‘InStockBillCode‘]
            inStockBill.InStockDate = cd[‘InStockDate‘]
            inStockBill.Amount = cd[‘Amount‘]
            inStockBill.Operator = cd[‘Operator‘]
            inStockBill.Item = cd[‘Item‘]

            biz = InventoryBiz()
            #获得入库单物料库存
              currentInventory = biz.getInventoryByItem(inStockBill.Item)
            biz.updatingInventoryIn(inStockBill,currentInventory) #更新库存数量
            currentInventory.save() #

提交更新后的库存数据

              inStockBill.save() 

#保存入库单数据

            return HttpResponseRedirect(‘/success/‘)
    else:
        form = InStockBillForm()
        return render_to_response(‘InStockAdd.html‘,{‘form‘: form}
                              ,context_instance = RequestContext(request))

12.3. 代码的持续改进

根据敏捷开发提倡的“敏捷团队注重简易,这样做可以消除那些没必要的复杂,只需专注于开发当前所需要的功能和最简单的设计。如果能使用简单的方法来帮助一个敏捷团队马上开发出需要的软件,而不浪费人力和资源,这就是他们给那些投资的用户以最好和最直接利益的方法。”

前面的AddInStockBill是满足当前需要的,所以根据简易原则,当前在views.py里直接调用库存的保存方法是满足要求的。可是如果接下来我们的需求增加或者变更,如:需要在入库单Model数据提交前做model的合法性验证,那么我该如何来编写我们的代码呢?最简单的写法就是在AddInStockBill函数的inStockBill.save()语句前增加业务判断,在model合法后再调用该保存语句。

笔者注:这里的验证可以理解是服务端的验证,与django Form表单的前端验证分开来看,这样未来我们才能把服务端与页面端的绑定分离,从而支持一个服务接口可以供页面调用也可以供其它客户端调用,如:android等。

if not inStockBill.InStockBillCode:

    validMsg = validMsg + ‘入库单编号不能为空!‘
    …
    if validMsg == ‘‘:
        inStockBill.save()

当这样的需求出现时,为了提高系统的内聚性和解少views.py的复杂性,我们增加一个InStockBillBiz来专门负责关于InStockBill模型的相关功能代码。在InStockBillBiz里我们增加一个save函数用来处理InStockBill model的保存,代码如下:

from inventory.models import *
class InStockBillBiz(object):
    """description of class"""
    def save(self,inStockBill):
        try:
            if not inStockBill.InStockBillCode:
                validMsg = ‘入库单编号不能为空!‘
            if not inStockBill.InStockDate:
                validMsg =   validMsg + ‘入库单时间不能为空!‘
            if validMsg != ‘‘:
                raise "ValidationException", validMsg
            inStockBill.save()
        except "ValidationException", arg:
            raise "ValidationException", arg
        except Exception, e:
            raise Exception(e)

如果model复杂,需要做的数据校验比较多,代码进一步重构改进如下,编写一个专门处理数据校验的函数:

from inventory.models import *
class InStockBillBiz(object):
    """description of class"""
    def save(self,inStockBill):
        try:

            validMsg = self.validBeforeSave(inStockBill)
            if validMsg != ‘‘:
                raise "ValidationException", validMsg
            inStockBill.save()
        except "ValidationException", arg:
            raise "ValidationException", arg
        except Exception, e:
            raise Exception(e)

    def validBeforeSave(self, inStockBill):
        validMsg = ‘‘
        if  not inStockBill:
            validMsg = validMsg + ‘入库单编号不能为空!‘
        if not inStockBill.InStockDate:
                validMsg =   validMsg + ‘入库单时间不能为空!‘

        return validMsg

代码这样重构的好处就是以后定位Bug以及增加数据校验的机制或者规则会方便很多,代码的可读性大大提高,这就是面向对象编程中高内聚的原则,单一对象尽量少的职责,只专注于自己的事情,而系统功能的形成更多的通过对象的组合模式来实现。对于函数来说也是如此,一个函数的功能尽快可能单一,处理的事情尽量少,后面通过委托调用来组合,满足功能需求。

敏捷编程的一个大前提就是我们的复杂的功能代码是通过渐进的模式来实现的,如果简单的模式能够满足需求,我们就采用简单的方式实现,当简单的代码编写不能满足需求了,我们就重构我们的代码,通过内聚的方法,增加新的类来专门负责某一类功能代码。

最后,我们的views. AddInStockBill就变成了如下这样:

@transaction.commit_on_success
def AddInStockBill(request):
    if request.method == ‘POST‘:
        form = InStockBillForm(request.POST)
        if form.is_valid():
            cd = form.cleaned_data
            inStockBill = InStockBill()
            inStockBill.InStockBillCode = cd[‘InStockBillCode‘]
            inStockBill.InStockDate = cd[‘InStockDate‘]
            inStockBill.Amount = cd[‘Amount‘]
            inStockBill.Operator = cd[‘Operator‘]
            inStockBill.Item = cd[‘Item‘]

            biz = InventoryBiz()
            #获得入库单物料库存
              currentInventory = biz.getInventoryByItem(inStockBill.Item)
            biz.updatingInventoryIn(inStockBill,currentInventory)
            currentInventory.save() #更新库存

              billBiz = InStockBillBiz()
            billBiz.save(inStockBill)#保存入库单数据

            return HttpResponseRedirect(‘/success/‘)
    else:
        form = InStockBillForm()
        return render_to_response(‘InStockAdd.html‘,{‘form‘: form}
                              ,context_instance = RequestContext(request))

12.4. 小结

业务逻辑层的增加让我们的项目完整的体现了视图、模型和控制器(业务逻辑层)三层架构模式,后面的章节我们会逐步说到这一代码结构变化带来的好处。Biz层代码只负责处理系统的业务逻辑,实现了类设计的高内聚”要求,同时,这样的代码结构也为我们编写业务逻辑单元测试提供了好的架构体系,单元测试主要检验业务逻辑是否满足系统设计要求。

下一章节我们将描述MVC架构如何快速的实现views层的替换,或者说系统同时支持不同的视图层。

时间: 2024-11-05 13:35:03

Python开发入门与实战12-业务逻辑层的相关文章

Python开发入门与实战14-基于Extjs的界面

14. 基于Extjs的界面 上一章我们实现了一个原生的html例子,本章我们将采用Extjs实现界面的展现,来说明MVC模式下我们是怎么考虑界面与业务层的关系的. 14.1. 引用Extjs目录 首先,我们在inventory app下增加一个static目录,拷贝Extjs发布目录到static下,本章节例子我们采用的是Extjs 4.1.1版本进行说明演示,Django项目能够访问static目录我们需要修改项目setting.py的STATIC_ROOT项的值,项目才能正确装载引用的静态

Python开发入门与实战22-简单消息回复

22. 简单消息回复 本章节我们来实现一个微信库存查询功能,使用我们前面的BIZ业务逻辑层示例如何利用微信入口来实现文本消息类的库存查询服务. 22.1. 在responseMsg函数里增加处理微信文本推送的处理逻辑 def responseMsg(postContent): postStr = smart_str(postContent) #postStr = postContent if postStr: msg = xmlContent2Dic(postStr) if msg['MsgTy

Python开发入门与实战10-事务

1. 事务 本章我们将通过一个例子来简要的说明“事务”,这个开发实战里经常遇到的名词.事务是如何体现在一个具体的业务和系统的实现里. 事务是通过将一组相关操作组合为一个,要么全部成功要么全部失败的单元,可以简化错误恢复并使应用程序更加可靠.事务具有4个特性:原子性.一致性.隔离性.持久性.业务事务就是完成具体业务操作后,形成的业务结果:数据库事务是数据库产品根据事务的特性实现的相关功能,数据库事务(Database Transaction) ,是指作为单个逻辑工作单元执行的一系列操作,要么完全地

Python开发入门与实战1-开发环境

1.搭建Python Django开发环境 1.1.Python运行环境安装 Python官网:http://www.python.org/ Python最新源码,二进制文档,新闻资讯等可以在Python的官网查看到. Python3.0已经发布,本文我们使用Django作为对象映射层,Django暂时还不支持3.0版本,本文我们以Python 2.7 Windows 8 64位版本为例.下载安Windows X86-64 MSI Installer (2.7.7) [1]安装包,运行安装文件.

Python开发入门与实战11-单元测试

1. 单元测试 本章节我们来讲讲django工程中如何实现单元测试,单元测试如何编写以及在可持续项目中单元测试的重要性. 下面是单元测试的定义: 单元测试是开发者编写的一小段代码,用于检验被测代码的一个很小的.很明确的功能是否正确. 1. 它是一种验证行为 程序中的每一项功能都是测试来验证它的正确性.它为以后的开发提供支援.就算是开发后期,我们也可以轻松的增加功能或更改程序结构,而不用担心这个过程中会破坏重要的东西,它为代码的重构提供了保障.这样,我们就可以更自由的对程序进行改进. 2. 它是一

Python开发入门与实战8-基于Java的集成开发环境

8. 基于Java的Python的集成开发环境 目前为止我们所有的代码和例子都是通过Notepad文本编辑器来实现的,实际项目开发中这种编码模式效率较低(大虾除外),使用IDE集成开发环境常常大幅度的提高编码效率.本章我们将简要介绍两个主流的集成开发环境. 8.1. 下载安装Java运行环境 http://www.java.com/zh_CN/download/manual.jsp 根据操作系统版本下载安装Java运行时环境,如下图: 8.2.Eclipse IDE http://www.ecl

Python开发入门与实战6-表单

6. 表单 从简朴的单个搜索框,到常见的Blog评论提交表单,再到复杂的自定义数据输入接口,HTML表单一直是交互性网站的重要交互手段.本章介绍如何用Django如何对用户通过表单提交的数据进行访问.有效性检查以及其它处理等. 首先,我们先简要介绍一下HttpRequest对象和Form对象. 6.1. 提交的数据信息 除了基本的元数据,HttpRequest对象有两个属性包含了用户所提交的信息: request.GET 和 request.POST.二者都是类字典对象,我们可以通过它们来访问G

Python开发入门与实战2-第一个Django项目

2.第一个Django项目 上一章节我们完成了python,django和数据库等运行环境的安装,现在我们来创建第一个django project吧,迈出使用django开发应用的第一步. 2.1.创建第一个Django项目 我们创建一个我们存放Django的工作目录,示例:C:\My Files\Python Projects 在命令提示符窗口进入到刚才创建的目录,运行运行命令: django-admin.py startproject mysite 这样会在你的当前目录下创建一个目录mysi

Python开发入门与实战19-Windows Azure部署2

19. 微软云部署2 上一章节我们介绍了如何实现在微软云通过虚拟机部署我们的在python django应用,本章我们来介绍如何Windows Azure上部署通过部署网站的方式来部署我们的应用,这种部署方式更方便,与vs 2013的集成度更高. 19.1. 创建Web 应用 我们登陆Windows Azure中国账户后进入到管理门户,选择“web 应用”,点击新建按钮,如下图: 创建完成后,我们选择myazure进度到应用的管理界面,如下图: 点击“浏览”按钮,浏览该网站,如果网站创建成功浏览