java - 在MVC開發模式中,封裝物件應該放在Controller還是Service? Controller與Service該如何互動?
伊谢尔伦
伊谢尔伦 2017-05-16 17:05:12
0
8
1471

已經學了兩年的MVC了,但是有些概念還是很模糊,希望能在這裡找到答案。

我曾經看到有人說Controller不負責資料處理,全部交給我們的Service來處理,網頁前端傳回來是什麼資料類型,就直接把資料類型轉送到Service,然後由Service來處理;但又有另一個聲音說有時候會同時呼叫多個Service,如果在Controller就將物件封裝好了,就免於在Service的方法中多次封裝。

另外還有一個問題就是關於Controller與Service互動的問題。
我們為了前端的客戶互動良好,往往會透過Controller向前端傳回一些錯誤提示,例如使用者名稱已存在,使用者名稱和密碼不符等等。可是處理業務邏輯我們是放在Service層,那麼如果把一個login(String username,String password)方法的回傳值設定為boolean就無法回傳多種錯誤,但是如果回傳String類型,就需要設定一些基本的字典。
我自己“奇思妙想”,我在Service中通過拋出我自定義的一些RuntimeException,然後在Controller中通過TryCatch來處理不同的錯誤,但是我自己認為這種拋出異常的方式不妥。最近就陷入了迷茫,馬上就要開始做下一個專案了。希望各位能幫我解答一下迷惑。

感謝。

伊谢尔伦
伊谢尔伦

小伙看你根骨奇佳,潜力无限,来学PHP伐。

全部回覆(8)
phpcn_u1582

我們專案裡分了三層:

  1. 表現層:Spring MVC,負責接收Http請求、展現(回)結果、簡單校驗

  2. App層:提供應用程式層的功能,例如匯入、匯出、複雜校驗

  3. Domain層:處理業務邏輯,例如一些Service

呼叫順序是單項的:Controller->App->Domain

且感知關係為:Controller->App->Domain,即下層不感知上層

先回答你第一個問題:

你說的第一個問題我是否可以理解為,Http請求過來的參數不是Object,而是一堆基本型,但你的Service接收的參數是Object。正確的做法應該是,在Controller將「生」參數轉換成Object對象,然後呼叫Service。

為何這樣是正確的?因為Service屬於Domain層,裡面是業務邏輯,其接受的參數應該根據自己的需求而進行設計,不應該考慮Web層過來的參數是什麼,這樣才可以做到在不同場景下重複使用。

舉個例子,你的Service應該可以重複使用不同表現層環境下:

  1. 在一個Web程式中,使用者傳過來的參數是POST/GET形式給你的

  2. 在一個Web service程式中,使用者傳過來的參數是json或SOAP

  3. 在一個Swing程式中,使用者傳過來的參數就是字串

如果你將Service和具體某個表現層環境綁定,那麼其方法參數肯定不穩定,結果就導致無法重複使用。

同理,Service的回傳值也不應該和特定場景綁定。

在Spring MVC層面,Controller可以很方便的把參數轉換成Object,相關文件

第二個問題:

這個問題可以分為三個:

1)簡單的校驗例如參數長度限制、非空判斷等在哪裡做?

簡單校驗利用Spring MVC的自身提供的機製做,相關文檔,相關文檔

2)和Service本身的業務邏輯平行的校驗在哪裡做,例如用戶下單時判斷其是否帳號被禁用

我傾向於將這些邏輯校驗放在App層做,Controller調用App,App調用兩個不同的Service,將業務編織起來

3)和Service本身有關的業務邏輯校驗怎麼做

你舉的是登入的例子,用異常告知呼叫方(Controller)處理結果沒有任何問題。你也可以豐富Service的回傳值達到這個目的,不過需要注意的是,Service的回傳值的設計不能和表現層環境綁定,否則就不能復用了,這也就是為什麼@YaTou 提到了apache-shiro採用的是異常機制處理認證失敗,因為只有這樣才夠通用。

某草草

我覺得Controller不负责处理数据是正确的, 因为在spring-mvcController是不能复用的, 但是如果你把业务逻辑抽象成Service, 那么这个Service就是可以重複使用的.

至於你說的"在Controller就將物件封裝好了,就免於在Service的方法中多次封裝" , 沒太明白什麼意思, 你每個Service需要什么参数, Controller就給什麼參數, 至於需要的參數是否需要封裝成對象就可以自己權衡了.

你所說的login(String username,String password), 你想抽象成Service用异常处理处理多种不同的结果, 这个我觉得完全没有问题, 而且我觉得非常好啊, 很多认证框架都用的这种方式, 至少我看的apache-shiro就是用異常處理認證失敗的不同情況的.

曾经蜡笔没有小新

第一個問題感覺沒有標準答案,具體情況具體分析,邏輯分層清晰易於維護就好。

第二個問題的話,你這裡給出的交互是隸屬於權限控制的,一般用filter、aop、代理、反射等等方式實現代碼收束都可以,異常也在這些集中控制的代碼里扔一次就好,直接在Controller裡硬編碼我反而覺得累贅。 Service這一層更多的是呼叫Dao層的方法來實現一些複雜的涉及多表的業務邏輯處理,事務也放在這一層(當然現在框架把這事兒都乾了),所以Service這一層一般不丟異常(參數驗證在Controller以及之前的層次都做掉了因此不出現業務相關異常,而Dao把資料庫相關的底層異常屏蔽了)。

當然這是個人看法,沒有定式,還是那句話,分層清晰易於維護就好。

滿天的星座

1、Controller預設是單例的,但可用@Scope(value = "prototype")替換

2、登入可以回傳int啊,自己加個枚舉

3、規定是在Service層處理邏輯,要看業務的吧,程式碼冗餘度低些好,也好優化

大家觀點會不同,做開發更多的還是優化改,降低冗餘度,而不是必須怎麼做。 。 。

伊谢尔伦

1,Controller應盡可能的不設計業務邏輯,只涉及交互
2,Service為可復用的業務邏輯
3,Controller為Service的上級調用方
4,你這個case可以在Service中返回固定的傳回值,在Controller層做判斷,並拋出你想對應的例外。

當然了這只是我們目前的做法,分享一下。 。 。

迷茫

可以看看我這篇
AOP,MVC——Spring的學習以及對CodeIgniter的反思 /a/11...

大家讲道理

建議邏輯放在service,我們最近在做分散式微服務架構,我們之前是放在controller層的,拆分的時候基本上全部重新,另外有些app需要使用的接口如果放在controller就無法共用,蛋疼吧?

phpcn_u1582

封裝物件到底在Controller還是Service,還是要看具體的情況,個人認為如果是簡單的參數在Controller中進行封裝時是完全可以的,如果放到Service反而會顯得很冗餘,而且導致Service通用性變差;對於第二個問題,不容同意透過枚舉或不同的狀態碼來在Controller左做判斷拋出異常,完全可以自己定義一套異常處理機制,直接在Service層拋出,項目有針對此類別業務異常的處理機制,直接兩將Service的錯誤訊息和錯誤碼回復到View層,讓客戶端根據狀態碼和錯誤訊息作處理

熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板