­

python工業互聯網應用實戰7—業務層

  本章我們演示程式碼是如何「進化」的,實戰的企業日常開發過程中,系統功能總伴隨著業務的不斷增加,早期簡單的程式碼慢慢的越來越複雜,敏捷編程中的「禪」——簡單設計、快速發布、獲得回饋、快速開發的迭代循環過程,如何保證迭代過程持續交互合格的程式碼,程式碼重構和單元測試是非常重要的手段。單元測試用來保證重構的程式碼先滿足原來的測試邏輯,程式碼結構優化滿足新的業務需求擴展,然後開發增加新的功能、新的單元測試,每一輪迭代發布新的功能,獲取用戶回饋…

  上一章功能完成的時候,你發現當前的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的業務邏輯封裝到對象類里,實現面向過程編碼邏輯到面向對象邏輯的抽象。未來的章節中我們會逐步體會到這個抽象帶來的擴展和維護優勢。下一章節我們通過增加單元測試來進一步闡明如何進行程式碼重構。