Re: [J2SE] 談談Service的設計
首先先感謝你提出的指教,我發現這邊的板友這點真的非常眼利
把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
07/20 02:50, 1F
→
07/20 02:52, , 2F
07/20 02:52, 2F
討論串 (同標題文章)