2017-02-02 39 views
-2

在我的項目中,我有一個場景,在一個控制器動作中,我需要做大量依賴的東西,不能直接移動到模型中。爲了保持控制器不變,我最終將該代碼移到幫助器中。這是一個很好的做法嗎?我應該在控制器還是助手中保留更多代碼?

例如,在一個控制器動作,我需要做第一驗證請求的校驗和,然後進行輸入在兩個表中,然後調用一些外部API和根據結果來更新一些的值。我最終將呼叫轉移到外部API給幫手。

回答

1

好習慣?當然不!它不僅會讓代碼維護變得一件糟糕的任務,而且還會使測試變得困難。

從未有其中的邏輯不能從一個控制器移動的場景。一般而言,對於任何面向對象的語言,項目應該基於MVC模式,至少將業務邏輯與演示分開。

根據您的例子,簡單的東西可以輕鬆地委派專人負責到專科班,從而清除控制器的邏輯。

class FooController < ApplicationController 
    def create 
    handler = FooHandler.new(params[:foos]) 
    foo = handler.process_foo 

    if foo[:result] 
     flash[:success] = 'Foo was successful' 
     redirect_to foo_path 
    else 
     flash[:error] = foo[:errors] 
     redirect_to foo_path 
    end 
    end 
end 

class FooHandler 
    delegate :valid_checksum?, to: :checksum_klass 
    delegate :create_foos, to: :foo_klass 
    delegate :call_foo_api, to: :foo_api_klass 

    def initialize(params) 
    @params = params 
    end 

    def process_foo 
    return {result: false, errors: 'failed checksum'} unless valiid_checksum? 
    return {result: false, errors: 'failed to create the foos' unless create_foos 
    return {result: false, errors: 'api errors'} unless call_foo_api 
    {result: true} 
    end 

    private 
    attr_accessor :params 

    def checksum_klass 
    @checksum_klass ||= ChecksumChecker.new(params[:checksum]) 
    end 

    def checksum_klass 
    @foo_klass ||= FooCreator.new(params[:foo_objects]) 
    end 

    def checksum_klass 
    @foo_api_klass ||= FooApiHandler.new(params[:foo_objects]) 
    end 
end 

實施上述風格是你如何可以開始每一道工序分解到自己的類,它通過單一FooHandler類運行,從控制器去耦所有的邏輯的例子。在這個例子中,所有的控制器都關心的是這個過程是否成功。

我的例子的工作原理是每個動作都包含在自己的類中,因此FooHandler可以在需要時將責任委託給該類,傳遞一些數據並清洗任何責任。它所關心的只是結果。

這一切歸結爲你對圖案的理解。我使用的兩個最有用的模式(除了Rubys鴨子打字和授權)是觀察者模式和裝飾者模式。

觀察者模式將允許你相依過程與可觀察類的狀態相關聯。因此,對於您的示例,如果API調用依賴於正在創建的對象,請嘗試設置將FooApiHandler作爲訂閱偵聽器的FooCreationHandler。如果處理程序成功創建記錄,則可以通知FooApiHandler可以調用外部API,從而將依賴關係解耦。

像裝飾者這樣的模式可以讓你在需要時用特定的行爲「裝飾」對象,而不是有一個大的,複雜的if語句和其他類的「知識」。再次,這是一個很好的模式,允許您創建具有抽象行爲的專業類,而不是包含一個類或控制器的邏輯。

希望這個簡短的例子有所幫助。

2

這是一個好的做法呢?

沒有。助手僅適用於視圖特定的內容。貨幣格式化,日期格式化,包裝東西在一個特定的<div>,那種東西。默認情況下,它們甚至在控制器中不可用。你必須明確地要求他們。

組織多個運動部件的合作不應該是幫手。是的,控制器也不是一個好地方。我,我通常把這種邏輯放在一個ServiceObject或其他東西。所以我的控制器通常是這樣的:

class ProjectsController < AppicationController 
    def create 
    # this will create the project, create associated objects, push notifications to 
    # whatever needs to be notified, etc. 
    ::ServiceObjects::Project::Create.new(project_params) 
    # render something 
    end 

    ... 
end 

這樣,你的控制器仍然很瘦。作爲額外的獎勵,服務對象更容易測試。

相關問題