作者swmimi (小君君要加油喔 !!)
看板java
标题Re: [J2SE] 谈谈Service的设计
时间Sat Jul 20 04:14:43 2013
我是觉得这篇的重点在於
static public <T extends Object> T getXXXX(Class<T> clazz)
然後这行程式码要放在哪里?
java管理物件因为静态特性的原因,导致整天转来转去的
导入泛型之後,算是解决这方面的困扰
然後加入injection之後,对於物件管理就更好了
不过我从本篇的范例
看到实作物件及实作管理的解法:继承
将那行程式码利用继承放到抽象那层,并透过建构子的方式来管理物件
但我自己是会采用组合方式的建构工厂样式的方式来提供
例如推文提到的google guice中的范例
Injector injector = Guice.createInjector(new BillingModule());
BillingService billingService = injector.getInstance(BillingService.class);
管理物件还是交由某个专门物件来负责,而且也不需要因为要管理物件而继承
因为不管是service handler也好
其继承的理由不应该是为管理其子类别或是该package底下的物件
而利用物件的建构式来作为物件管理的入口也容易会遭到继承的实作破坏
而物件管理也必要得要透过该物件的建构式才能的话,也是有点不确定
因为我想物件实体的产生应该会有特定时机跟限制
也就是不会没事就去new一下,也不会到处都在new,或者忘了new
所以我应该会采用工厂的概念
然後用guice去实作
并且制定限制
(其实我已经习惯bridge了,而非adapter了)
对外限制,对内随便
class SystemContext{
//这行可以改在建构子
Injector injector = Guice.createInjector(new BillingModule());
public <T extends IService> T getService(Class<T> clazz){
//其实这里或许可以做点事...
return injector.getInstance(clazz.class);
}
public <T extends IHandler> T getHandler(Class<T> clazz){
return injector.getInstance(clazz.class);
}
}
又或者我可以自己简单DIY
class SystemContext{
//这行是放物件的map
static public <T extends IService> T getService(Class<T> clazz){
return 物件;
}
static public <T extends IHandler> T getHandler(Class<T> clazz){
return 物件;
}
}
然後SystemContext可以变成介面
从而提供更大的弹性
把物件管理的要点从各IService的实作抽离出来
如此IService的继承架构就会单纯许多,仅只在於本身的意义
(因为java没有多重继承,万一IService下面某代需要范本或是抽象的话...)
继承的子类别的建构式也不会要迁就物件管理的选项
其实我只想表达的就是,我不会用继承来做物件管理就是
最後在此仅为赚P币的参考
谢谢
※ 引述《Killercat (杀人猫™)》之铭言:
: 首先先感谢你提出的指教,我发现这边的板友这点真的非常眼利
: 把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: 111.241.124.167
1F:→ Killercat:这也是一种看法。Injection目前可能是因为我还没看过 07/20 04:18
2F:→ Killercat:比较合乎我个人标准的实作,所以我会比较避免RTTI 07/20 04:20
3F:→ Killercat:不过我很赞同你从物件管理便利的观点看设计的想法 07/20 04:21