- 浏览: 1704969 次
- 性别:
- 来自: 杭州699号
文章分类
最新评论
-
莫莫摸:
为什么不用dubbo
RCP数据传输模型回顾 -
大胡子爸爸:
String, Class 都实现了Serializable接 ...
RPC框架几行代码就够了 -
lss598018587:
谢谢大神分享,比起新手看复杂的dubbo框架还不如看大神的这一 ...
RPC框架几行代码就够了 -
15606915740:
你好,请问一下。<dubbo:consumer filt ...
Dubbo文档 -
joqk12345:
...
一些设计上的基本常识
转于自己在公司的Blog:
http://pt.alibaba-inc.com/wp/experience_886/software_design_general_knowledge.html
最近给团队新人讲了一些设计上的常识,可能会对其它的新人也有些帮助,
把暂时想到的几条,先记在这里。
1. API与SPI分离
框架或组件通常有两类客户,一个是使用者,一个是扩展者,
API(Application Programming Interface)是给使用者用的,
而SPI(Service Provide Interface)是给扩展者用的,
在设计时,尽量把它们隔离开,而不要混在一起,
也就是说,使用者是看不到扩展者写的实现的,
比如:一个Web框架,它有一个API接口叫Action,
里面有个execute()方法,是给使用者用来写业务逻辑的,
然后,Web框架有一个SPI接口给扩展者控制输出方式,
比如用velocity模板输出还是用json输出等,
如果这个Web框架使用一个都继承Action的VelocityAction和一个JsonAction做为扩展方式,
要用velocity模板输出的就继承VelocityAction,要用json输出的就继承JsonAction,
这就是API和SPI没有分离的反面例子,SPI接口混在了API接口中,
合理的方式是,有一个单独的Renderer接口,有VelocityRenderer和JsonRenderer实现,
Web框架将Action的输出转交给Renderer接口做渲染输出。
2. 服务域/实体域/会话域分离
任何框架或组件,总会有核心领域模型,比如:
Spring的Bean,Struts的Action,Dubbo的Service,Napoli的Queue等等
这个核心领域模型及其组成部分称为实体域,它代表着我们要操作的目标本身,
实体域通常是线程安全的,不管是通过不变类,同步状态,或复制的方式,
服务域也就是行为域,它是组件的功能集,同时也负责实体域和会话域的生命周期管理,
比如Spring的ApplicationContext,Dubbo的ServiceManager等,
服务域的对象通常会比较重,而且是线程安全的,并以单一实例服务于所有调用,
什么是会话?就是一次交互过程,
会话中重要的概念是上下文,什么是上下文?
比如我们说:“老地方见”,这里的“老地方”就是上下文信息,
为什么说“老地方”对方会知道,因为我们前面定义了“老地方”的具体内容,
所以说,上下文通常持有交互过程中的状态变量等,
会话对象通常较轻,每次请求都重新创建实例,请求结束后销毁。
简而言之:
把元信息交由实体域持有,
把一次请求中的临时状态由会话域持有,
由服务域贯穿整个过程。
3. 在重要的过程上设置拦截接口
如果你要写个远程调用框架,那远程调用的过程应该有一个统一的拦截接口,
如果你要写一个ORM框架,那至少SQL的执行过程,Mapping过程要有拦截接口,
如果你要写一个Web框架,那请求的执行过程应该要有拦截接口,
等等,没有哪个公用的框架可以Cover住所有需求,允许外置行为,是框架的基本扩展方式,
这样,如果有人想在远程调用前,验证下令牌,验证下黑白名单,统计下日志,
如果有人想在SQL执行前加下分页包装,做下数据权限控制,统计下SQL执行时间,
如果有人想在请求执行前检查下角色,包装下输入输出流,统计下请求量,
等等,就可以自行完成,而不用侵入框架内部,
拦截接口,通常是把过程本身用一个对象封装起来,传给拦截器链,
比如:远程调用主过程为invoke(),那拦截器接口通常为invoke(Invocation),
Invocation对象封装了本来要执行过程的上下文,并且Invocation里有一个invoke()方法,
由拦截器决定什么时候执行,同时,Invocation也代表拦截器行为本身,
这样上一拦截器的Invocation其实是包装的下一拦截器的过程,
直到最后一个拦截器的Invocation是包装的最终的invoke()过程,
同理,SQL主过程为execute(),那拦截器接口通常为execute(Execution),原理一样,
当然,实现方式可以任意,上面只是举例。
4. 重要的状态的变更发送事件并留出监听接口
这里先要讲一个事件和上面拦截器的区别,拦截器是干预过程的,它是过程的一部分,是基于过程行为的,
而事件是基于状态数据的,任何行为改变的相同状态,对事件应该是一致的,
事件通常是事后通知,是一个Callback接口,方法名通常是过去式的,比如onChanged(),
比如远程调用框架,当网络断开或连上应该发出一个事件,当出现错误也可以考虑发出一个事件,
这样外围应用就有可能观察到框架内部的变化,做相应适应。
5. 扩展接口职责尽可能单一,具有可组合性
比如,远程调用框架它的协议是可以替换的,
如果只提供一个总的扩展接口,当然可以做到切换协议,
但协议支持是可以细分为底层通讯,序列化,动态代理方式等等,
如果将接口拆细,正交分解,会更便于扩展者复用已有逻辑,而只是替换某部分实现策略,
当然这个分解的粒度需要把握好。
6. 微核插件式,平等对待第三方
大凡发展的比较好的框架,都遵守微核的理念,
Eclipse的微核是OSGi, Spring的微核是BeanFactory,Maven的微核是Plexus,
通常核心是不应该带有功能性的,而是一个生命周期和集成容器,
这样各功能可以通过相同的方式交互及扩展,并且任何功能都可以被替换,
如果做不到微核,至少要平等对待第三方,
即原作者能实现的功能,扩展者应该可以通过扩展的方式全部做到,
原作者要把自己也当作扩展者,这样才能保证框架的可持续性及由内向外的稳定性。
7. 不要控制外部对象的生命周期
比如上面说的Action使用接口和Renderer扩展接口,
框架如果让使用者或扩展者把Action或Renderer实现类的类名或类元信息报上来,
然后在内部通过反射newInstance()创建一个实例,
这样框架就控制了Action或Renderer实现类的生命周期,
Action或Renderer的生老病死,框架都自己做了,外部扩展或集成都无能为力,
好的办法是让使用者或扩展者把Action或Renderer实现类的实例报上来,
框架只是使用这些实例,这些对象是怎么创建的,怎么销毁的,都和框架无关,
框架最多提供工具类辅助管理,而不是绝对控制。
8. 可配置一定可编程,并保持友好的CoC约定
因为使用环境的不确定因素很多,框架总会有一些配置,
一般都会到classpath直扫某个指定名称的配置,或者启动时允许指定配置路径,
做为一个通用框架,应该做到凡是能配置文件做的一定要能通过编程方式进行,
否则当使用者需要将你的框架与另一个框架集成时就会带来很多不必要的麻烦,
另外,尽可能做一个标准约定,如果用户按某种约定做事时,就不需要该配置项。
比如:配置模板位置,你可以约定,如果放在templates目录下就不用配了,
如果你想换个目录,就配置下。
9. 区分命令与查询,明确前置条件与后置条件
这个是契约式设计的一部分,尽量遵守有返回值的方法是查询方法,void返回的方法是命令,
查询方法通常是幂等性的,无副作用的,也就是不改变任何状态,调n次结果都是一样的,
比如get某个属性值,或查询一条数据库记录,
命令是指有副作用的,也就是会修改状态,比如set某个值,或update某条数据库记录,
如果你的方法即做了修改状态的操作,又做了查询返回,如果可能,将其拆成写读分离的两个方法,
比如:User deleteUser(id),删除用户并返回被删除的用户,考虑改为getUser()和void的deleteUser()。
另外,每个方法都尽量前置断言传入参数的合法性,后置断言返回结果的合法性,并文档化。
10. 增量式扩展,而不要扩充原始核心概念
参见:http://javatar.iteye.com/blog/690845
------------------------
Dubbo设计分享系列:
谈谈扩充式扩展与增量式扩展
是的,因为Renderer是给扩展者实现的,而Action是给使用者实现的,两者之间不应该互相干扰。
http://pt.alibaba-inc.com/wp/experience_886/software_design_general_knowledge.html
最近给团队新人讲了一些设计上的常识,可能会对其它的新人也有些帮助,
把暂时想到的几条,先记在这里。
1. API与SPI分离
框架或组件通常有两类客户,一个是使用者,一个是扩展者,
API(Application Programming Interface)是给使用者用的,
而SPI(Service Provide Interface)是给扩展者用的,
在设计时,尽量把它们隔离开,而不要混在一起,
也就是说,使用者是看不到扩展者写的实现的,
比如:一个Web框架,它有一个API接口叫Action,
里面有个execute()方法,是给使用者用来写业务逻辑的,
然后,Web框架有一个SPI接口给扩展者控制输出方式,
比如用velocity模板输出还是用json输出等,
如果这个Web框架使用一个都继承Action的VelocityAction和一个JsonAction做为扩展方式,
要用velocity模板输出的就继承VelocityAction,要用json输出的就继承JsonAction,
这就是API和SPI没有分离的反面例子,SPI接口混在了API接口中,
合理的方式是,有一个单独的Renderer接口,有VelocityRenderer和JsonRenderer实现,
Web框架将Action的输出转交给Renderer接口做渲染输出。
2. 服务域/实体域/会话域分离
任何框架或组件,总会有核心领域模型,比如:
Spring的Bean,Struts的Action,Dubbo的Service,Napoli的Queue等等
这个核心领域模型及其组成部分称为实体域,它代表着我们要操作的目标本身,
实体域通常是线程安全的,不管是通过不变类,同步状态,或复制的方式,
服务域也就是行为域,它是组件的功能集,同时也负责实体域和会话域的生命周期管理,
比如Spring的ApplicationContext,Dubbo的ServiceManager等,
服务域的对象通常会比较重,而且是线程安全的,并以单一实例服务于所有调用,
什么是会话?就是一次交互过程,
会话中重要的概念是上下文,什么是上下文?
比如我们说:“老地方见”,这里的“老地方”就是上下文信息,
为什么说“老地方”对方会知道,因为我们前面定义了“老地方”的具体内容,
所以说,上下文通常持有交互过程中的状态变量等,
会话对象通常较轻,每次请求都重新创建实例,请求结束后销毁。
简而言之:
把元信息交由实体域持有,
把一次请求中的临时状态由会话域持有,
由服务域贯穿整个过程。
3. 在重要的过程上设置拦截接口
如果你要写个远程调用框架,那远程调用的过程应该有一个统一的拦截接口,
如果你要写一个ORM框架,那至少SQL的执行过程,Mapping过程要有拦截接口,
如果你要写一个Web框架,那请求的执行过程应该要有拦截接口,
等等,没有哪个公用的框架可以Cover住所有需求,允许外置行为,是框架的基本扩展方式,
这样,如果有人想在远程调用前,验证下令牌,验证下黑白名单,统计下日志,
如果有人想在SQL执行前加下分页包装,做下数据权限控制,统计下SQL执行时间,
如果有人想在请求执行前检查下角色,包装下输入输出流,统计下请求量,
等等,就可以自行完成,而不用侵入框架内部,
拦截接口,通常是把过程本身用一个对象封装起来,传给拦截器链,
比如:远程调用主过程为invoke(),那拦截器接口通常为invoke(Invocation),
Invocation对象封装了本来要执行过程的上下文,并且Invocation里有一个invoke()方法,
由拦截器决定什么时候执行,同时,Invocation也代表拦截器行为本身,
这样上一拦截器的Invocation其实是包装的下一拦截器的过程,
直到最后一个拦截器的Invocation是包装的最终的invoke()过程,
同理,SQL主过程为execute(),那拦截器接口通常为execute(Execution),原理一样,
当然,实现方式可以任意,上面只是举例。
4. 重要的状态的变更发送事件并留出监听接口
这里先要讲一个事件和上面拦截器的区别,拦截器是干预过程的,它是过程的一部分,是基于过程行为的,
而事件是基于状态数据的,任何行为改变的相同状态,对事件应该是一致的,
事件通常是事后通知,是一个Callback接口,方法名通常是过去式的,比如onChanged(),
比如远程调用框架,当网络断开或连上应该发出一个事件,当出现错误也可以考虑发出一个事件,
这样外围应用就有可能观察到框架内部的变化,做相应适应。
5. 扩展接口职责尽可能单一,具有可组合性
比如,远程调用框架它的协议是可以替换的,
如果只提供一个总的扩展接口,当然可以做到切换协议,
但协议支持是可以细分为底层通讯,序列化,动态代理方式等等,
如果将接口拆细,正交分解,会更便于扩展者复用已有逻辑,而只是替换某部分实现策略,
当然这个分解的粒度需要把握好。
6. 微核插件式,平等对待第三方
大凡发展的比较好的框架,都遵守微核的理念,
Eclipse的微核是OSGi, Spring的微核是BeanFactory,Maven的微核是Plexus,
通常核心是不应该带有功能性的,而是一个生命周期和集成容器,
这样各功能可以通过相同的方式交互及扩展,并且任何功能都可以被替换,
如果做不到微核,至少要平等对待第三方,
即原作者能实现的功能,扩展者应该可以通过扩展的方式全部做到,
原作者要把自己也当作扩展者,这样才能保证框架的可持续性及由内向外的稳定性。
7. 不要控制外部对象的生命周期
比如上面说的Action使用接口和Renderer扩展接口,
框架如果让使用者或扩展者把Action或Renderer实现类的类名或类元信息报上来,
然后在内部通过反射newInstance()创建一个实例,
这样框架就控制了Action或Renderer实现类的生命周期,
Action或Renderer的生老病死,框架都自己做了,外部扩展或集成都无能为力,
好的办法是让使用者或扩展者把Action或Renderer实现类的实例报上来,
框架只是使用这些实例,这些对象是怎么创建的,怎么销毁的,都和框架无关,
框架最多提供工具类辅助管理,而不是绝对控制。
8. 可配置一定可编程,并保持友好的CoC约定
因为使用环境的不确定因素很多,框架总会有一些配置,
一般都会到classpath直扫某个指定名称的配置,或者启动时允许指定配置路径,
做为一个通用框架,应该做到凡是能配置文件做的一定要能通过编程方式进行,
否则当使用者需要将你的框架与另一个框架集成时就会带来很多不必要的麻烦,
另外,尽可能做一个标准约定,如果用户按某种约定做事时,就不需要该配置项。
比如:配置模板位置,你可以约定,如果放在templates目录下就不用配了,
如果你想换个目录,就配置下。
9. 区分命令与查询,明确前置条件与后置条件
这个是契约式设计的一部分,尽量遵守有返回值的方法是查询方法,void返回的方法是命令,
查询方法通常是幂等性的,无副作用的,也就是不改变任何状态,调n次结果都是一样的,
比如get某个属性值,或查询一条数据库记录,
命令是指有副作用的,也就是会修改状态,比如set某个值,或update某条数据库记录,
如果你的方法即做了修改状态的操作,又做了查询返回,如果可能,将其拆成写读分离的两个方法,
比如:User deleteUser(id),删除用户并返回被删除的用户,考虑改为getUser()和void的deleteUser()。
另外,每个方法都尽量前置断言传入参数的合法性,后置断言返回结果的合法性,并文档化。
10. 增量式扩展,而不要扩充原始核心概念
参见:http://javatar.iteye.com/blog/690845
------------------------
Dubbo设计分享系列:
谈谈扩充式扩展与增量式扩展
评论
10 楼
joqk12345
2018-02-26
9 楼
wuhukui8369
2016-11-23
文章最后提到的 幂等性 和 命令 ,我平时设计的时候,有的时候考虑到网络开销而放在了一起。现在想想,接口的简洁更加重要。而且粒度更加细一点,可以供外部更加灵活的重用。
8 楼
aaa110
2015-02-06
请问楼主可以推荐一些书看看吗?
7 楼
liu0013
2014-05-18
6 楼
bin_1715575332
2014-01-10
好文章!!!!必须顶
5 楼
wuchienchao
2013-11-20
从dubbo和您的文章中学习到太多太多,由衷感谢楼主的博学和奉献精神。
祝愿楼主事业有成、身体健康。
祝愿楼主事业有成、身体健康。
4 楼
javatar
2011-06-01
wu_yong988 写道
关于 API与SPI分离 不是很理解!
Renderer接口 就是SPI?
Renderer接口 就是SPI?
是的,因为Renderer是给扩展者实现的,而Action是给使用者实现的,两者之间不应该互相干扰。
3 楼
wu_yong988
2011-05-30
关于 API与SPI分离 不是很理解!
Renderer接口 就是SPI?
Renderer接口 就是SPI?
2 楼
flykk
2010-08-26
刚学习了struts的拦截器,对第一条深有感触。楼主的总结真的很好。学习了!
1 楼
sarstime
2010-08-20
实践中宝贵的经验,谢谢分享
发表评论
-
能力成长模型
2012-05-09 00:28 23020最近看了温伯格1986年出版的《技术领导之路》, 很老的书,讲 ... -
以HTTL为例讲讲模块分包&领域模型&扩展框架
2011-10-09 20:08 16819注:该博客内容已加入 ... -
使用Map参数的Webx3扩展
2011-08-28 02:10 5935因Webx3是开源的,所以把这个简单的Webx3扩展发在博客上 ... -
Netty内存泄露
2011-08-02 20:09 24980转于自己在公司的Blog: ... -
Grizzly和Netty以及Mina简单性能对比
2011-07-17 02:48 29767转于自己在公司的Blog: http://pt.alibaba ... -
RPC框架几行代码就够了
2011-07-14 00:34 90295转于自己在公司的Blog: http://pt.alibaba ... -
魔鬼在细节中
2011-05-24 14:50 32255转于自己在公司的Blog: ... -
Dubbo扩展点重构
2011-05-12 22:09 38913转于自己在公司的Blog: http://pt.alibaba ... -
配置设计
2011-03-09 23:41 23615转于自己在公司的Blog: ... -
[转]HTML5设计原理
2011-03-09 22:57 7841Jeremy Keith在 Fronteers 2010 ... -
Hessian序列化不设SerializerFactory性能问题
2010-12-27 11:38 6490转于自己在公司的Blog: http://pt.alibaba ... -
动态代理方案性能对比
2010-11-17 21:38 46258转于自己在公司的Blog: http://pt.alibaba ... -
防痴呆设计
2010-11-05 18:58 17704转于自己在公司的Blog: ... -
负载均衡扩展接口重构
2010-11-05 18:53 8768转于自己在公司的Blog: ... -
分布式服务框架常被质疑的价值
2010-11-05 18:52 5743转于自己在公司的Blog: http://pt.alibaba ... -
Hessian3.2.1在序列化32.5k字符串时的问题
2010-11-05 18:49 7291转于自己在公司的Blog: http://pt.alibaba ... -
谈谈扩充式扩展与增量式扩展
2010-06-12 19:46 19428转于自己在公司的Blog: http://pt.alibaba ... -
Scaling Architecture
2010-02-25 10:31 4130Scaling Second Life: http://p ... -
EBay SOA
2010-02-23 18:23 4812EBay SOA PPT -
服务化基础设施
2009-11-15 23:11 6291服务化,也可以叫SOA, ...
相关推荐
电子设计基础知识b.avi"这个视频文件可能涵盖了上述的一些或全部主题,通过视觉和听觉的方式进一步解释和演示了电子设计的实践操作。观看这个视频,你将有机会深化对电子设计的理解,提高你的设计技能。同时,结合...
以下是一些在PCB设计过程中必须掌握的基础知识点: 1. FPGA器件管脚分配验证:在设计包含FPGA的电路系统时,应先使用专门的软件(例如Quartus II)对管脚分配进行验证,因为FPGA中的某些特殊管脚不能用作普通输入...
### 网页设计配色基础知识 在网页设计过程中,色彩的选择与搭配是至关重要的一个环节。良好的色彩搭配不仅能够提升网站的整体美感,还能更好地传达信息、吸引用户注意,并激发用户的兴趣。本文将从基本的颜色理论...
本文将重点介绍汽车设计中的一些基本概念,包括车型分类、环保理念以及汽车技术的英文缩写。 1. SUV(Sport Utility Vehicle): SUV全称运动型多用途汽车,它最初的设计理念是结合越野车的性能和轿车的舒适性。...
这个名为“平面设计师设计基本常识.rar”的压缩包文件,显然旨在为初入设计领域的学习者提供基础的理论知识和实践指导。下面我们将深入探讨平面设计的一些关键知识点。 1. **设计原则**:平面设计的基础包括对称、...
热设计的基础知识与规范文档旨在向读者介绍热设计领域的基础理论知识以及相关技术规范。热设计是指在产品设计和制造过程中,对产品进行散热处理,以确保产品在正常工作条件下不会因为热量堆积而损坏或降低性能。热...
不过,根据提供的文件信息,我可以围绕“集成电路掩模设计-基础版图技术”这一主题进行详细说明。 首先,集成电路的设计是现代电子技术不可或缺的一部分,而掩模设计作为集成电路生产过程中最为关键的步骤之一,它...
由于提供的文件内容片段混乱且包含大量不可识别的字符和结构不清的数字和字母组合,这些片段并...实际上,要确切地从《机械设计基础》答案.pdf文件中提取出知识点,需要获得完整的、未经OCR扫描错误和遗漏的文档内容。
《数字系统设计基础教程》是一本专为集成电路(IC)设计初学者编写的教材,它深入浅出地介绍了数字系统设计的基本概念和技术。这门课程旨在帮助学生掌握数字逻辑、计算机硬件以及数字信号处理的基础知识,为后续的...
最后,作者可能还会讨论一些高级话题,如低功耗设计策略、SoC(System on Chip)集成技术,以及当前热门的片上网络(NoC)设计。 总的来说,《经典书籍——IC设计基础》是一本全面覆盖IC设计基础知识的教程,不仅...
第18章可能涵盖了现代机械设计的一些新技术和趋势,比如计算机辅助设计(CAD)、有限元分析(FEM)等,这些技术在现代机械设计中起着关键作用,提高了设计效率和精度。 通过研读这些章节的习题答案,学习者可以深入...
"程序员用到的一些基础知识"这个标题暗示了这是一个关于程序员通用技能和知识的集合,可能是为了帮助程序员准备面试或者提升日常工作效率。从描述中可以看出,这份资源可能包含了从网络上收集的对程序员面试有帮助的...
"电路设计基础知识2021"这一主题涵盖了从理论基础到实践应用的多方面知识,旨在帮助学习者掌握电路设计的基本概念和技术。 首先,电路设计涉及到电路分析,包括电压、电流、电阻的欧姆定律,以及电源、负载和导线的...
"设计配色常识"这个主题涵盖了多个关键知识点,这些知识可以帮助设计师们创作出引人入目的界面和视觉元素。让我们深入探讨一下其中的一些核心概念。 1. **色彩理论**:设计配色的基础是色彩理论,它包括了颜色的...
通过这些章节的学习,学生可以掌握到设计机械部件的基本方法和原则,为未来在机械工程领域的发展打下坚实的基础。 书中所涉及的章节,如齿轮设计、螺纹连接、轴的设计、弹簧设计等,每一点都贯穿着对机械元件性能的...
从提供的文件信息来看,内容似乎是关于“服装设计入门基础知识”的文档,但是【部分内容】这部分信息呈现的是一串看似无序的数字和符号,并不构成有意义的句子。因此,无法从中直接提取知识点。但是,考虑到“计算机...
对数据库设计的一些基本知识进行解答,列举出数据库设计常见的一些使用场景和场景设计需求分析,列举8个常见的经典数据库课程设计项目,列出数据库课程设计常用的工具,列出5个免费的数据库设计工具
而数字逻辑设计基础涉及的知识点主要包括数字逻辑电路的基本概念、逻辑代数的原理、逻辑门电路的设计与分析、组合逻辑和时序逻辑电路的设计、数字系统的设计方法等。 VHDL(Very-High-Speed Integrated Circuit ...
IC 设计基础知识点 在本章中,我们将学习 IC 设计基础知识的重要方面,即 CMOS 版图设计基础。CMOS 版图设计是 IC 设计中最基本和最重要的部分,它涉及到数字电路和模拟电路的设计。 基本概念 在 CMOS 版图设计中...
集成电路设计基础是电子工程领域中的核心课程,它涵盖了半导体物理学、电路理论、数字逻辑和VLSI(超大规模集成电路)设计等多个方面的知识。本课件集合了这些关键领域的要点,旨在帮助学习者深入理解集成电路的设计...