Re: [J2SE] 談談Service的設計

看板java作者 (殺人貓™)時間12年前 (2013/07/20 02:27), 編輯推噓0(002)
留言2則, 2人參與, 最新討論串5/9 (看更多)
首先先感謝你提出的指教,我發現這邊的板友這點真的非常眼利 把Service該設計上沒有提到的疏漏都有看到 而Service該怎麼設計是我另外一篇文會提到的東西, 這篇其實只是以Service為範例聊generic technique,很明顯是我標題下的不好 =P 應該題目是generic techique,而不是打上Service設計 XD 讓我考慮一下正確的題目該下什麼好.... btw,您們提到的都是在真正跑專案會碰到的問題,well done 我也會針對這部分在這篇文章說明一下 ※ 引述《sbrhsieh (十年一夢)》之銘言: : 看完整篇文,我的心得是: : 的確有善用到 Java generics 這個 feature,但你在文中強調的型別安全實際上 : 在 compile-time 是沒有保證的,純視這個設計正不正確罷了。 這個...我想強調的compile-time是相對於framework user,也就是client。 "使用者不會因為任何原因拿錯東西"(這概念很相似於C++的exception netural) "使用者可以完全正確的由Auto Complete取得功能"(好聰明的eclipse) "使用者完全不可能接錯Service" 至於該轉對與否,其實是我們creator應該去做的,我個人是覺得這點出發點就夠了 -------不過其實framework creator端是否真的沒做到轉型安全? 我們看一下 如果說是要完全的compile-time check safe,那那隻Supress Warning一定要消掉 不過仔細看就可以發現,這是完全語法上的問題,而不是轉型的問題... private static Map<Class<? extends IService>, IService> serviceMap = new HashMap<Class<? extends IService>, IService>(); @SuppressWarnings("unchecked") static public <T extends IService> T getService(Class<T> clazz) { return (T)serviceMap.get(clazz); } 我們把最關鍵轉型的兩行貼上來看一下 乍看之下getService是不安全的,因為他強轉了T回去。但是,仔細看我們就會發現, 經由精巧的generic限制,所以我們的傳回值理論上不該強轉也該能傳回去。 1. T被限制住一定是IService(所以傳入錯誤的.class會編譯爆炸) 2. T被限制住一定試傳入的Class<T> 在這兩個條件的限制下去拿取service map,其實已經有足夠的明確性以及把握去指向 正確的instance,而這個(T)強轉其實只是語法上不得不為 我們在寫Generic的時候其實很多時候都會被迫莫名其妙的強轉T 完全只是因為compiler time沒辦法去得到T之間的關係而已,所以即使在Framework side 我相信這邊也是符合了型別安全的要求,而非因為SupressWarning所以有疑慮 : 你的做法的優點在於你把風險集中在一處,你透過讓他人遵守一個 convension, : 並讓轉型的風險全落在一個(或一組)較有經驗的人手上;缺點是暴露實作的細節( : 至少以文內的範例來說),client code 要使用一個 service 他必需要知道實作 : 這個功能的 class 是哪一個。 這點其實我不太懂為什麼叫做暴露細節 大多數來講Service都有自己獨特的功能,除非像是CRUDService而且僅僅被限制在CRUD 我們來分兩部分來講,比方說你提到的以及CRUD,這種Service的特徵是 "我服務的介面完全相同,差別僅在於我處理的Bean(我們稱為Marsheled Bean)" 這也是為什麼範例裡的CRUD有這Marshel Bean的原因 -- 其實就是當時懶 隨手拷貝我自己的專案的Service再塗塗改改 XD 事實上我會在下一篇Service設計提到,我試圖在這裡聊聊這個先 你可以完全不知道Service是哪個的情況下做到完全的CRUD PersonalRecord pr = new PersonalRecord(); ... ... //fill the data in ... BaseCRUDService.create(pr); ---- AccessContext<PersonalRecord> ac = new AccessContext<PersonalRecord>("John"); PersonalRecord pr = BaseCRUDService.read(ac); BaseCRUDService.delete(ac); 完全不需要知道Service,我們只需要Bean就可以正確無誤地找到該負責的Service 不知道是不是就是你提的這個"只靠interface就能做事" (當然 我的例子是只靠BaseCRUDService就能做事...他也是繼承IService) 這些都是已經寫好了,而且正在跑的code,所以我很確定做得到(炸) 再來看第二種Service 這些Service只有部分method是每個Service都相同的 但是各個Service有自己獨特的不同,那我們的目標就不是"透過interface操作" 我們必須讓使用者看到service獨有的功能才能做事 就有如我blog裡面提到的第一個NameCardService 只有他有sort跟getDuplicated,其他Service都沒有 這算是惡性的expose嗎?我還真的想不到不expose的方法 XD 如果你說的是我們應該要有一個INameCardService的話,正好,我們現在的設計有 不過是為了另外一個的理由,詳見下一段 : 如果目標是讓 client 只透過 initerface 使用 service,在取用 service 時又 : 不需要指名道姓實作 service 的 class,那麼你這樣的設計該怎麼去改良以 : 符合目標? 真是銳利的眼神 XD 我不記得我有在裡面提過"用interface存取service" 但是的確,實作設計上是這樣 -- 為了thread safe做了一些很精美的調整 上面那句話其實我已經在上一段回答,在能夠被ICRUDService涵蓋的範圍內 的確可以相當輕易的連PersonalRecordService都不需要import 就能操作CRUD 所以我想上面那句大多數的部分我已經回答了 =P 問題在於不能被涵蓋的情況(不過我相信你一開始想問的應該不是這種case) 我順便回答一下上一篇忘了是推文還是回文提到的Multi Thread問題 <下面文章很長而且很雜亂,慎入> 事實上在真正的系統裡面這個Service其實長的是這樣 (這本來是下一篇Service設計的主題,我簡略的試圖在bbs上面介紹一下) 順便介紹一下我自己的PR Client Service設計克服multi thread的作法 (PR = Peer2Peer Remote) 因為我的PR這部分是用C++寫的,所以我只能在概念上把他轉成java 而沒辦法直接拿code過來用(畢竟template跟generic大不同....) 所以要是下面有語法上的錯誤請不吝指正 interface IService { //common service method, getName(), getServiceID(), getReferenceCount()... } interface IMasterChannelService extends IService{ void A(); void B(); } class MasterChannelService extends QueueServiceBase implements IMasterChannelService { public void A(); public void B(); } QueueServiceBase等等會提到這是幹嘛的,他跟我們主題有很大的關係 他一部分的功能其實跟上面的BaseCRUDService有點像,不過不全是 題外話: 他在C++裡面是用Policy方式實作出來的(請見Modern C++ Design第二章) 但是在java裡面只能用可憐兮兮的QueueServiceBase的方式實現 這就是為什麼當初這塊用C++來寫的一部分原因 XD C++ Template可以做出太多奇妙的事情了 我們要怎麼使用這個Service呢?由提供者提供一個Handler 假設我們已經有一個實體MasterChannelService mcs; //很像getService吧? IMasterChannelService handler = QueueServiceBase.getHandler(MasterChannelService.class); sh.A(); sh.B(); QueueServiceBase.transit(sh); //把handler排進去,等著真正執行 getHandler會拿回一個"實作IMasterChannelService"的東西 他做的事情僅僅只有一項,就是把使用者對於Master Channel Service的動作記錄下來 紀錄完畢以後再transit回mcs這個service instance 由mcs instance來排時間執行他,可能可以是Thread pool,可能可以是single thread 但是我們可以做multi thread不停丟quest進去 這部份則是我們怎麼用interface來避開multi thread的方法, 也經由handler這個完全不相干的實作避免暴露MasterChannelService的細節 希望這部分有回答到上面兩個人的問題 應該說,我是這樣設計的,跟大家分享一下 =P -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 119.14.20.94

07/20 02:50, , 1F
...好聰明的eclipse ???
07/20 02:50, 1F

07/20 02:52, , 2F
blog下面範例的"大家可以試試看"那段 =P
07/20 02:52, 2F
文章代碼(AID): #1HwOKfKl (java)
討論串 (同標題文章)
文章代碼(AID): #1HwOKfKl (java)