8. 4 常见的架构设计策略 - 《轻量级J2EE企业应用实战——Struts Spring Hibernate整合开发》 - 免费试读 - book.csdn.net

来源:百度文库 编辑:神马文学网 时间:2024/07/08 07:19:49
【登录】【免费注册】
首页新闻论坛群组Blog文档下载读书Tag网摘搜索开源FAQ第二书店博文视点程序员
研发数据库中间件信息化视频.NETJava游戏移动人才外包培训厂商专区BEA专区
iAnywhere
IBM专区
Intel专区
IJS专区
SAP专区
Telelogic
SUN专区
定制邮局
人邮出版
微软专区

精品连载
书友会
收藏秀
特色书架
出版圈子
读书讨论群
读书博客
社区银行
    图书品种:204680种
书友会   连载
热门搜索:ASP.NETAjaxSpringHibernetJava
轻量级J2EE企业应用实战——Struts+Spring+Hibernate整合开发
8. 4 常见的架构设计策略 
http://book.csdn.net/2007-6-13 20:07:00
图书导读
当前章节:8. 4 常见的架构设计策略
·目录
·9. 1 系统架构说明
·9. 2 Hibernate持久层
·9. 3 DAO组件层
·9. 4 业务逻辑层
·9. 5 Web层设计
测试定制的 Java 控件
Rational Functional 正在挑战 GUI 自动化测试过程。
ibm.csdn.net/
北邮软件学院2007(京/沪)6月热考
全国规模最大的通信软件工程硕士培养基地 ☆自主命题 ☆多项奖金 ☆优势课程 ☆100%就业
www.buptmse.cn/zhaos...
多核编程的几个难题及其应对策略(难题一)
非常有用的实用资料
community.csdn.net/E...
8.4  常见的架构设计策略
目前流行的轻量级J2EE应用的架构比较一致,采用的技术也比较一致,通常使用Spring作为核心,向上整合MVC框架,向下整合ORM框架。使用Spring的IOC容器来管理各组件之间的依赖关系时,Spring的声明事务将负责业务逻辑层对象方法的事务管理。
但在固定的技术组合上,依然可能存在小的变化。下面依次讨论可能存在的架构策略。
8.4.1  贫血模式
贫血模式是最常用的设计架构,也是最容易理解的架构。为了让读者通过本书顺利进入轻量级J2EE企业应用开发,本书的第9章及第10章的范例都将采用这种简单的架构模式。
所谓贫血,指Domain Object只是单纯的数据类,不包含业务逻辑方法,即每个Domain Object类只包含基本的setter和getter方法。所有的业务逻辑都由业务逻辑组件实现,这种DomainObject就是所谓的贫血的DomainObject,采用这种DomainObject的架构即所谓的贫血模式。
下面以第9章的消息发布系统的部分代码为例,介绍贫血模式。
在贫血模式里,所有的Domain Object只是单纯的数据类,只包含每个属性的setter和getter方法,如下是两个持久化类。
第一个DomainObject是消息,其代码如下:
public class Newsextends BaseObject implements Serializable
{
//主键
privateLong id;
//消息标题
privateString title;
//消息内容
privateString content;
//消息的发布时间
privateDate postDate;
//消息的最后修改时间
privateDate lastModifyDate;
//消息所属分类
privateCategory category;
//消息对应的消息回复
privateSet newsReviews;
//无参数的构造器
publicNews() {
}
//消息回复对应的getter方法
publicSet getNewsReviews() {
returnnewsReviews;
}
//消息回复对应的setter方法
publicvoid setNewsReviews(Set newsReviews) {
this.newsReviews= newsReviews;
}
//消息分类对应的getter方法
publicCategory getCategory() {
returncategory;
}
//消息分类对应的setter方法
publicvoid setCategory(Category category) {
this.category= category;
}
//消息最后修改时间的getter方法
publicDate getLastModifyDate() {
returnlastModifyDate;
}
//消息最后修改时间的setter方法
publicvoid setLastModifyDate(Date lastModifyDate) {
this.lastModifyDate= lastModifyDate;
}
//消息发布时间的getter方法
publicDate getPostDate() {
returnpostDate;
}
//消息发布时间的setter方法
publicvoid setPostDate(Date postDate) {
this.postDate= postDate;
}
//消息内容对应的getter方法
publicString getContent() {
returncontent;
}
//消息发布者对应的setter方法
publicvoid setContent(String content) {
this.content= content;
}
//消息主键对应的getter方法
publicLong getId() {
returnid;
}
//消息主键对应的setter方法
publicvoid setId(Long id) {
this.id= id;
}
//消息标题对应的getter方法
publicString getTitle() {
returntitle;
}
//消息标题对应的setter方法
publicvoid setTitle(String title) {
this.title= title;
}
//DomainObject重写equals方法
publicboolean equals(Object object) {
if(!(object instanceof News)) {
returnfalse;
}
Newsrhs = (News) object;
returnthis.poster.equals(rhs.getPoster())
&&this.postDate.equals(rhs.getPostDate());
}
//DomainObject重写的hashCode方法
publicint hashCode() {
returnthis.poster.hashCode() + this.postDate.hashCode();
}
//DomainObject重写toString方法
publicString toString() {
return newToStringBuilder(this).append("id", this.id).append("title",
this.title).append("postDate",this.postDate).append("content",
this.content).append("lastModifyDate",this.lastModifyDate)
.append("poster",this.poster)
.append("category",this.category).append("newsReviews",
this.newsReviews).toString();
}
}
第二个DomainObject是消息对应的回复,其代码如下:
public class NewsReviewextends BaseObject
{
//消息回复的主键
privateLong id;
//消息回复的内容
privateString content;
//消息回复的回复时间
privateDate postDate;
//回复的最后修改时间
privateDate lastModifyDate;
//回复的对应的消息
privateNews news;
//消息回复的构造器
publicNewsReview() {
}
//回复内容对应的getter方法
publicString getContent() {
returncontent;
}
//回复内容对应的setter方法
publicvoid setContent(String content) {
this.content= content;
}
//回复主键对应的setter方法
publicLong getId() {
returnid;
}
//回复主键对应的setter方法
publicvoid setId(Long id) {
this.id= id;
}
//回复的最后修改时间对应的getter方法
publicDate getLastModifyDate() {
returnlastModifyDate;
}
//回复的最后修改时间对应的setter方法
publicvoid setLastModifyDate(Date lastModifyDate) {
this.lastModifyDate= lastModifyDate;
}
//回复对应的消息的getter方法
publicNews getNews() {
returnnews;
}
//回复对应的消息的setter方法
publicvoid setNews(News news) {
this.news= news;
}
//回复发布时间的getter方法
publicDate getPostDate() {
returnpostDate;
}
//回复发布时间的setter方法
publicvoid setPostDate(Date postDate) {
this.postDate= postDate;
}
//DomainObject重写的equals方法
publicboolean equals(Object object) {
if(!(object instanceof NewsReview)) {
returnfalse;
}
NewsReviewrhs = (NewsReview) object;
returnthis.poster.equals(rhs.getPoster()) &&
this.postDate.equals(rhs.getPostDate());
/*returnnew EqualsBuilder().append(this.news, rhs.news).append(
this.content,rhs.content).append(this.postDate, rhs.postDate)
.append(this.lastModifyDate,rhs.lastModifyDate).append(
this.id,rhs.id).append(this.poster, rhs.poster)
.isEquals();
*/
}
//DomainObject对应的hashCode方法
publicint hashCode() {
returnthis.poster.hashCode() + this.postDate.hashCode();
/*return newHashCodeBuilder(-1152635115, 884310249).append(this.news)
.append(this.content).append(this.postDate).append(
this.lastModifyDate).append(this.id)
.append(this.poster).toHashCode();
*/
}
//DomainObject对应的toString方法
publicString toString() {
returnnew ToStringBuilder(this).append("id", this.id).append(
"postDate",this.postDate).append("lastModifyDate",
this.lastModifyDate).append("content",this.content).append(
"poster",this.poster).append("news", this.news).toString();
}
}
从上面贫血模式的DomainObject可看出,其类代码中只有setter和getter方法,这种DomainObject只是单纯的数据体,类似于C的数据结构。虽然它的名字是Domain Object,却没有包含任何业务对象的相关方法。MartinFowler认为,这是一种不健康的建模方式,DomainModel既然代表了业务对象,就应该包含相关的业务方法。从语言的角度上来说,DomainModel在这里被映射为Java对象(一般都是ORM),Java对象应该是数据与动作的集合,贫血模型相当于抛弃了Java面向对象的性质。
Rod Johnson和Martin Fowler一致认为:贫血的Domain Object实际上以数据结构代替了对象。他们认为Domain Object应该是个完整的Java 对象, 既包含基本的数据,也包含了操作数据相应的业务逻辑方法。
下面是NewsDAOHibernate的源代码,该DAO对象用于操作News对象:
//NewsDAOHibernate继承HibernateDaoSupport,实现NewsDAO接口
public classNewsDAOHibernate extends HibernateDaoSupport implements NewsDAO
{
//根据主键加载消息
publicNews getNews(Long id)
{
News news = (News) getHibernateTemplate().get(News.class, id);
if (news == null) {
throw new ObjectRetrievalFailureException(News.class,id);
}
returnnews;
}
//保存新的消息
publicvoid saveNews(News news) {
getHibernateTemplate().saveOrUpdate(news);
}
//根据主键删除消息
publicvoid removeNews(Long id)
{
getHibernateTemplate().delete(getNews(id));
}
//查找全部的消息
publicList findAll()
{
getHibernateTemplate().find("fromNews"));
}
}
既然DAO对象完成具体的持久化操作,因此基本的CRUD操作都应该在DAO对象中实现。但DAO对象应该包含多少个查询方法,并不是确定的。因此,根据业务逻辑的不同需要, 不同的DAO对象可能有数量不等的查询方法。
对于现实中News,应该包含一个业务方法(addNewsReviews方法)。在贫血模式下,News类的代码并没有包含该业务方法,只是将该业务方法放到业务逻辑对象中实现,下面是业务逻辑对象实现addNewsReviews的代码:
public classFacadeManagerImpl implements FacadeManager
{
//业务逻辑对象依赖的DAO对象
privateCategoryDAO categoryDAO;
privateNewsDAO newsDAO;
privateNewsReviewDAO newsReviewDAO;
privateUserDAO userDAO;
//...此处还应该增加依赖注入DAO对象必需的setter方法
//...此处还应该增加其他业务逻辑方法
//下面是增加新闻回复的业务方法
publicNewsReview addNewsReview(Long newsId , String content)
{
//根据新闻id加载新闻
News news = newsDao.getNews(newsId);
//以默认构造器创建新闻回复
NewsReview review = new NewsReview();
//设置新闻与新闻回复之间的关联
review.setNews(news);
//设置新闻回复的内容
review.setContent(content);
//设置回复的回复时间
review.setPostDate(new Date());
//设置新闻回复的最后修改时间
review.setLastModifyDate(new Date());
//保存回复
newsReviewDAO.saveNewsReview(review);
return review;
}
}
在贫血模式下,业务逻辑对象正面封装了全部的业务逻辑方法,Web层仅与业务逻辑组件交互即可,无须访问底层的DAO对象。Spring的声明式事务管理将负责业务逻辑对象方法的事务性。
在贫血模式下,其分层非常清晰。DomainObject并不具备领域对象的业务逻辑功能,仅仅是ORM框架持久化所需的POJO,仅是数据载体。贫血模型容易理解,开发便捷,但严重背离了面向对象的设计思想,所有的Domain Object并不是完整的Java对象。
总结起来,贫血模式存在如下缺点:
— 项目需要书写大量的贫血类,当然也可以借助某些工具自动生成。
—  Domain Object的业务逻辑得不到体现。由于业务逻辑对象的复杂度大大增加,许多不应该由业务逻辑对象实现的业务逻辑方法,完全由业务逻辑对象实现,从而使业务逻辑对象的实现类变得相当臃肿。
贫血模式的优点是:开发简单、分层清晰、架构明晰且不易混淆;所有的依赖都是单向依赖,解耦优秀。适合于初学者及对架构把握不十分清晰的开发团队。
8.4.2  Rich Domain Object模式
在这种模式下,Domain Object不再是单纯的数据载体,Domain Object包含了相关的业务逻辑方法。例如News类包含了addNewsView方法等。
下面是修改后的News类的源代码:
public class Newsextends BaseObject
{
//此处省略了其他的属性
//此处省略了属性对应的setter和getter方法
//增加新闻回复的业务逻辑方法
publicNewsReview addNewsReview(String content)
{
//以默认构造器创建新闻回复实例
NewsReview review = new NewsReview();
//设置回复内容
review.setContent(content);
//设置回复的发布日期
review.setPostDate(new Date());
//设置回复的最后修改日期
review.setLastModifyDate(new Date());
//设置回复与消息的关联
review.setNews(this);
return review;
}
//此处省略了重写的hashCode,equals等方法
}
在上面的DomainObject中,包含了相应的业务逻辑方法,这是一种更完备的建模方法。
注意:不要在DomainObject中对消息回复完成持久化,如需完成持久化,必须调用DAO组件;一旦调用DAO组件,将造成DAO对象和DomainObject的双向依赖;另外,Domain Object中的业务逻辑方法还需要在业务逻辑组件中代理,才能真正实现持久化。
在上面的业务逻辑方法中,并没有进行持久化。如果抛开DAO层,这种DomainObject也可以独立测试,只是没有进行持久化。
DAO对象是变化最小的对象,它们都是进行基本的CRUD操作,在两种模型下的DAO对象没有变化。
另外还需要对业务逻辑对象进行改写,虽然Domain Object包含了基本业务逻辑方法,但业务逻辑对象还需代理这些方法,修改后业务逻辑对象的代码如下:
publicclass FacadeManagerImpl implements FacadeManager
{
//业务逻辑对象依赖的DAO对象
privateCategoryDAO categoryDAO;
privateNewsDAO newsDAO;
privateNewsReviewDAO newsReviewDAO;
privateUserDAO userDAO;
//...此处还应该增加依赖注入DAO对象必需的setter方法
//...此处还应该增加其他业务逻辑方法
//下面是增加新闻回复的业务方法
public NewsReview addNewsReview(Long newsId , String content)
{
//根据新闻id加载新闻
News news = newsDao.getNews(newsId);
//通过News的业务方法添加回复
NewsReviewreview = news.addNewsReview(content);
//此处必须显示持久化消息回复
newsReviewDAO.saveNewsReview(review);
return review;
}
}
在Rich DomainObject的模型中,addNewsReview方法将放在News类中实现,而业务逻辑对象仅对该方法进行简单的代理,执行必要的持久化操作。
在这里存在一个问题:业务逻辑方法很多,哪些业务逻辑方法应该放在Domain Object对象中实现,而哪些业务逻辑方法完全由业务逻辑对象实现呢?RodJohnson认为,可重用度高,与Domain Object密切相关的业务方法应放在Domain Object对象中实现。
业务逻辑方法是否需要由DomainObject实现的标准,从一定程序上说明了采用Rich Domain Object模型的原因。由于某些业务方法只是专一地属于某个DomainObject,因此将这些方法由Domain Object实现,能提供更好的软件复用,能更好地体现面向对象的封装性。
Rich DomainObject模型的各组件之间关系大致如图8.2所示(贫血模式的组件关系图与此类似)。

图8.2 Rich Domain Object的组件关系图
这种RichDomain Object模型主要的问题是业务逻辑对象比较复杂,由于业务逻辑对象需要正面封装所有的DAO对象,因而难免有大量的DAO方法(基本的CRUD)需要业务逻辑对象封装。业务逻辑对象封装DAO方法主要基于如下考虑:
—  DAO对象不应该暴露为Web层。
—  DAO对象的DAO方法必须增加事务控制代码,而事务控制则放在业务逻辑层完成。
为了简化业务逻辑对象的开发,Rich Domain Object模型可以有如下两个方向的改变:
— 合并业务逻辑对象与DAO对象。
— 合并业务逻辑对象和Domain Object。
1.合并业务逻辑对象与DAO对象
在这种模型下DAO对象不仅包含了各种CRUD方法,而且还包含各种业务逻辑方法。此时的DAO对象,已经完成了业务逻辑对象所有任务,变成了DAO对象和业务逻辑对象混合体。此时,业务逻辑对象依赖Domain Object,既提供基本的CRUD方法,也提供相应的业务逻辑方法。
下面是这种模式的代码(DomainObject的实现与前面的RichDomain Object模式一样,此处不再给出):
// NewsServiceHibernate继承HibernateDaoSupport,实现NewsService接口
publicclass NewsServiceHibernate extends HibernateDaoSupport
implementsNewsService
{
//此处添加NewsService对象依赖的DAO对象,以及对应的setter方法
//根据主键加载消息
public News getNews(Long id)
{
News news = (News) getHibernateTemplate().get(News.class, id);
if (news == null) {
throw new ObjectRetrievalFailureException(News.class, id);
}
returnnews;
}
//保存新的消息
public void saveNews(News news) {
getHibernateTemplate().saveOrUpdate(news);
}
//根据主键删除消息
publicvoid removeNews(Long id)
{
getHibernateTemplate().delete(getNews(id));
}
//查找全部的消息
publicList findAll()
{
getHibernateTemplate().find("fromNews"));
}
//下面是增加新闻回复的业务方法
public NewsReview addNewsReview(Long newsId , String content)
{
//根据新闻id加载新闻
News news = newsDao.getNews(newsId);
//通过News的业务方法添加回复
NewsReviewreview = news.addNewsReview(content);
//此处必须显示持久化消息回复
newsReviewService.saveNewsReview(review);
return review;
}
}
正如上面见到的,DAO对象和业务逻辑对象之间容易形成交叉依赖(可能某个业务逻辑方法的实现,必须依赖于原来的DAO对象)。当DAO对象被取消后,业务逻辑对象取代了DAO对象,因此变成了一个业务逻辑对象依赖多个业务逻辑对象。而每个业务逻辑对象都可能包含需要多个DAO对象协作的业务方法,从而导致业务逻辑对象之间的交叉依赖。
业务逻辑对象和DAO对象合并后的组件关系如图8.3所示。

图8.3 合并DAO对象和业务逻辑对象
这种模型也导致了DAO方法和业务逻辑方法混合在一起,显得职责不够单一,软件分层结构不够清晰。此外,使业务逻辑对象之间交叉依赖,容易产生混乱,未能做到彻底的简化。
2.合并业务逻辑对象和Domain Object
在这种模型下,所有的业务逻辑都应该被放在Domain Object里面,而此时的业务逻辑层不再是传统的业务逻辑层,它仅仅封装了事务和少量逻辑,完全无需DAO对象的支持。而DomainObject依赖于DAO对象执行持久化操作,此处Domain Object和DAO对象形成双向依赖,这种设计在某些地方也被称为充血模式,但有时会带来相当大的危险。
在这种设计模式下,几乎不再需要业务逻辑层,而Domain Object则依赖DAO对象完成持久化操作,下面是在这种模式下的News类代码:
public class Newsextends BaseObject
{
//此处省略了其他的属性。
//此处省略了属性对应的setter和getter方法
//增加新闻回复的业务逻辑方法
publicNewsReview addNewsReview(String content)
{
//以默认构造器创建新闻回复实例
NewsReview review = new NewsReview();
//设置回复内容
review.setContent(content);
//设置回复的发布日期
review.setPostDate(new Date());
//设置回复的最后修改日期
review.setLastModifyDate(new Date());
//设置回复与消息的关联
review.setNews(this);
//直接调用newsReviewsDao完成消息回复的持久化。
newsReviewsDao.save(review);
return review;
}
//此处省略了重写的hashCode,equals等方法
}
从上面代码中可以看到,由于DomainObject必须使用DAO对象完成持久化,因此Domain Object必须接收IOC容器的注入,而Domain Object获取容器注入的DAO对象,通过DAO对象完成持久化操作。
合并业务逻辑对象和Domain Object后各组件的关系如图8.4所示。
这种模型的优点是:业务逻辑对象非常简单,只提供简单的事务操作,业务逻辑对象无须依赖于DAO对象。
但这种模型的缺点也是非常明显的:
—  DAO对象和Domain Object形成了双向依赖,其复杂的双向依赖会导致很多潜在的问题。
— 业务逻辑层和Domain层的逻辑混淆不清,在实际项目中,极容易导致架构混乱。
— 由于使用业务逻辑对象提供事务封装特性,业务逻辑层必须对所有的DomainObject的逻辑提供相应的事务封装,因此业务逻辑对象必须重新定义Domain Object实现的业务逻辑,其工作相当烦琐。

图8.4 合并业务逻辑组件和DoaminObject
8.4.3  抛弃业务逻辑层
在Rich Domain Object模型的各种变化中,虽然努力简化业务逻辑对象,但业务逻辑对象依然存在,依然使用业务逻辑对象正面封装所有的业务请求。下面介绍更彻底的简化即,彻底放弃业务逻辑层。
抛弃业务逻辑层也有两种形式:
—  Domain Object彻底取代业务逻辑对象。
— 由控制器直接调用DAO对象。
1.Domain Object完全取代业务逻辑对象
这种设计模式是充血模式更加激进的演化。由于在充血模式中业务逻辑对象的作用仅仅只提供事务封装,业务逻辑对象存在的必要性不是很大,因此考虑对Domain Object的业务逻辑方法增加事务管理,而Web层的控制器则直接依赖于Domain Object。
这种模型更加简化,使Domain Object与DAO对象形成双向依赖,而Web层的控制器直接调用Domain Object的业务逻辑方法。这种模型在有些地方也被称为胀血模式。
这种模型的优点是:分层少,代码实现简单。
但这种模型的缺点也很明显:
— 业务逻辑对象的所有业务逻辑都将在Domain Object中实现,势必引起Domain Object的混乱。
—  Domain Object必须向Web层直接暴露,可能导致意想不到的问题。
这种模型与充血模式的缺点相同:Domain Object必须配置在Spring容器中,接受Spring容器的依赖注入。
在这种架构模型下,DomainObject相当不稳定。如果业务逻辑需要改变,Domain Object也需要发生改变,而DAO对象与DomainObject形成双向依赖,这将导致从底层的DomainObject和DAO对象的修改,使这种架构模式的分层完全失去意义。各层之间以强耦合方式组合在一起,各层对象互相依赖,牵一发而动全身,几乎是最差的一种策略。
2.控制器完成业务逻辑
在这种模型里,控制器直接调用DAO对象的CRUD方法,通过调用基本的CRUD方法,完成对应的业务逻辑方法。这种模型下,业务逻辑对象的功能由控制器完成。事务则推迟到控制器中完成,因此对控制器的execute方法增加事务控制即可。
对于基本的CRUD操作,控制器可直接调用DAO对象的方法,省略了业务逻辑对象的封装,这就是这种模型的最大优势。对于业务逻辑简单(当业务逻辑只是大量的CRUD操作时)的项目,使用这种模型也未尝不是一种好的选择。
但这种模型将导致控制变得臃肿,因为每个控制器除了包含原有的execute方法之外,还必须包含所需要的业务逻辑方法的实现。极大地省略了业务逻辑层的开发,避免了业务逻辑对象不得不大量封装基本的CRUD方法的弊端。
这种模型也有其缺点:
— 因为没有业务逻辑层,对于那些需要多个DAO参与的复杂业务逻辑,在控制器中必须重复实现,其效率低,也不利于软件重用。
—  Web层的功能不再清晰,人为复杂化。Web层不仅负责实现控制器逻辑,还需要完成业务逻辑的实现,因此必须精确控制何时调用DAO方法控制持久化。
—扩大了事务的影响范围。大部分情况下,只有业务逻辑方法需要增加事务控制,而execute方法无须增加事务控制。但如果execute方法直接调用了DAO对象的CRUD方法,则会导致这些方法不在事务环境下执行。为了让数据库访问都在事务环境下进行,因此不得不将事务范围扩大到整个execute方法。
本章小结
本章首先介绍了笔者在架构设计方面一些经验,从企业应用开发面临的困难讲起,并讲解了面对这些困难时应该采用何种应对策略。
其次介绍了常用的代理模式的使用,并深入介绍了由此衍生出来的Spring AOP框架。
最后重点介绍了贫血模型、Rich Domain Object模型、以及几种简化的模型,并分别分析了几种模型各自的优缺点。
图书导读
当前章节:8. 4 常见的架构设计策略
·目录
·9. 1 系统架构说明
·9. 2 Hibernate持久层
·9. 3 DAO组件层
·9. 4 业务逻辑层
·9. 5 Web层设计
同类图书推荐

Java邮件开...

Visual Fox...

.NET组件程...

Python网络...

JAVA并发编...
网友热评
你还没有登录,无法发表回复,请首先登录登 录..   [注 册]   忘记密码

',1)">
热点推荐
【专题】重磅出击----Asp.net2.0
【活动】6月5日与微软专家在线讨论Vista安全
【专题】SOA袭来,技术人员如何提升自己?
【活动】读书社区好戏连台,写书评得各项大奖
【专题】历届Jolt大奖全回顾。17届最新发布
【专题】07年技术最新热点:Ruby on Rails
【专题】徜徉在经验与科学之间
【专题】时代DIY 自己动手开发自己的搜索引擎
【专题】O‘reilly经典图书Programming系列【专题】引起众多MVP关注的Excel
【专题】征服ASP.NET2.0 Ajax Web开发技术详解
【专题】名著名译:深入解析Windows操作系统
【活动】最新博文外版图书征集译者【活动】社区银行新品上线,C币金豆换购物券
热门TAG
学生    美国    阅读    考研    材料    理论    旅游    测试    艺术    适合    机械    相关    小说    方面    基础知识    日本    经济    信息    医学    结合    关系    教师    市场    基本    方式    研究    高考    经验    我国    基础    实现    员工    具体    教材    传统    初学者    文学    政治    工程    考点
最新在读
最新读过
周评论最多
求医不如求己
JavaScript高级程序设计
《JavaScript权威指南,第五版》...
C#高级编程(第4版)
SQL必知必会(第3版)
SQL Server 2005数据库开发详解
深入浅出Ajax
深入浅出MFC(第二版)
代码大全(第二版)两届Software...
Java设计模式(第二版)(英文版...
最新书架
Web 2.0开发利器:RoR学习书架
最新版PHP+MySQL编程开发经典书籍
五种移动开发平台经典书架
SQL Server 2005学习必看的几本图书
AJAX学习必备三本书
2006年度计算机图书分类排行榜
2006年度.NET类图书读者评选Top10
2006JAVA类图书读者评选Top10
2006年度数据库类图书读者评选Top10
2006年度操作系统类图书读者评选Top10
2006年度设计语言类图书读者评选Top10


网站简介-广告服务-网站地图-帮助-联系方式-诚聘英才-English-问题报告
北京百联美达美数码科技有限公司  版权所有  京 ICP 证 020026 号
Copyright © 2000-2006, CSDN.NET, All Rights Reserved
360pskdocImg_14