本章我们演示代码是如何“进化”的,实战的企业日常开发过程中,系统功能总伴随着业务的不断增加,早期简单的代码慢慢的越来越复杂,敏捷编程中的“禅”——简单设计、快速发布、获得反馈、快速开发的迭代循环过程,如何保证迭代过程持续交互合格的代码,代码重构和单元测试是非常重要的手段。单元测试用来保证重构的代码先满足原来的测试逻辑,代码结构优化满足新的业务需求扩展,然后开发增加新的功能、新的单元测试,每一轮迭代发布新的功能,获取用户反馈…
上一章功能完成的时候,你发现当前的admin.py已经较早期复杂了,里面包含了较多的功能逻辑代码。文件里有django admin设置需要增加的代码,也有业务逻辑“下达”、“任务分解”的业务功能代码。现场是时候重构和优化我们的代码结构了。把把业务相关的代码纳入到业务层(Biz)层去实现,然后确保功能不变,将来你会发现这一抽象和功能内聚极大的提高了代码的重用性和可扩展性。
1.1. 增加TaskBiz.py类
我们通过IDE环境增加对应的Task Model业务类文件TaskBiz.py,并把Task相关的业务逻辑封装到TaskBiz类下面。
TaskBiz类代码如下(代码会说话):
from .models import Task,Job class TaskBiz(object): """description of class""" def task_start(self,obj): """ 任务变更状态到 运行中 """ success=False if obj.State==Task.STATE_PROCESSED: obj.State=Task.STATE_RUNNING try: obj.save() success = True except Exception: success = False return success def task_decompose(self,request,obj): success=True try: job1=Job(**{"Task":obj,"TaskNum":obj.TaskNum,"OrderNo":1,"Source":obj.Source,"Target":'102',"Executor":"AGV01","State":Job.STATE_NEW, "Priority":obj.Priority,"Barcode":obj.Barcode,"User":request.user,}) job1.save() job2=Job(**{"Task":obj,"TaskNum":obj.TaskNum,"OrderNo":2,"Source":None,"Target":'1',"Executor":"ELEVATOR","State":Job.STATE_NEW, "Priority":obj.Priority,"Barcode":obj.Barcode,"User":request.user,}) job2.save() job3=Job(**{"Task":obj,"TaskNum":obj.TaskNum,"OrderNo":3,"Source":"102","Target":'104',"Executor":"AGV01","State":Job.STATE_NEW, "Priority":obj.Priority,"Barcode":obj.Barcode,"User":request.user,}) job3.save() job4=Job(**{"Task":obj,"TaskNum":obj.TaskNum,"OrderNo":4,"Source":'104',"Target":'103',"Executor":"AGV01","State":Job.STATE_NEW, "Priority":obj.Priority,"Barcode":obj.Barcode,"User":request.user,}) job4.save() job5=Job(**{"Task":obj,"TaskNum":obj.TaskNum,"OrderNo":5,"Source":'1',"Target":'5',"Executor":"ELEVATOR","State":Job.STATE_NEW, "Priority":obj.Priority,"Barcode":obj.Barcode,"User":request.user,}) job5.save() job6=Job(**{"Task":obj,"TaskNum":obj.TaskNum,"OrderNo":6,"Source":'504',"Target":'503',"Executor":"AGV05","State":Job.STATE_NEW, "Priority":obj.Priority,"Barcode":obj.Barcode,"User":request.user,}) job6.save() job7=Job(**{"Task":obj,"TaskNum":obj.TaskNum,"OrderNo":7,"Source":'5',"Target":None,"Executor":"ELEVATOR","State":Job.STATE_NEW, "Priority":obj.Priority,"Barcode":obj.Barcode,"User":request.user,}) job7.save() job8=Job(**{"Task":obj,"TaskNum":obj.TaskNum,"OrderNo":8,"Source":'503',"Target":'05-01-01',"Executor":"AGV05","State":Job.STATE_NEW, "Priority":obj.Priority,"Barcode":obj.Barcode,"User":request.user,}) job8.save() #子任务分解完成,并提交数据库 #更新任务的状态到“处理完成” obj.State=Task.STATE_PROCESSED obj.save() except Exception: success = False return success
同时,我们修改admin.py的task_decompose和task_start调用方式。
from .TaskBiz import TaskBiz #Task模型的管理器 class TaskAdmin(admin.ModelAdmin): ... @atomic def task_start_action(self, request, queryset): for obj in queryset: biz= TaskBiz() result=biz.task_start(obj) if result: self.message_user(request, str(obj.TaskNum) + " 下达成功.") else: self.message_user(request, str(obj.TaskNum) + " 下达失败.") task_start_action.short_description = '下达所选的' + ' 任务' @atomic def task_decompose_action(self, request, queryset): for obj in queryset: #只处理状态等于未处理的任务 if obj.State==Task.STATE_NEW: #result=self.task_decompose(request,obj) biz=TaskBiz() result=biz.task_decompose(request,obj) if result: self.message_user(request, str(obj.TaskNum) + " 处理成功.") else: self.message_user(request, str(obj.TaskNum) + " 处理成功.") task_decompose_action.short_description = '处理所选的' + ' 任务'
这样admin.py文件只专注于django admin相关设置和事件触发的转调用,不再包含Task业务相关的代码,TaskBiz类则内聚了所有于Task业务业务操作的功能代码,这样通过代码结构性调整,实现了面向对象里的“高内聚”原则,业务代码就不用全部散落参杂在admin.py文件里,尤其随着业务的推进admin.py文件只会越来越复杂。业务层的抽象让admin.py变得简单易读,更利于功能得扩展和维护。
1.2. 持续TaskBiz重构
为了让TaskBiz更专注于业务本身,你发现现在的task_decompose_action方法使用了界面段传过来的request参数,把参数修改成传user对象,同时修改admin.py调用的参数传入。
from .models import Task,Job class TaskBiz(object): """description of class""" def task_start(self,obj): success=False if obj.State==Task.STATE_PROCESSED: obj.State=Task.STATE_RUNNING try: obj.save() success = True except Exception: success = False return success def task_decompose(self,obj,user): success=True try: job1=Job(**{"Task":obj,"TaskNum":obj.TaskNum,"OrderNo":1,"Source":obj.Source,"Target":'102',"Executor":"AGV01","State":Job.STATE_NEW, "Priority":obj.Priority,"Barcode":obj.Barcode,"User":user,}) job1.save() job2=Job(**{"Task":obj,"TaskNum":obj.TaskNum,"OrderNo":2,"Source":None,"Target":'1',"Executor":"ELEVATOR","State":Job.STATE_NEW, "Priority":obj.Priority,"Barcode":obj.Barcode,"User":user,}) job2.save() job3=Job(**{"Task":obj,"TaskNum":obj.TaskNum,"OrderNo":3,"Source":"102","Target":'104',"Executor":"AGV01","State":Job.STATE_NEW, "Priority":obj.Priority,"Barcode":obj.Barcode,"User":user,}) job3.save() job4=Job(**{"Task":obj,"TaskNum":obj.TaskNum,"OrderNo":4,"Source":'104',"Target":'103',"Executor":"AGV01","State":Job.STATE_NEW, "Priority":obj.Priority,"Barcode":obj.Barcode,"User":user,}) job4.save() job5=Job(**{"Task":obj,"TaskNum":obj.TaskNum,"OrderNo":5,"Source":'1',"Target":'5',"Executor":"ELEVATOR","State":Job.STATE_NEW, "Priority":obj.Priority,"Barcode":obj.Barcode,"User":user,}) job5.save() job6=Job(**{"Task":obj,"TaskNum":obj.TaskNum,"OrderNo":6,"Source":'504',"Target":'503',"Executor":"AGV05","State":Job.STATE_NEW, "Priority":obj.Priority,"Barcode":obj.Barcode,"User":user,}) job6.save() job7=Job(**{"Task":obj,"TaskNum":obj.TaskNum,"OrderNo":7,"Source":'5',"Target":None,"Executor":"ELEVATOR","State":Job.STATE_NEW, "Priority":obj.Priority,"Barcode":obj.Barcode,"User":user,}) job7.save() job8=Job(**{"Task":obj,"TaskNum":obj.TaskNum,"OrderNo":8,"Source":'503',"Target":'05-01-01',"Executor":"AGV05","State":Job.STATE_NEW, "Priority":obj.Priority,"Barcode":obj.Barcode,"User":user,}) job8.save() #子任务分解完成,并提交数据库 #更新任务的状态到“处理完成” obj.State=Task.STATE_PROCESSED obj.save() except Exception: success = False return success
到这里我们得到了一个小步迭代的新版本代码,让TaskBiz更专注于Task业务本身,作为业务层更关注业务对象本身。最后,我们也需要重构一下admin.py里task_start_view的函数代码,已确保调用重构后的task_start函数。
from .TaskBiz import TaskBiz #Task模型的管理器 class TaskAdmin(admin.ModelAdmin): ... @atomic def task_start_view(self, request, *args, **kwargs): obj = get_object_or_404(Task, pk=kwargs['pk']) #self.task_start(obj) biz= TaskBiz() biz.task_start(obj) #raise Exception('模拟抛出异常!') #重新刷新列表界面 co_path = request.path.split('/') new_path=co_path[0:4] new_path='/'.join(new_path) request.path = new_path return redirect(new_path)
这样本章节的抽象出一个业务Biz层的逻辑分层就做完了,这里看似简单的实现代码在实战中,往往不容易做这样的抽象,尤其糟糕项目后期做这样的抽象基本上可能已是举步维艰了,唯一能做的就是“将就”,点上三柱香祈祷新加入的代码不要影响原来的功能(熊猫烧香是有群众基础的)。笔者早些年的编码经历里,这样的例子比比皆是。让你的作品(代码)有生命,他们能在功能演化过程中不走进死胡同么。从简设计、迭代、重构和单元测试,敏捷编程的核心原则,让笔者看到代码一片“光明”。
1.3. 小结
本章节我们演示了如何对业务逻辑层进行抽象,通过面向对象内聚的原则,构建一个专注于Task的业务操作的TaskBiz类(层),从而实现把分散在admin.py的业务逻辑封装到对象类里,实现面向过程编码逻辑到面向对象逻辑的抽象。未来的章节中我们会逐步体会到这个抽象带来的扩展和维护优势。下一章节我们通过增加单元测试来进一步阐明如何进行代码重构。