作者Killercat (杀人猫™)
看板java
标题Re: [J2SE] 谈谈Service的设计
时间Sat Jul 20 02:27:50 2013
首先先感谢你提出的指教,我发现这边的板友这点真的非常眼利
把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
1F:→ cuteclare:...好聪明的eclipse ??? 07/20 02:50
2F:→ Killercat:blog下面范例的"大家可以试试看"那段 =P 07/20 02:52