Architecture

[对谈录]“让主角穿好衣服”究竟是演员的职责还是导演的职责?

    如果要让剧中的主角要穿上西服, 导演 会 让 主演 去干这件事, 那么在建模时,“穿衣服”到底算演员的职责,还是导演的职责呢? 关于这类问题的争论,可能是建模时争议次数最多的,而且争论双方一般都很难说服对方。      A: 穿衣服是演员的份内事(封装),而且不同的演员有不同的穿衣顺序(多态),导演不需要关心演员是怎么穿的,只需要演员能穿好衣服出镜就行了      B: 演员不过是导演的木偶(贫血者),具体怎么穿应该让导演来定(Transaction Script),尤其在当穿衣需要他人辅助时(数据库,JNDI 等资源),更加只有导演才能做好,因为导演才能调配这些资源(在Script中注入资源或通过工厂生成资源)      A:若演员穿衣需要调配其它资源,那的确应该让导演承担这个职责;否则的话,应该把把穿衣置为演员的职责,这样可以实现较好的封装和多态;否则的话,如果把这个职责丢给导演,那么,首先导演的封装性不太好,其次一个导演要实现所有演员的穿衣逻辑,这要造成较多的if/else(影响代码简洁性),或者要把导演本身进行多态设计(这一般不好做,因为导演的singleton被破坏,一些资源也不好处理,比如事务、AOP什么的)      B:话虽这样说,但是在我们公司的整体架构下,演员这种东西仅仅是数据结构,它是静态的,它不应该封装什么奇怪的算法;而导演才是动作者,导演才有权力干活。这样的设计思路更容易让人理解,并且这样做有利于维护我们系统设计风格的一致性!     A:  …….     B:  …….

O/R Mapping中的n+1 select 问题

假设对象A关联对象B,对应地, 表a 关联 表b(表b 外键参考 表a) 现在我们要把所有A和其对应的B 从数据库中翻出来 如果不用O/R Mapping,直接用SQL,则可以写一条连接两表的SELECT语句搞定         select * from a join b on b.aId = a. aId 如果用O/R Mapping,实际生成的SQL可能是        select * from a   (假设取出的a的ID分别是 1,2…n)        select * from b where b.aId =  1        select * from b where b.aId =  2        ……        select …

O/R Mapping中的n+1 select 问题 Read More »

Core J2EE Patterns 笔记

Presentation Tier Patterns: 1. Interceptiing Filter。 一种AOP思想的体现。将requet/response的“通用”的pre-处理和post-处理放到一个filter servlet中来。这样就只需写一个filter,而不必将通用代码在各个servlet中都写一份。关于“通用”代码,例子有会话验证、编码转换等 2.Front Controler。 一种Facade思想。将所有的请求都交给一个或几个Servlet集中处理。这个Servlet可以把按请求的具体内容再将它分发给真正的处理者。这样的好处是可以只写一份公用代码,对所有request应用通用逻辑,还可以限制WEB系统的access point。 典型的例子有Spring的DispatcherServlet 3.Context Object Controller 不去从与协议密切相关的容器中去拿东西(如request.getParameter()),而是从与协议无关的context中去拿。这样可以去掉应用对协议的依赖。 个人认为这个模式没多大意义,因为所有的WEB应用基本上都完全采用HTTP协议 4.Application Controller 将action管理和view管理集中化、模块化。 5.View Helper 将View与它的处理逻辑分开。它的处理逻辑包括 业务逻辑、控制逻辑、展现格式化逻辑等。典型的例子如 Controller,JSTL,自建标签 等 6.Composite View 在页面上,如果要使用复合视图时,要使布局和各页面的内容分离。如Tiles,Sitemesh 7.Service to Worker 在把控制权传给View之前处理好请求并执行业务逻辑 8.Dispatch View 如果业务逻辑很少,并且View自己可以处理请求并执行业务逻辑,则把控制权直接交给View 9.Business Delegate 在客户端/表现层 和 业务层之间再放一个Business Delegate,它代理业务层向客户端/表现层服务,并隐藏业务层的实现细节。主要好处有: a.消除业务层和客户端/表现层之间的耦合 b.把网络、技术方面的exception(如naming、socket)翻译成可读的exception c.Business Delegate可以做一些附加的动作,比如出错重试、验证、缓存、同步等,而且这些细节客户端/表现层都不需要知道 d.查找远端对象并与之交互的职责交给了Business Delegate,表现层/客户端不需要关心这些事情 10.Service Locator 使用一个接口,集中地、透明地定位远端的服务组件。 11.Session Facade 在业务层,向远端的Client只暴露一个集中的服务接口。这样可以向Client隐藏服务层的细节,并可以集中地进行事务管理和安全管理。我不太认可这种方式,因为客户要求的服务是非常多的,这会使得此Facade中也存在很多并且相关性不是很强的方法,不符强内聚的原则 …

Core J2EE Patterns 笔记 Read More »

用URL作为资源,实现可配置的权限管理 — 这样真的好吗?

    用URL作为资源的意思是:将  访问某URL 定义为一个权限,只有拥有了该权限的用户,才可以请求该URL。    这样做的好处是:      1.实现可配置的权限管理。也就是说URL资源不必写死,可在生产环境中修改。      2.还可以将权限逻辑从业务逻辑中提成出来 —- 代码里不用判断权限,在后台配一下即可,以后改变权限逻辑时,也不需要修改代码    这两个好处是确切的,但是它在发布时存在一定的负面作用:      1.url定义要么在后台界面手动配,要么写在配置文件里,要么作为SQL写到数据库中。这些东西不是高级语言代码,没法用编译器防止“语法”错误,因此出错率很高      2.开发人员在开发调试时,一般会写一个功能,配一个权限,由于耐心、急躁等因素,等功能全部完成时,权限定义的配置却没有统一记录,等到系统测试时才恍然大悟!      3.由于测试环境和生产环境的不同,权限定义从测试移到生产之前,往往要做很多改动才行,这也增加了出错率,提高了烦恼程度      4.一个系统中, URL定义 一般会有很多, 本身维护起来就麻烦

在新增记录时清除过早的历史记录 —- 免去了维护负担,保持了数据完整性

     系统里为了防止数据库中记录太多,一般都要将过早的历史记录清除掉。一种策略是写一个SQL脚本,然后定期执行,这会带来一定的维护量。而且还有一个问题,如果一条业务记录分散在多个表中,删除记录时要非常注意这些表的关系,尤其是外键关联的级联特征,这可能会导致SQL脚本比较复杂。        而我常用的策略是,系统在新增某类记录时,自动删除过早的记录。这样就没有了额外的维护负担,而且,由高级语言写成的程序,在删除跨表记录时可能更便捷,更能保证数据的完整性。

其他企业级模式

Active Record     将数据持久化的逻辑(CRUD操作)直接写在Domain Object里面。“包装数据库表或视图中数据行的对象,封装数据库访问,在数据上添加域逻辑”

程序和数据要尽量分开,不要让 数据 承载 业务功能

       让 数据 承载 业务功能,比如用SQL来配置业务逻辑,可以减少硬编码,新增功能时不须重新开发,但是,一旦生产环境中的数据库被破坏,不但破坏了业务数据,也破坏了业务功能本身。这些被破坏了的业务功能,恢复起来是很困难的,除非每配一次业务逻辑,就记录一下对应的SQL语句.     如果将业务功能都写在程序里(比如代码里),那就不存在这个问题了

为嘛要分层?

    如果分了三层,当第三层的接口改了,只需要改第二层的调用代码,而不用改第一层的代码。如果不分层全都粘在一起,比如说展现逻辑和业务逻辑都调用了DAO接口,那么DAO接口变化时,业务逻辑代码和展现逻辑代码都要改了