作者sbrhsieh (十年一梦)
看板java
标题Re: [J2SE] 谈谈Service的设计
时间Sun Jul 21 03:29:31 2013
已明了重点不在於 service 该怎麽设计,现在只针对一些细节说明(回覆)我的看法。
※ 引述《Killercat (杀人猫™)》之铭言:
: -------不过其实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所以有疑虑
我不明白第二点的意思。T被限制住一定是传入的Class<T>,这与从 serviceMap 取出
一个 value 会是 T 的关联是?
compiler 会给 warning 就是因为它没有办法确定他所产生的 code 能够保证
型别上的正确。
getService method 宣称它会传回完全相容於参数所指定的型别的物件,而其实作
是从一个 Map 的 value 而来,依照 serviceMap 的 generics 宣告,只能确定 value
定是 IService 相容的物件(当然这里不考虑使用 casting 乱搞的行为),getService
的实作中 compiler 所产生的 guarding code 是没有办法确定它一定能够传回相容於
参数所指定的型别,需要你要求 supress warning 并不是莫名其妙的事情。
编译器没有在 compile-time 做到型别上的保证,同样地你也没有办法,你会觉得
你有信心还是在於你去 reasoning 你的程式流程,认定这样子没错。如果这个
serviceMap 不是完全由你掌控(比如它从别处而来或是它的 reference 可扩散到
其他地方),你就不会认为这个 SupressWarning 是没有疑虑的,不是吗?
: : 你的做法的优点在於你把风险集中在一处,你透过让他人遵守一个 convension,
: : 并让转型的风险全落在一个(或一组)较有经验的人手上;缺点是暴露实作的细节(
: : 至少以文内的范例来说),client code 要使用一个 service 他必需要知道实作
: : 这个功能的 class 是哪一个。
: 这点其实我不太懂为什麽叫做暴露细节
: 大多数来讲Service都有自己独特的功能,除非像是CRUDService而且仅仅被限制在CRUD
: 我们来分两部分来讲,比方说你提到的以及CRUD,这种Service的特徵是
: "我服务的介面完全相同,差别仅在於我处理的Bean(我们称为Marsheled Bean)"
:[略]
: 再来看第二种Service
: 这些Service只有部分method是每个Service都相同的
: 但是各个Service有自己独特的不同,那我们的目标就不是"透过interface操作"
: 我们必须让使用者看到service独有的功能才能做事
: 就有如我blog里面提到的第一个NameCardService
: 只有他有sort跟getDuplicated,其他Service都没有
: 这算是恶性的expose吗?我还真的想不到不expose的方法 XD
: 如果你说的是我们应该要有一个INameCardService的话,正好,我们现在的设计有
我认为 service(or plugin) 都是可以在 runtime 去抽换的,那麽 client 一定是
以 interface 去操作之,不可能在编译期去知道 service 确切实作者(class)。
即便不考虑 runtime 抽换 service/plugin,client 要使用到 class name 来取用
service 的做法,不论是在开发上还是架构设计上,已放弃了许多弹性。
service 由 interface 来定义它的功能,每个 service 都是一个独立的
interface,不会有"多种 service 由一个 interface 定义共通部分,然後特定
service 有的功能就直接将 service 视为特定 class",而是若 service B 比
service A 多一项功能,就应该有一个 interface 是比 service A interface 多
一项功能的 interface 来定义 service B。(多个 service interface 彼此间可以
行成一组阶层)
: : 如果目标是让 client 只透过 initerface 使用 service,在取用 service 时又
: : 不需要指名道姓实作 service 的 class,那麽你这样的设计该怎麽去改良以
: : 符合目标?
我提出这一点的目的是:依你的范例来说它是个很单纯的(阳春),可是即便如此你
还是用到两个 SupressWarning,这 OK,若你能够验证你的设计定不会出错。
可是若考量其他的需求来改良你的设计,当它变得复杂了,你可能会发现利用参数
来 infer return type 的做法,变得不太容易写,SupressWarning 越来越难避免
且 SupressWarning 会扩散,情况不再是只有你需要 SupressWarning 你确定它是
无害的,也许 service 的实作者都需要用到 SupressWarning,那麽你就要要求
他们也能够保证他们的 code 的正确性,否则你当初希望 client 可以很简单地
取用到有正确编译期型别的目标就失去了。
*利用 argument 来 infer return type 的做法不算罕见,是不多但我认为这是
因为能这样用的场合很有限,要写多这样的设计又不用到 SupressWarning 更是少,
如果要求实作中所用到的 code(library, core classes)的实作也不使用
SupressWarning 则更少。
在很单纯的应用中的话,想要 client 在取用 service 可以有正确的编译期型别,
为什麽不考虑由你来写个 Services 这样的 utility class 来给 client 使用。
class Services {
IServiceA getServiceA(...);
IServiceB getServiceB(...);
...
}
由你来提供这些 method 里的 wrapper code。
或者是设计像 ServiceRegistry 这种东西,有注册/取得各种 Service 介面的实作品
的 method,实际使用注册部分的 code 由你来写,也可以简单做到你要的效果。
--
※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 1.172.243.180
※ 编辑: sbrhsieh 来自: 1.172.243.180 (07/21 03:30)