今天碰到一個bug,最後發現原因應該就是service的設計不當(另一個提問)
那麼我們應該如何設計合理的service?有哪些要注意的?什麼才是好的service?有哪些的例子可以參考?
回複內容:
今天碰到一個bug,最後發現原因應該就是service的設計不當(另一個提問)
那麼我們應該如何設計合理的service?有哪些要注意的?什麼才是好的service?有哪些的例子可以參考?
我的解決辦法如下,有什麼缺點請指教:
Service應該分為2種:1,名詞Service; 2, 行為Service
如:UserService 與 RegisterService
對於【名詞Service】其裡面每個method都必須返回相應的對象,如UserService下的upgrade(uid)就必須返回被升級後的user對象。
對於【行為Service】只對外暴露出一個execute(data),excute(data)必須返回行為成功與否的狀態以及被施加這個行為的對象,如RegisterService下的excute(data)就必須返回註冊成功與否,以及如果成功了它影響的對象。
通常我們約定對外只調用【行為Service】,再在【行為Service】裡調用多個【名詞Service】和其他【行為Service】,如在RegisterService::execute(data)裡調用UserService::create(), UserService::markNewbee(uid),SendEmailService::execut()等;
【名詞Service】中允許調用別的【名詞Service】,但不允許調用【行為Service】,如UserService::upgrade不單單可以修改user也可以調用LogService::create來建立log,但不能調用LogoutService::execute()來登出使用者。
可以簡單的理解為一個Model處理它那張資料庫表,一個【名詞Service】處理多個Model,一個【行為Service】處理多個【名詞Service】,這裡【行為Service】也就是設計模式裡的facade,可以配合command模式使用。
所有Service的每個method的入參都可以是id或者對象執行個體,如upgrade()可以接受uid也可以接受user作為入參。
回到我的另一個提問,可以這麼寫
class AService{ function get(aid_or_object) { if (aid_or_object instanceOf A) { return aid_or_object; } return A.getById(aid); }}class PService{ function get(pid_or_object) { if (pid_or_object instanceOf P) { return pid_or_object; } return P.getById(pid); }}class Do2Service{ function execute(aid_or_object, pid_or_object = null) { a = AService.get(aid_or_object); if (pid_or_object instanceOf P) { p = pid_or_object } else { p = PService.get(a.pid); } p.s = 'zz'; p.save(); a.save(); return [:success, a, p]; }}class Do3Service{ function execute(pid_or_object) { p = PService.get(pid_or_object); p.s = 'cc'; p.save(); return [:success, p]; }}class Do1Service{ function execute(pid_or_object) { p = PService.get(pid_or_object); p.s = 'yy' if condition1 result, a, p = Do2Servce.execute(p.aid, p) if condition2 result, p = Do3Servce.execute(p) if condition3 p.a = 'a'; p.b = 'b'; p.save() return [:success, p, a]; }}
你的問題的本質,是兩個“主語”(只是在你的案例中恰好都是service而已)的各自一個“行為”(do1 和 do2)含有了完全相同的一個“行動效果”(修改p.s的值)。
衝突不在於service,而在於行動效果冗餘。
試想一下,換一個案例,其中只有一個主語,兩個行為(do1 和 do2)都是它的,那麼問題也是等價的。
兩個行為有重疊的行動效果,實在太常見的了。
關鍵在於,你怎樣界定,哪種重疊是滿足需求的?哪種是錯誤、不合理的?
舉一個滿足需求的例子:
需求是:p是一個滑鼠移至上方的tips(介面組件)。先根據滑鼠座標,賦值p.top為一個值。隨後,計算tips是否超出了視窗邊緣。如果是,則計算tips的top的最大值(因為視窗大小可能會被改變,所以需要計算),然後賦值p.top為該最大值。p.left同理。
這是我做網頁前端開發時遇到過的需求。
你的解決辦法,大概可以解決你的那一個具體案例,但換成別的情況可能就又不對症了。
在我看來,關鍵在於,一個行為的源頭(往往是事件)所導致一連串列動效果,其中要避免出現重疊;除非需求要求必要的重疊。
這“一連串”的“串法”,是設計上要想清楚的。你已經在朝這個方向努力了,只是關注點稍有偏離。
至於串的過程中的對象(主語/賓語)是不是service、是何種service,倒是沒有關係。