2010年7月13日
摘要: 一、分布式实现原理
如上图所示,主要通过Apache-Server作为中转服务器,实现多个tomcat服务器之间的分布式处理,用户直接请求Apache-Server,然后Apache-Server会将请求分发到具体的tomcat-server,之...
阅读全文
posted @
2011-06-22 16:09 obpm 阅读(8197) |
评论 (4) |
编辑 收藏
从设计理念层面看abstract class和interface
上面主要从语法定义和编程的角度论述了abstract class和interface的区别,这些层面的区别是比较低层次的、非本质的。本小节将从另一个层面:abstract class和interface所反映出的设计理念,来分析一下二者的区别。作者认为,从这个层面进行分析才能理解二者概念的本质所在。
前面已经提到过,abstarct class在Java语言中体现了一种继承关系,要想使得继承关系合理,父类和派生类之间必须存在"is a"关系,即父类和派生类在概念本质上应该是相同的(参考文献〔3〕中有关于"is a"关系的大篇幅深入的论述,有兴趣的读者可以参考)。对于interface 来说则不然,并不要求interface的实现者和interface定义在概念本质上是一致的,仅仅是实现了interface定义的契约而已。为了使论述便于理解,下面将通过一个简单的实例进行说明。
考虑这样一个例子,假设在我们的问题领域中有一个关于Door的抽象概念,该Door具有执行两个动作open和close,此时我们可以通过abstract class或者interface来定义一个表示该抽象概念的类型,定义方式分别如下所示:
使用abstract class方式定义Door:
abstract class Door {
abstract void open();
abstract void close();
}
使用interface方式定义Door:
interface Door {
void open();
void close();
}
其他具体的Door类型可以extends使用abstract class方式定义的Door或者implements使用interface方式定义的Door。看起来好像使用abstract class和interface没有大的区别。
如果现在要求Door还要具有报警的功能。我们该如何设计针对该例子的类结构呢(在本例中,主要是为了展示abstract class和interface反映在设计理念上的区别,其他方面无关的问题都做了简化或者忽略)?下面将罗列出可能的解决方案,并从设计理念层面对这些不同的方案进行分析。
解决方案一:
简单的在Door的定义中增加一个alarm方法,如下:
abstract class Door {
abstract void open();
abstract void close();
abstract void alarm();
}
或者
interface Door {
void open();
void close();
void alarm();
}
那么具有报警功能的AlarmDoor的定义方式如下:
class AlarmDoor extends Door {
void open() { … }
void close() { … }
void alarm() { … }
}
或者
class AlarmDoor implements Door {
void open() { … }
void close() { … }
void alarm() { … }
}
这种方法违反了面向对象设计中的一个核心原则ISP(Interface Segregation Priciple),在Door的定义中把Door概念本身固有的行为方法和另外一个概念"报警器"的行为方法混在了一起。这样引起的一个问题是那些仅仅依赖于Door这个概念的模块会因为"报警器"这个概念的改变(比如:修改alarm方法的参数)而改变,反之依然。
解决方案二:
既然open、close和alarm属于两个不同的概念,根据ISP原则应该把它们分别定义在代表这两个概念的抽象类中。定义方式有:这两个概念都使用abstract class方式定义;两个概念都使用interface方式定义;一个概念使用abstract class方式定义,另一个概念使用interface方式定义。
显然,由于Java语言不支持多重继承,所以两个概念都使用abstract class方式定义是不可行的。后面两种方式都是可行的,但是对于它们的选择却反映出对于问题领域中的概念本质的理解、对于设计意图的反映是否正确、合理。我们一一来分析、说明。
如果两个概念都使用interface方式来定义,那么就反映出两个问题:1、我们可能没有理解清楚问题领域,AlarmDoor在概念本质上到底是Door还是报警器?2、如果我们对于问题领域的理解没有问题,比如:我们通过对于问题领域的分析发现AlarmDoor在概念本质上和Door是一致的,那么我们在实现时就没有能够正确的揭示我们的设计意图,因为在这两个概念的定义上(均使用interface方式定义)反映不出上述含义。
如果我们对于问题领域的理解是:AlarmDoor在概念本质上是Door,同时它有具有报警的功能。我们该如何来设计、实现来明确的反映出我们的意思呢?前面已经说过,abstract class在Java语言中表示一种继承关系,而继承关系在本质上是"is a"关系。所以对于Door这个概念,我们应该使用abstarct class方式来定义。另外,AlarmDoor又具有报警功能,说明它又能够完成报警概念中定义的行为,所以报警概念可以通过interface方式定义。如下所示:
abstract class Door {
abstract void open();
abstract void close();
}
interface Alarm {
void alarm();
}
class AlarmDoor extends Door implements Alarm {
void open() { … }
void close() { … }
void alarm() { … }
}
这种实现方式基本上能够明确的反映出我们对于问题领域的理解,正确的揭示我们的设计意图。其实abstract class表示的是"is a"关系,interface表示的是"like a"关系,大家在选择时可以作为一个依据,当然这是建立在对问题领域的理解上的,比如:如果我们认为AlarmDoor在概念本质上是报警器,同时又具有Door的功能,那么上述的定义方式就要反过来了。
转载人员-Nicholas
posted @
2010-11-07 13:57 obpm 阅读(552) |
评论 (4) |
编辑 收藏
可关闭的TabbedPane结构:
测试代码:
package cn.demo.test;
import java.awt.Component;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.UIManager;
/**
* Test
* @author Tom
*
*/
public class TestDemo {
public static void main(String[] args) {
try {
String feel = UIManager.getSystemLookAndFeelClassName();
UIManager.setLookAndFeel(feel);
} catch (Exception e) {
e.printStackTrace();
}
JFrame frame = new JFrame();
frame.setTitle("可关闭Tab测试");
frame.setSize(300, 400);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
TabbedPane tabbedPane = new TabbedPane();
tabbedPane.setCloseButtonEnabled(true);
tabbedPane.addTab("测试一", null, new JLabel("测试一"));
tabbedPane.addTab("测试二", null, new JLabel("测试二"));
tabbedPane.addTab("测试三", null, new JLabel("测试三"));
tabbedPane.addTab("测试四", null, new JLabel("测试四"));
tabbedPane.addTabbedPaneListener(new TabbedPaneListener(){
@Override
public void allTabsRemoved() {
// TODO Auto-generated method stub
}
@Override
public boolean canTabClose(Tab tab, Component component) {
// TODO Auto-generated method stub
return false;
}
@Override
public void tabAdded(Tab tab, Component component, int index) {
// TODO Auto-generated method stub
}
@Override
public void tabRemoved(Tab tab, Component component, int index) {
// TODO Auto-generated method stub
System.out.println("close");
}
@Override
public void tabSelected(Tab tab, Component component, int index) {
// TODO Auto-generated method stub
}
});
frame.add(tabbedPane);
frame.setVisible(true);
}
}
测试效果:
源码下载:TabbedPane.rar
发表人: Tom
posted @
2010-10-24 16:44 obpm 阅读(6115) |
评论 (2) |
编辑 收藏
在数据库层使用SQL分页可以很大程度增加平台系统程序运行速度与效率。本人只是初入手半数据库半程序的开发,所以对数据库研究不深。于是要收集下列代码以作参考,同时也Post出来可以让需要的人参考一下,高手就请见笑了。说转载说不上,说原创也... 国庆期间在家家里的机子跑DB比较困难,做不了过多测试。不过语句或函数方面本人都仔细看过抄回来的SQL的网页里示例,感觉基本上大同小异,如果有错误查下做相应的修改或百度Google一下应该没什么大问题,也请多多包涵。当然,有机会就会对下列SQL做测试,然后会进行修正。至于每个数据库分页在这就不进行深究,只是列出个可用的方法。
##########
# MySQL#
##########
select * from tlk_buginfo limit startPos, pageSize
startPos: 定义当前页起始位置(不包括startPos)
pageSize: 每页显示数据的条数
##########
# MSSQL#(2005的row_number,暂无2000)
##########
1、
--返回第20-40行数据
select top 20 * from (select row_number() over (order by EmployeeID) as RowNumber, * from HumanResources.Employee) TableNickname where RowNumber>=20
2、
--返回第20-40行数据
select * from (select row_number() over (order by EmployeeID) as RowNumber, * from HumanResources.Employee) TableNickname where RowNumber between 20 and 40
3、
--返回第20-40行数据
with OrderedResults as
(select *, ROW_NUMBER() OVER (order by EmployeeID) as RowNumber FROM HumanResources.Employee)
select * from OrderedResults where RowNumber between 20 and 40
##########
# Oracle#
##########
①采用rownum关键字(三层嵌套)
--返回第5-15行数据
select * from (select row_.*, rownum num from (select * from tlk_buginfo) row_ where rownum<=15) where num>=5
②采用row_number解析函数进行分页(效率更高)
--返回第5-15行数据
select tab.* from (select t.*, row_number() over (order by lastmodified) as num from tlk_buginfo t) tab where num between 5 and 15
##########
# DB2#
##########
select * from (select *, rownumber() over(order by 排序字段 asc ) as rowid from 表名 )as a where a.rowid >= startPage AND a.rowid <endPage
##########
# Hsqldb#
##########
select LIMIT 0 10 表名
收集资料:(allen)
posted @
2010-10-10 21:32 obpm 阅读(383) |
评论 (1) |
编辑 收藏
(denny)
posted @
2010-10-08 00:11 obpm 阅读(1457) |
评论 (1) |
编辑 收藏
HTML5 是近十年来 Web 标准最巨大的飞跃。和以前的版本不同,HTML 5 并非仅仅用来表示 Web 内容,它的使命是将 Web
带入一个成熟的应用平台,在这个平台上,视频,音频,图象,动画,以及同电脑的交互都被标准化。尽管 HTML 5 的实现还有很长的路要走,但 HTML 5 正在改变
Web。
HTML 最近的一次升级是1999年12月发布的 HTML
4.01。自那以后,发生了很多事。最初的浏览器战争已经结束,Netscape 灰飞烟灭,IE5 作为赢家后来又发展到 IE6, IE7到IE8。Mozilla
Firefox 从 Netscape 的死灰中诞生,并跃居第二位。苹果和 Google 各自推出自己的浏览器,而小家碧玉的 Opera
仍然嘤嘤嗡嗡地活着,并以推动 Web 标准为己命。我们甚至在手机和游戏机上有了真正的 Web 体验,感谢 Opera,iPhone 以及 Google
已经推出的 Android。
然而这一切,仅仅让 Web 标准运动变得更加混乱,HTML 5 和其它标准被束之高阁,结果,HTML 5
一直以来都是以草案的面目示人。
于是,一些公司联合起来,成立了一个叫做 Web Hypertext Application
Technology Working Group (Web 超文本应用技术工作组 - WHATWG) 的组织,他们将重新拣起 HTML 5。这个组织独立于
W3C,成员来自 Mozilla, KHTML/Webkit 项目组,Google,Apple,Opera 以及微软。尽管 HTML 5
草案不会在短期内获得认可,但 HTML 5 总算得以延续。
HTML 5 将带来什么?以下是 HTML 5 草案中最激动人心的部分:
全新的,更合理的 Tag,多媒体对象将不再全部绑定在 object 或 embed Tag
中,而是视频有视频的 Tag,音频有音频的 Tag。本地数据库。这个功能将内嵌一个本地的 SQL 数据库,以加速交互式搜索,缓存以及索引功能。同时,那些离线
Web 程序也将因此获益匪浅。不需要插件的富动画。Canvas 对象将给浏览器带来直接在上面绘制矢量图的能力,这意味着我们可以脱离 Flash 和
Silverlight,直接在浏览器中显示图形或动画。一些最新的浏览器,除了 IE,已经开始支持 Canvas。浏览器中的真正程序。将提供 API
实现浏览器内的编辑,拖放,以及各种图形用户界面的能力。内容修饰 Tag 将被剔除,而使用 CSS。理论上讲,HTML 5 是培育新 Web
标准的土壤,让各种设想在他的组织者之间分享,但 HTML 5 目前仍处于试验阶段。
Mozilla 的技术副总裁 Mike Shaver 说,HTML 5 是一个被寄予厚望的概念,它既是
WHATWG 组织的实验田,又是 W3C 的标准之路。
Shaver 认为,Mozilla 的兴趣和 WHATWG 实验相吻合,Mozilla 在 HTML 5
工作组中非常活跃,我们对一些早期的细则进行实验并将成熟的结果提交 W3C。
在过去的几年,Mozilla 随着各种出现的新标准,推出多个富有前瞻性的项目,包括
Prism,一个用于离线运行 Web 程序的系统,以及 Weave,一个数据存储框架。
Shaver 说,HTML 5 运动肇始于对 W3C 的不耐烦,Web 标准中的很多进展都因 W3C
将重点从 HTML 转移到 XML 而停滞不前。
很多基于 XML 架构的新技术被设计出来替代 HTML,Shaver
说,这不是一条正确的道路,人们不应象黑瞎子掰玉米把样一边掰一边丢。
HTML 5 的新实验在 Firefox 以及 基于 Webkit 的 Safari 和 Chrome
浏览器中逐渐得到强化,但仍有不少问题。
Chrome 的开发者 Darin Fisher 说,Chrome
仍在襁褓中时,就不得不面临几个问题,尽管使用的是最新的 Webkit,HTML 5 的本地数据库功能在 Chrome 的初期版本中并没有实现。因为 Chrome
的沙箱机制和 Webkit 的数据库功能有冲突。
而由于 Chrome 属于秘密开发,Chrome 的开发人员也不便参与 Webkit 的开发。
我们要想保守 Chrome 的秘密,就无法参与 Webkit 社区。Fisher
说,我们很希望可以在某些方面给 Webkit 以帮助,我们拥有众多经验丰富的开发者,我们很想知道人们目前遇到的挑战并乐意提供帮助。
随着 Chrome 的发布,Fisher 说他的团队成员有时会和 Webkit
的人一起吃饭,有些人私下里还成了好朋友。Fisher 称,他们迫切地想同其他 Webkit 开发组一起工作解决离线数据库的问题。
Chrome 里面还包含Google 的开源 Gears 技术,用来实现与 HTML 5 类似的离线功能。
Gears 可以看作已有 API 的替代品,Fisher 说,HTML 5
对新浏览器来说是非常好的东西,但绝大多数用户还使用旧浏览器。Gears 可以让那些旧浏览器也获得这样的 API,我们正在为 HTML 5 版 API 提供兼容。
Gears 兼容性非常好,它正成为将 HTML 5 带向人们桌面的另外一条途径。
目前,绝大多数工作由 Apple,Mozilla, Opera, Google 以及 Trolltech
展开。微软在干什么?IE 因其对 Web 标准的迟钝而闻名,更不要说 HTML 5。但 IE8 可能会做出改变。
微软 IE 平台与 WHAT 工作组主席 Chris Wilson
在邮件中称,我们希望我们现在开始的工作可以在 HTML 工作组创建一套测试系统。Wilson 说,IE 开发组仍然对 HTML 5
的一些提议感到担忧。我觉得工作组的所有成员都会承认我们还有很多事要做。
目前处于 Beta 版的 IE9,已经包含 HTML 5
的诸多新功能。它拥有一个跨文档消息系统,本地存储,以及一些离线事件来检测网络的中断。但还有些功能还未提上议程,如 Canvas。
HTML 5
非常庞大,仍处在开发阶段,我认为浏览器厂商应当尽快达成一致,而每个浏览器的具体实现时间可以自己选择。Web 开发者和浏览器厂商会同意 Wilson
的下面这句话,这确切无疑是一个激动人心的时刻,我们希望看到 Web 成为新的应用平台。
HTML5写的例子(IE9或google浏览器才有效果):
HTML5学习资料:
http://www.chinabyte.com/bang/html5/
收集资料:(denny)
posted @
2010-10-07 21:46 obpm 阅读(2102) |
评论 (1) |
编辑 收藏
首先,在jbpm4中,流程定义相关的部署信息就存在
JBPM4_DEPLOYMENT、
JBPM4_DEPLOYPROP及
JBPM4_LOB (存放当发布一个png和xml文件后的流程定义后的记录)。中。
JBPM4_HIST_PROCINST、
JBPM4_HIST_ACTINST两张表中,分别存放的是process Instance、Activity Instance的历史记录,Activity Instance是指流程定义中各个步骤:task descition等存放Process Instance、Activity Instance历史记录的表有了,那他们的当前记录存在什么地方呢?这就需要弄清楚jBPM的另外几个概念。一般而言,在jBPM中,“a process instance is the root of a tree of executions”。因此,当一个流程实例Split出两个并行步骤的时候,在
JBPM4_EXECUTION表中将有三笔相关记录,一笔是代表流程实例的Root Execution,另外两笔是关于上述两个并行步骤的Child Execution。
此外,在jbpm中,Activity的种类是很丰富的,可以是Control Flow Activities,如sub-process,decision等,也可以是Automatic Activity,如java、script、sql等,其中需要人来参与完成的Activity被称为Task,待办任务放在
JBPM4_TASK表中,而历史任务放在
JBPM4_HIST_TASK表中。
对一个Task而言,它可能会有多个Participation(swim lane 同样会有多个Participation),Participation的种类有Candidate、client、owner、Replaced Assignee和viewer,而具体的Participation既可以是单一用户,也可以是用户组,Participation的信息存放在
JBPM4_PARTICIPATION中。
Swim Lane是一种Runtime Process Role,通过Swim Lane,多个Task可以一次分配到同一Actor身上,存放这些信息是表
JBPM4_PARTICIPATION。
JBPM4_ID_GROUP、
JBPM4_ID_MEMBERSHIP、
JBPM4_ID_USER这是基本的权限控制,建议关于用户认证方面还是自己开发一套,这个功能太简单了,难以满足需求。
JBPM4_JOB存放的是Timer的定义。
JBPM4_PROPERTY这是jbpm引擎参数表。
JBPM4_VAR表存放流程临时变量,当流程实例结束后,表中内容清除。
JBPM4_HIST_VAR表存放历史临时变量,但是jbpm4好像还没有对这张表进行利用。
JBPM4_HIST_DETAIL表保存变量变更记录。
了解jbpm4.3以上这18张表后,我们应该在流程运行中,详细观察jbpm是如何对这些表进行操作,以及进行什么样的操作的。
发布一个流程定义后:
JBPM4_DEPLOYMENT新增一条记录
JBPM4_DEPLOYPROP新增三条记录
JBPM4_LOB新增两条记录
开始一个流程startProcessInstanceByKey后:
JBPM4_EXECUTION新增一条记录
JBPM4_TASK新增一条记录
JBPM4_HIST_PROCINST、
JBPM4_HIST_ACTINST分别新增一条记录
JBPM4_HIST_TASK新增一条记录
当执行taskService.setVariables(task.getId(), map);时,
JBPM4_VARIABLES中添加变量记录
转载人员:Nicholas
posted @
2010-09-16 19:51 obpm 阅读(1240) |
评论 (0) |
编辑 收藏
权限分析文档
基于RBAC的权限设计模型:
1 RBAC 介绍
RBAC 模型作为目前最为广泛接受的权限模型。
NIST (The National Institute of Standards and Technology,美国国家标准与技术研究院)标准RBAC模型由4个部件模型组成,这4个部件模型分别是基本模型RBAC0(Core RBAC)、角色分级模型RBAC1(Hierarchal RBAC)、角色限制模型RBAC2(Constraint RBAC)和统一模型RBAC3(Combines RBAC)[1]。RBAC0模型如图1所示。
图表 1 RBAC 0 模型
l RBAC0 定义了能构成一个RBAC控制系统的最小的元素集合
在RBAC之中,包含用户users(USERS)、角色roles(ROLES)、目标objects(OBS)、操作operations(OPS)、许可权permissions(PRMS)五个基本数据元素,权限被赋予角色,而不是用户,当一个角色被指定给一个用户时,此用户就拥有了该角色所包含的权限。会话sessions是用户与激活的角色集合之间的映射。RBAC0与传统访问控制的差别在于增加一层间接性带来了灵活性,RBAC1、RBAC2、RBAC3都是先后在RBAC0上的扩展。
l RBAC1 引入角色间的继承关系
角色间的继承关系可分为一般继承关系和受限继承关系。一般继承关系仅要求角色继承关系是一个绝对偏序关系,允许角色间的多继承。而受限继承关系则进一步要求角色继承关系是一个树结构。
l RBAC2 模型中添加了责任分离关系
RBAC2 的约束规定了权限被赋予角色时,或角色被赋予用户时,以及当用户在某一时刻激活一个角色时所应遵循的强制性规则。责任分离包括静态责任分离和动态责任分离。约束与用户-角色-权限关系一起决定了RBAC2模型中用户的访问许可。
l RBAC3 包含了RBAC1和RBAC2
既提供了角色间的继承关系,又提供了责任分离关系。
建立角色定义表。定出当前系统中角色。
因为有继承的问题,所以角色体现出的是一个树形结构。
2 权限设计:
配置资源以及资源的操作 : 这里资源可以定义为一个通用的资源模型。提供通用的资源统一接口。
数据库 ER 图:
关系图:
3 分析:
根据以上的类关系图和ER图可以看出。整个权限可以抽象为五个对象组成。
OrgBean : 用于描述org模型。
Role : 用于描述角色。
Permission : 用于描述权限。
Resource : 用于描述资源。
Operation : 用于描述操作。
其中Permission中有Resource , Operation 的聚合,资源和操作组成权限。
Role 和 Permission 都有自包含。因为设计到权限的继承。
资源Resource 也可能出现一颗树形结构,那资源也要有自包含。
思想 :
权限系统的核心由以下三部分构成: 1. 创造权限, 2. 分配权限, 3. 使用权限,然后,系统各部分的主要参与者对照如下: 1. 创造权限 -Creator 创造, 2. 分配权限 - Administrator 分配, 3. 使用权限 - User :
1. Creator 创造 Privilege , Creator 在设计和实现系统时会划分,一个子系统或称为模块,应该有哪些权限。这里完成的是 Privilege 与Resource 的对象声明,并没有真正将 Privilege 与具体 Resource 实例联系在一起,形成 Operator 。
2. Administrator 指定 Privilege 与 Resource Instance 的关联 。在这一步, 权限真正与资源实例联系到了一起, 产生了 Operator (Privilege Instance )。 Administrator 利用 Operator 这个基本元素,来创造他理想中的权限模型。如,创建角色,创建用户组,给用户组分配用户,将用户组与角色关联等等 ... 这些操作都是由 Administrator 来完成的。
3. User 使用 Administrator 分配给的权限去使用各个子系统。 Administrator 是用户,在他的心目中有一个比较适合他管理和维护的权限模型。于是,程序员只要回答一个问题,就是什么权限可以访问什么资源,也就是前面说的 Operator 。程序员提供 Operator 就意味着给系统穿上了盔甲。 Administrator 就可以按照他的意愿来建立他所希望的权限框架 可以自行增加,删除,管理 Resource 和 Privilege 之间关系。可以自行设定用户 User 和角色 Role 的对应关系。 ( 如果将 Creator 看作是 Basic 的发明者, Administrator 就是 Basic 的使用者,他可以做一些脚本式的编程 ) Operator 是这个系统中最关键的部分,它是一个纽带,一个系在 Programmer , Administrator , User 之间的纽带。
4 权限API
getPermissionByOrgGuid(String orgGuid )
通过传入一个org的Guid , 拿到当前这个org对象都具有那些访问权限。
getSourcePermissionByOrgGuid(String orgGuid , String resouceGuid)
通过传入一个org的Guid 和 一个资源的Guid , 返回改Org对当前这个资源的访问权限。
getPermissionByResourceGuid(String resource)
通过传入一个资源的Guid , 得到当前资源下都有那些权限定义。
havingHeritPermission(String orgGuid , String resouceGuid) : Boolean
传入一个orgGuid, 资源GUID ,查看改OrgGuid下对资源是否有向下继承的权限。这里继承是资源的继承。即对父栏目有权限,可以继承下去对父栏目下的子栏目同样有权限。
havingPermission(String orgGuid , String resourceGuid) : Boolean
判断某Org对某一资源是否用权限。
以上是粗粒度的权限API 。 以下为细粒度的权限:
getOperationByPermission(String permissionGuid)
通过permission 的Guid 得到该permission 的所有有效操作。
getOperationByGuid(String permissionGuid , String resourceGuid)
通过permision的Guid , 资源的Guid 得到该资源下所有的有效操作。
screeningOpreationByGuid (String permissionGuid , String resourceGuid , String orgGuid)
通过permission , resource , org的Guid 得到改Org对这一资源的有效操作。
hasOperation(String operationGuid) : boolean
通过传入的operationGuid 返回是否具有操作权限。
5 权限的实现:
1 .表单式认证,这是常用的,但用户到达一个不被授权访问的资源时, Web 容器就发
出一个 html 页面,要求输入用户名和密码。
2 .用 Filter 防止用户访问一些未被授权的资源, Filter 会截取所有 Request/Response ,
然后放置一个验证通过的标识在用户的 Session 中,然后 Filter 每次依靠这个标识来决定是否放行 Response 。
这个模式分为:
Gatekeeper :采取 Filter 或统一 Servlet 的方式。
Authenticator : 在 Web 中使用 JAAS 自己来实现。
Filter 拦截只是拦截该用户是否有访问这个页面,或这一资源的权限。真正做到显示后拦截是在应用程序内部去做。
做显示拦截提供API , 标签这两种方式
转载人员:Happy
原文地址 http://blog.csdn.net/huanghanzzz2006/archive/2006/12/04/1429666.aspx
posted @
2010-09-14 13:30 obpm 阅读(912) |
评论 (0) |
编辑 收藏
flex4帮助文档大小有34M(网页版),上传不了。需要该文档的,请留下你的邮箱地址。
115共享地址:
http://u.115.com/file/f8c22d4e48
flex4api.zip 提取码:f8c22d4e48
原创人员:denny
posted @
2010-09-03 08:50 obpm 阅读(4566) |
评论 (104) |
编辑 收藏
图片剪切功能:
效果图:
flex代码:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" initialize="init()" xmlns:local="astion.*">
<mx:Script>
<![CDATA[
import mx.controls.Image;
import mx.graphics.ImageSnapshot;
import flash.net.FileReference;
import mx.graphics.codec.JPEGEncoder;
import mx.managers.PopUpManager;
import mx.containers.TitleWindow;
import mx.controls.Alert;
import mx.events.CloseEvent;
import mx.core.IFlexDisplayObject;
import mx.utils.*;
import mx.core.Application;
import astion.Dot;
import astion.ScaleBox;
public static const LINE_WIDTH:Number = 1;//缩放边框宽度
private var file:FileReference;
public var IMAGE_URL:String="http://localhost:8080/cutPicuter/aa/aa.jpg";
private var loader:Loader;
private var bmp:Bitmap;
private var stream:URLStream;
public var realPath:String="D:\myWorkSpace\cutPicuter\WebRoot\aa\aa.jpg";
//初始化数据
private function init():void{
this.loader = new Loader();
this.stream = new URLStream();
this.loader.contentLoaderInfo.addEventListener(Event.COMPLETE,this.onComplete);
this.loader.load(new URLRequest(encodeURI(this.IMAGE_URL)));//解决中文乱码
this.stream.load(new URLRequest(encodeURI(this.IMAGE_URL)));
this.stream.addEventListener(Event.COMPLETE,this.onLoaded);
}
private function onLoaded(e:Event):void
{
var bytearray:ByteArray = new ByteArray();
this.stream.readBytes(bytearray);
if(this.stream.connected)
this.stream.close();
this.loader.loadBytes(bytearray);
}
private function onComplete(e:Event):void
{
try
{
this.bmp = this.loader.content as Bitmap;
var showImage:Image= new Image();
showImage.source=this.loader.content;
canvas.addChild(showImage);
canvas.setChildIndex(box,1);
canvas.setChildIndex(showImage,0);
}
catch(e:Error)
{
}
}
//截图,显示缩放选择框
private function doCapture():void{
box.x = 100;
box.y = 100;
box.visible = true;
}
//获取缩放选择框内的图像
private function getImg():BitmapData{
//截取整个区域
box.scaleEnable = false;
var bmp:BitmapData = ImageSnapshot.captureBitmapData(canvas);
box.scaleEnable = true;
//矩形为要截取区域
var re:Rectangle = new Rectangle(box.x+LINE_WIDTH,box.y+LINE_WIDTH,box.boxWidth-LINE_WIDTH,box.boxHeight-LINE_WIDTH);
var bytearray:ByteArray = new ByteArray();
//截取出所选区域的像素集合
bytearray = bmp.getPixels(re);
var imgBD:BitmapData = new BitmapData(box.boxWidth-LINE_WIDTH,box.boxHeight-LINE_WIDTH);
//当前的bytearray.position为最大长度,要设为从0开始读取
bytearray.position=0;
var fillre:Rectangle = new Rectangle(0,0,box.boxWidth-LINE_WIDTH,box.boxHeight-LINE_WIDTH);
//将截取出的像素集合存在新的bitmapdata里,大小和截取区域一样
imgBD.setPixels(fillre,bytearray);
return imgBD;
}
//预览图片
private function doScan():void{
var t:TitleWindow = new TitleWindow();
t.showCloseButton=true;
t.addEventListener(CloseEvent.CLOSE,closeWindow);
t.width = box.boxWidth+t.getStyle("borderThickness");
t.height =box.boxHeight+t.getStyle("borderThickness")+t.getStyle("headerHeight");
var img:Image = new Image();
img.width = box.boxWidth;
img.height = box.boxHeight;
img.source = new Bitmap(getImg());
t.addChild(img);
PopUpManager.addPopUp(t,this,true);
PopUpManager.centerPopUp(t);
}
private function closeWindow(e:CloseEvent):void{
var t:TitleWindow = e.currentTarget as TitleWindow;
PopUpManager.removePopUp(t);
}
//保存图片到本地
private function downloadPicture():void{
file=new FileReference();
file.addEventListener(Event.COMPLETE,downloadComplete);
file.save(new JPEGEncoder(80).encode(getImg()),"default.jpg");
}
private function downloadComplete(event:Event):void{
Alert.show("成功保存图片到本地!","提示");
}
//保存图片到服务器即覆盖原来的图片
private function save():void{
Alert.show("是否保存剪切图片?","提示",3, this, function(event:CloseEvent):void {
if (event.detail==Alert.YES){
var request:URLRequest = new URLRequest("http://localhost:8080/cutPicuter/servlet/FileManagerSaveFileServlet?realPath="+encodeURIComponent(StringUtil.trim(realPath)));
request.method=URLRequestMethod.POST;
request.contentType = "application/octet-stream";
request.data = new JPEGEncoder(80).encode(getImg());
var loader:URLLoader = new URLLoader();
loader.load(request);
loader.addEventListener(Event.COMPLETE,saveResult);
}});
}
private function saveResult(event:Event):void{
Application.application.reLoadFolderFiles(realPath.substr(0,realPath.lastIndexOf("\\")));
Alert.show("保存剪切图片成功","提示");
}
]]>
</mx:Script>
<mx:HBox x="0" y="0">
<mx:LinkButton label="剪裁" click="doCapture();" icon="@Embed('assets/cut.png')"/>
<mx:LinkButton label="预览" click="doScan();" icon="@Embed('assets/ok.png')"/>
<mx:VRule height="22"/>
<mx:LinkButton label="保存" click="save()" icon="@Embed('assets/save.png')"/>
<mx:LinkButton label="另存为" click="downloadPicture();" icon="@Embed('assets/saveAs.png')"/>
</mx:HBox>
<mx:Canvas id="canvas" y="23" x="1">
<local:ScaleBox id="box" visible="false" y="0" x="0" width="100" height="100"/>
</mx:Canvas>
</mx:Application>
java代码:
package com;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class FileManagerSaveFileServlet
*/
public class FileManagerSaveFileServlet extends HttpServlet {
private int len=0;//处理流
private int mm=0;//重命名
private String fileName="";//文件原名
private String extName="";//文件扩展名
private String tempFileName="";//文件名加扩展名
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
public void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
String realPath=request.getParameter("realPath");
//System.out.println("FMSFS-->realPath:"+realPath);
response.setContentType("application/octet-stream");
InputStream is = request.getInputStream();
try {
int size = 0;
byte[] tmp = new byte[100000];
tempFileName=realPath.substring(realPath.lastIndexOf("\\")+1);//切割获得文件名加扩展名
fileName=tempFileName.substring(0,tempFileName.lastIndexOf("."));//切割获得文件名
//确保获得真实的文件名如:1(1)可以获得真实为1,
if(fileName.indexOf("(")!=-1){
fileName=fileName.substring(0,fileName.indexOf("("));
}
extName=tempFileName.substring(tempFileName.lastIndexOf("."));//切割获得扩展名
//调用递归方法
fileName+=reNameFile(realPath.substring(0,realPath.lastIndexOf("\\")+1),fileName,extName);
// 创建一个文件夹用来保存发过来的图片;
File f = new File(realPath.substring(0,realPath.lastIndexOf("\\")+1)+fileName+extName);
DataOutputStream dos = new DataOutputStream(new FileOutputStream(f));
while ((len = is.read(tmp)) != -1) {
dos.write(tmp, 0, len);
size += len;
}
dos.flush();
dos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//递归来重命名文件名
String str="";
public String reNameFile(String realPath,String filename,String extName){
File file =new File(realPath+"\\"+filename+extName);
str="";
if(file.exists()){
mm++;
str="_"+mm;
reNameFile(realPath,fileName+str,extName);
}else{
if(mm!=0){
str="_"+mm;
}
}
return str;
}
}
源码: flex图片剪切示例
原创人员:Denny
posted @
2010-09-01 09:55 obpm 阅读(8590) |
评论 (6) |
编辑 收藏
James Gosling : Java之父
文/陶文
作为Java之父,James Gosling的名字可谓是耳熟能详。当人们评论一种编程语言时,总喜欢捎带着把下蛋的母鸡一起带上。Java做为中国的编程语言学习者餐桌上有限的那么几样餐点中的流行款式,自然是让James Gosling风光不已。虽然James Gosling现在已经不是领导Java发展潮流的领军人物了,做为Sun的开发者产品组的CTO,怎么算来也是身居高位了,俗事缠身吧,但是这并不妨碍其对于Java一如既往的爱护,表达着各式各样鲜明的观点,引发一场又一场的争论。
James Gosling是很爱Java的——是啊,哪有当父母的不爱自己的孩子的呢。James Gosling也是很爱Sun的——是啊,哪有当领导的不爱自己的公司的呢。于是我们在批评.NET的安全性的队伍前头,在褒扬Java性能的队伍前头,在抨击SWT开倒车的队伍前头,在给NetBeans大唱赞歌的队伍前头,我们都看到了James Gosling的身影。无论对错、偏见或者固执,至少说明了Gosling的鲜明个性丝毫没有受到年龄的影响。也许也只有这种天才而偏执的人物才能创造出Java这般伟大的语言来吧。
Bill Joy : 软件业的爱迪生
文/徐昊
Joy生于1954年,1982年与Vinod Khosla, Scott McNealy和Andy Bechtolsheim一起创建了Sun Microsystems,并从那时起担任首席科学家,直到2003年离开。他是一位令人崇敬的软件天才,他在软件和硬件的历史上留下了无数令人仰止的传奇。
在上个世纪80年代早期,DARPA与BBN达成协议,准备将Vinton Cerf和Bob Kahn设计的TCP/IP协议添加到Berkeley UNIX中。Bill Joy被委派来完成这项任务,然而他却拒绝将BBN的TCP/IP协议栈添加到BSD中,因为在他的眼中BBN的TCP/IP实现还远不够好,于是他就写了一个高性能的TCP/IP协议栈。John Gage回忆道,“BBN和DARPA签署了巨额合同来实现TCP/IP协议,然而他们的员工所编写的代码远没有一个研究生所做的好。于是他们邀请Bill Joy参加他们的一个会议,这位研究生穿着一件T-Shirt就出现了,他们询问他,‘你是如何做到的呢?’Bill回答说,‘这是非常简单的一件事,你读一下协议然后就可以编码了’”。除了TCP/IP协议,基于分页的虚拟内存系统最早也是由Bill Joy添加到Berkeley UNIX内核当中的。同时他还是vi、csh、早期Pascal编译器的作者。
关于Bill Joy惊人的软件才能流传最广的一个传奇是,据说他在上研究生的时候,想看看自己能不能写一个操作系统出来,于是就在三天里写了一个非常简陋,但是可以使用的Unix系统, 传说就是BSD的前身。虽然如此夸张的才情令人难以置信,但是考虑到主角是Bill Joy,还是有一定的可信度的。Bill Joy硕士毕业之后,决定到工业界发展,于是就到了当时只有一间办公室的Sun, 他作为主要设计者参与了SPARC微处理器的设计,负责设计最为关键的一部分电路。这样兼精软硬件的天才实在是让人不得不佩服啊。1995年,Sun发布了轰动世界的Java语言。当然,Bill Joy对Java也作出了不少的贡献,首先是JINI——一种针对分布式服务的基础连接技术。任何可以内嵌JVM的电子设备都可以通过JINI相互连接;JXTA是基于Java的P2P协议,允许互联网上的软件进行点对点交流和协作。
这个其貌不扬的瘦高个,有着凌乱的亚麻色头发,被《财富》杂志誉为“网络时代的爱迪生”的技术狂人,在短短的二十年间,创造了无数令人心动的软件。在MIT的BBS上曾有一个帖子,说微软电话面试有一道题,问“Who do you think is the best coder, and why?”虽然回复的帖子中大家都声明列举的best coder排名不分先后,然而大多数人仍把Bill Joy列在第一位,或许可以从一个侧面验证Bill Joy在广大Programmer心目中的地位吧。
Joshua Bloch : Java 2 元勋
早在1996年,适逢Java刚刚崭露头角,年内好事连连。先是1月份发布JDK 1.0,然后是5月底在旧金山召开首届JavaOne大会,年末又是JDK 1.1紧跟其后。正是在Java技术如火如荼、大展拳脚的背景之下,Joshua Bloch来到了Sun,开始了他带领Java社区步入“迦南美地”的漫长历程。
很快,他被从安全组调入核心平台组,从事底层API设计。至此以后,每逢JDK的重大版本发布,总能在其中见到Joshua的“妙笔”。JDK 1.1中的java.math、1.4中的assertions,还有大家所熟识的Collections Framework皆是Joshua一手打造。其中的Collections Framework还获得了当年的Jolt大奖。到了J2SE 5.0研发阶段,身为平台组构架师的Joshua接掌了Tiger大旗,其核心地位已然无人可以替代。作为Tiger的代言人和领路人,没有谁比Joshua更清楚Tiger。相信大家一定还记得Joshua当年仿效英国诗人William Blake所做的咏Tiger诗八首,优雅的笔调,透出大师深厚底蕴的同时,也道出了Tiger的几大重要特性,这些特性是自JDK 1.1引入Inner Class以来,Java最大的语法改进。
Java风雨十年,从JDK 1.1到J2SE 5.0,Joshua实在功不可没。难怪有人戏言,假如将James Gosling比作Java之父,那么Joshua就是一手将Java “哺育”成人的Java之母。Joshua对Java的贡献还不止于JDK,提起他的大作《Effective Java》(Addison Wesley, 2001),相信Java粉丝们一定耳熟能详。该书荣膺2002年度Jolt大奖,且备受James Gosling推崇。书中57条颇具实用价值的经验规则,来自Joshua多年来在JDK开发工作中,尤其是Collections Framework设计中的实践心得,各个有理有据,剖析深入,也足见其深厚功力。该书对Java社群的影响,犹如C++社群中的《Effective C++》。Joshua对JCP的贡献也不小。他是JSR201和JSR175的领导者,前者包含了Tiger四大语言特性,后者则为Java提供了元数据支持。此外,他还是JSR166的发起人之一(该JSR由Doug Lea领导),并且是许多其他JSR的参与者。Joshua目前是JCP为数不多的几个执行委员会成员之一。
Joshua Bloch给人的印象是谦逊平和,行事低调而不喜抛头露面,一个典型的技术人员和实干家。不过即便如此,也丝毫不会减弱他对Java技术的卓越贡献和对Java社区的绝对影响力。有人说,如果他能更彰显一些,就很有可能成为Java开发者中的领军人物,就有如Don Box之于微软社群。
2004年7月初,就在Tiger发布在即之时,就在Jusha Bloch刚刚荣获Sun“杰出工程师(Distinguished Engineer)”的称号之时,他突然离开Sun而去了正值发展态势迅猛的Google。当他离开Sun的消息在TSS发布之后,众多拥趸表达了怀念与不舍之情。一年过去了,我们还没有获知Joshua的任何近闻,似乎又是他行事低调的一贯作风所致,不知他在Google状况如何。希望Joshua依然能继续“摩西未尽的事业”,以他的影响力推动Java社群继续前行。据称,《Effective Java》的下一版会加入Java 5.0的部分,让我们翘首以待吧。
Bruce Eckel : 功勋卓著的机会主义分子
Bruce Eckel原本是一位普通的汇编程序员。不知道是什么因缘际会,他转行去写计算机技术图书,却在此大红大紫。他成功的秘诀不外乎两点:超人的表达能力和捕捉机会的能力。他最早的一本书是1990年代初期的《C++ Inside & Out》,随后,在1995年他写出了改变自己命运的《Thinking in C++》。如果说这本书充分表现了他作为优秀技术作家的一面,那么随后他写作《Thinking in Java》并因此步入顶级技术作家行列,则体现了他作为优秀的机会主义分子善于捕捉机会的另一面。写作中擅长举浅显直接的小例子来说明问题,语言生动,娓娓道来,特别适合于缺乏实践经验的初学者。因此《Thinking in Java》俨然成为天字第一号的Java教科书,对Java的普及与发展发挥着不可忽略的作用。不过公允地说,Bruce Eckel的书欠深刻。比如在“Thinking in…”系列中对设计模式的解说就有失大师水准。这一方面是因为书的定位非常清晰,另一方面也是因为Bruce太过分心赶潮流,未能深入之故。TIJ之后,他预言Python将火,就匆匆跑去写了半本《Thinking in Python》。后来Python并未如期而旺,于是他也就把书稿撂在那里不过问了,机会主义的一面暴露无遗。我们也可以善意的猜测一下,他的下一个投机对象会是什么呢?Ruby?.NET?MDA?总之,是什么我都不奇怪。
Rickard Oberg :J2EE奇才
Oberg的作品很多,流行的代码生成工具XDoclet和MVC框架WebWork都出自他的手笔。这两个框架有一个共同的特点,即它们的功能虽然简单,但设计都非常优雅灵活,能够很方便地扩展新功能甚至移植到新环境下使用。优雅的设计源自Oberg的过人才华,简单的功能则折射出他玩世不恭的人生态度。正是这两种特质的融合,才造就了这个不世出的奇才。
1999年,JDK 1.3发布,其中带来了一个重要的新特性:动态代理(Dynamic Proxy)。当所有人都还在对这项新技术的用途感到迷惑时,Oberg发现用它便可以轻松攻克EJB容器实现中的一些难关。这一发现的产物就是一本《Mastering RMI》,以及大名鼎鼎的JBoss应用服务器。但Oberg很快又让世人见识了他的玩世不恭。由于和总经理Marc Fleury在经营理念上不合,Oberg抱怨“法国的天空总让我感到压抑”,甩手离开了自己一手打造的JBoss。此后的几年里,他和老友Hani Suleiman不断地对JBoss的“专业开源”模式和Marc Fleury的商人味道冷嘲热讽,让众人为他的孩子气扼腕叹息。
2002年10月,微软推出Petstore示例应用的.NET版本,并宣称其性能比Java Petstore高出数倍。正是Oberg深入分析这个示例应用的源代码,在第一时间指出它大量运用了SQL Server专有的特性,性能对比根本不具参考价值。后来Oberg又先后关注了AOP和IoC容器,两者都成为了J2EE架构的新宠。
Doug Lea : 世界上对Java影响力最大的个人
文/KIT
如果IT的历史,是以人为主体串接起来的话,那么肯定少不了Doug Lea。这个鼻梁挂着眼镜,留着德王威廉二世的胡子,脸上永远挂着谦逊腼腆笑容,服务于纽约州立大学Oswego分校计算器科学系的老大爷。
说他是这个世界上对Java影响力最大的个人,一点也不为过。因为两次Java历史上的大变革,他都间接或直接的扮演了举足轻重的脚色。一次是由JDK 1.1到JDK 1.2,JDK1.2很重要的一项新创举就是Collections,其Collection的概念可以说承袭自Doug Lea于1995年发布的第一个被广泛应用的collections;一次是2004年所推出的Tiger。Tiger广纳了15项JSRs(Java Specification Requests)的语法及标准,其中一项便是JSR-166。JSR-166是来自于Doug编写的util.concurrent包。
值得一提的是: Doug Lea也是JCP (Java小区项目)中的一员。
Doug是一个无私的人,他深知分享知识和分享苹果是不一样的,苹果会越分越少,而自己的知识并不会因为给了别人就减少了,知识的分享更能激荡出不一样的火花。《Effective JAVA》这本Java经典之作的作者Joshua Blosh便在书中特别感谢Doug是此书中许多构想的共鸣板,感谢Doug大方分享丰富而又宝贵的知识。这位并发编程的大师级人物的下一步,将会带给Java怎样的冲击,不禁令人屏息以待。
Scott McNealy :SUN十年来的掌舵者
文/KIT
McNealy,Sun的CEO、总裁兼董事长。他曾经狂傲的说:“摧毁微软是我们每个人的任务。”这位英勇的硅谷英雄,似乎带头起义,试图组织一个反微软阵线联盟,以对抗微软这股庞大的托拉斯恶势力。他时常口出惊人之语,在公开场合大肆的批评微软,并曾经说微软的.NET是.NOT。
Scott McNealy先后毕业于哈佛大学及史丹佛大学,分别持有经济学学士学位及企管硕士。1982年MBA毕业的他和三个同学共同合伙创建了Sun,并于1984年成为Sun的执行官。“要么吞了别人,不然就被别人吞了”是Scott McNealy的名言录之一。他擅长以信念带动员工,鼓舞士气。极富自信的他,对于认定的事,总是坚持自己的想法,因此有人形容他是一个刚愎自用的决策者。
身为Sun这艘船的掌舵者,Scott McNealy能够看多远,Sun就能走多远。Scott McNealy认为将来软件界是一个只有服务,没有产品的世代。他希望打造出Sun不是一个纯靠硬件赚钱的公司。从Open Source到Open Solaris,Sun希望可以成为提供整合性解决方案的服务厂商。Solaris 10 + UltraSPARC是否可以像Scott McNealy希望的是下一匹世纪黑马呢?Sun是否能以股价来证明华尔街分析师及普罗大众的诽短流长?Scott McNealy是否能带领着Sun成为继微软之后的下一个巨人,一场场IT界的争霸战值得我们拭目以待。
Rod Johnson : 用一本书改变了Java世界的人
Rod在悉尼大学不仅获得了计算机学位,同时还获得了音乐学位。更令人吃惊的是在回到软件开发领域之前,他还获得了音乐学的博士学位。有着相当丰富的C/C++技术背景的Rod早在1996年就开始了对Java服务器端技术的研究。他是一个在保险、电子商务和金融行业有着丰富经验的技术顾问,同时也是JSR-154(Servlet 2.4)和JDO 2.0的规范专家、JCP的积极成员。
真正引起了人们的注意的,是在2002年Rod Johnson根据多年经验撰写的《Expert One-on-One J2EE Design and Development》。其中对正统J2EE架构的臃肿、低效的质疑,引发了人们对正统J2EE的反思。这本书也体现了Rod Johnson对技术的态度,技术的选择应该基于实证或是自身的经验,而不是任何形式的偶像崇拜或者门户之见。正是这本书真正地改变了Java世界。基于这本书的代码,Rod Johnson创建了轻量级的容器Spring。Spring的出现,使得正统J2EE架构一统天下的局面被打破。基于Struts+Hibernate+Spring的J2EE架构也逐渐得到人们的认可,甚至在大型的项目架构中也逐渐开始应用。
Rod Johnson的新作《Expert One-on-one J2EE Development without JEB》则更让人吃惊,单单“Without EJB”一词就会让大多数J2EE架构师大跌眼镜了。不过Rod Johnson可能仅仅是想通过“Without EJB”一词表明应该放开门户之见。这也是Rod Johnson一贯的作风,。也许正是这种思想,促使得Rod Johnson创建了Spring,真正改变了Java世界。
Alan Kay :Java的精神先锋
Sun的官方Java教材中有一句话,说Java是“C++的语法与Smalltalk语义的结合”。而Smalltalk的创造者就是Alan Kay。
Alan Kay于1970年加入Xerox公司的Palo Alto研究中心。早在70年代初期,Alan Kay等人开发了世界上第二个面向对象语言Smalltalk,因此,Alan Kay被誉为Smalltalk之父。2003年,Alan Key因为在面向对象程序设计上的杰出贡献,获得了有计算机界的诺贝尔奖之称的ACM Turing Award。
Alan Kay成名于Smapltalk和OOP,而Java虽然在语言上类似于C,但是在语义上非常接近Smalltalk,很多Java中的设计思想在Alan Kay的文献中找到根源,也有些人将Alan Kay尊为Java思想的先驱。不过遗憾的是似乎Alan Kay老先生对Java并不买账,反倒攻击说Java是存在致命缺陷的编程语言,Java的成功不是由于Java本身的内在价值,而是其商业化的成功。Alan Kay欣赏的是Lisp,他认为Lisp是软件的麦克斯韦方程,其中的许多想法是软件工程和计算机科学的一部分。看来拥有Alan Kay这样一位重量级的Java先驱仍是我们Java一厢情愿的单恋吧。
Kent Beck : 领导的敏捷潮
Beck全家似乎都弥漫着技术的味道。生长在硅谷, 有着一个对无线电痴迷的祖父,以及一个电器工程师父亲。从小就引导Kent Beck成为了业余无线电爱好者。
在俄勒冈州大学读本科期间,Kent Beck就开始研究起模式。然而在他最终拿到计算机学位之前,他却是在计算机和音乐中交替学习。似乎Java大师都能够有这样的能耐,另一Java大牛Rod Johnson同样也拥有音乐学的博士学位。
Kent Beck一直倡导软件开发的模式定义。早在1993年,他就和Grady Booch(UML之父)发起了一个团队进行这个方面的研究。虽然著有了《Smalltalk Best Practice Patterns》一书,但这可能并不是Kent Beck最大的贡献。他于1996年在DaimlerChrysler启动的关于软件开发的项目,才真正地影响后来的软件开发。这次的杰作就是XP(极限编程)的方法学。
和软件开发大师Martin Fowler合著的《Planning Extreme Programming》可谓是关于XP的奠基之作。从此,一系列的作品如《Test Driven Development: By Example》,《Extreme Programming Explained: Embrace Change》让更多的人领略到了极限编程的精髓,也逐步导致了极限编程的流行。
Kent Beck的贡献远不仅如此。对于众多的Java程序员来说,他和Erich Gamma共同打造的JUnit,意义更加重大。也许正式这个简单而又强大的工具,让众多的程序员更加认可和信赖极限编程,从而引起了Java敏捷开发的狂潮吧。
|
转载人员:Nicholas
posted @
2010-08-30 11:27 obpm 阅读(192) |
评论 (0) |
编辑 收藏
功能:在线拍照
简介:用flex与java结合实现在线拍照
需求:为了满足希望通过摄像头拍照的图片,然后通过服务器来展示需要
效果:
后台:
前台:
实现代码:
flex:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="388" height="222" creationComplete="initApp()" backgroundColor="#A6C9E2">
<mx:Style>
Alert{font-size:12px;}
</mx:Style>
<mx:Script>
<![CDATA[
import mx.events.CloseEvent;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.controls.Alert;
import mx.core.Application;
private static const DEFAULT_CAMERA_WIDTH:Number = 160; //摄像头显示宽度
private static const DEFAULT_CAMERA_HEIGHT:Number = 120; //摄像头显示高度
private var DEFAULT_WEBSERVICE_URL:String = ""; //WebService地址
private var str:String;
private var m_camera:Camera; //定义一个摄像头
private var m_localVideo:Video; //定义一个本地视频
private var m_pictureBitmapData:BitmapData //定义视频截图
[Bindable]
private var m_pictureData:String;
private function initApp():void
{
t_btn_Shooting.enabled = false;
t_ban_Save.enabled = false;
initCamera();
DEFAULT_WEBSERVICE_URL = Application.application.parameters.contextPath+"/onLineTakePhotoServlet";
t_ws_SavePicture.url=DEFAULT_WEBSERVICE_URL;
}
//初始化摄像头
private function initCamera():void
{
m_camera = Camera.getCamera();
if(m_camera != null)
{
m_camera.addEventListener(StatusEvent.STATUS,__onCameraStatusHandler);
m_camera.setMode(DEFAULT_CAMERA_WIDTH,DEFAULT_CAMERA_HEIGHT,30);
m_localVideo = new Video();
m_localVideo.width = DEFAULT_CAMERA_WIDTH;
m_localVideo.height = DEFAULT_CAMERA_HEIGHT;
m_localVideo.attachCamera(m_camera);
t_vd_Video.addChild(m_localVideo);
}
else
{
Alert.show("没有找到摄像头,是否重新查找。","提示:",Alert.OK|Alert.NO,this,__InitCamera);
return;
}
}
//拍照按钮事件,进行视频截图
private function SnapshotPicture():void
{
m_pictureBitmapData = new BitmapData(DEFAULT_CAMERA_WIDTH,DEFAULT_CAMERA_HEIGHT);
m_pictureBitmapData.draw(t_vd_Video,new Matrix());
var m_pictureBitmap:Bitmap = new Bitmap(m_pictureBitmapData);
t_img_Picture.addChild(m_pictureBitmap);
t_panel_Picture.visible = true;
t_ban_Save.enabled = true;
}
//保存按钮事件,保存视频截图
//通过WebService保存
private function SavePicture():void
{
m_pictureData = "";
for(var i:int = 0; i < DEFAULT_CAMERA_WIDTH; i++)
{
for(var j:int = 0; j < DEFAULT_CAMERA_HEIGHT; j++)
{
if(m_pictureData.length > 0)
{
m_pictureData += "," + m_pictureBitmapData.getPixel32(i,j).toString();
}
else
{
m_pictureData = m_pictureBitmapData.getPixel32(i,j).toString();
}
}
}
var params:URLVariables = new URLVariables();
params.width = DEFAULT_CAMERA_WIDTH;
params.height = DEFAULT_CAMERA_HEIGHT;
params.bitmap_data = m_pictureData;
t_ws_SavePicture.send(params);
}
//检测摄像头权限事件
private function __onCameraStatusHandler(event:StatusEvent):void
{
if(!m_camera.muted)
{
t_btn_Shooting.enabled = true;
}
else
{
Alert.show("无法链接到活动摄像头,是否重新检测。","提示:",Alert.OK|Alert.NO,this,__InitCamera);
}
m_camera.removeEventListener(StatusEvent.STATUS,__onCameraStatusHandler);
}
//当摄像头不存在,或连接不正常时重新获取
private function __InitCamera(event:CloseEvent):void
{
if(event.detail == Alert.OK)
{
initApp();
}
}
//WebService保存图片成功事件
private function __onSavePictureResult(event:ResultEvent):void
{
//trace(event.result);
if(event.result.toString() != "保存失败")
{
str = event.result.toString();
Alert.show("保存成功,是否关闭窗口?","提示",3,this,__onAlertCloseHandler);
}
else
{
Alert.show(event.result.toString(),"提示",Alert.OK);
}
}
//连接WebService失败事件
private function __onSavePictureFault(event:FaultEvent):void
{
//Alert.show(event.fault.toString(),"提示",Alert.OK);
Alert.show("连接服务器失败。","提示",Alert.OK);
}
//保存图片成功后的弹出窗口确认事件
private function __onAlertCloseHandler(event:CloseEvent):void
{
if(event.detail == Alert.YES)
{
ExternalInterface.call("setValueToField",str);
}
}
]]>
</mx:Script>
<mx:HTTPService id="t_ws_SavePicture" showBusyCursor="true" method="POST" useProxy="false" result="__onSavePictureResult(event)" fault="__onSavePictureFault(event)"/>
<mx:Panel x="10" y="10" width="180" height="200" layout="absolute" title="视频拍照" fontSize="12">
<mx:VideoDisplay id="t_vd_Video" width="160" height="120"/>
<mx:ControlBar horizontalAlign="right">
<mx:Button id="t_btn_Shooting" label="拍照" click="SnapshotPicture()"/>
</mx:ControlBar>
</mx:Panel>
<mx:Panel id="t_panel_Picture" x="198" y="10" width="180" height="200" layout="absolute" title="拍照图片" fontSize="12" visible="false">
<mx:Image id="t_img_Picture" x="0" y="0" width="160" height="120"/>
<mx:ControlBar horizontalAlign="right">
<mx:Button id="t_ban_Save" label="保存" click="SavePicture()" />
</mx:ControlBar>
</mx:Panel>
</mx:Application>
java:
package cn.myapps.core.onlinetakephoto;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import cn.myapps.constans.Environment;
import cn.myapps.util.sequence.Sequence;
/**
* Servlet implementation class onLineTakePhotoServlet
*/
public class onLineTakePhotoServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private Environment env = Environment.getInstance();
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
public void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
String bitmap_data = request.getParameter("bitmap_data");
int width = Integer.parseInt(request.getParameter("width"));
int height = Integer.parseInt(request.getParameter("height"));
BufferedImage img = new BufferedImage(width, height,BufferedImage.TYPE_INT_RGB);
try {
int w = width;
int h = height;
int[] pixels = new int[w * h];
String[] m_tempPics = bitmap_data.split(",");
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
Long pic_argb = Long.parseLong(m_tempPics[x * h + y]);
int a = (int) (pic_argb >> 24 & 0xFF);
int r = (int) (pic_argb >> 16 & 0xFF);
int g = (int) (pic_argb >> 8 & 0xFF);
int b = (int) (pic_argb & 0xFF);
pixels[y * w + x] = new Color(r, g, b, a).getRGB();
}
}
img.setRGB(0, 0, w, h, pixels, 0, w);
img.flush();
ByteArrayOutputStream bao = new ByteArrayOutputStream();
ImageIO.write(img, "jpg", bao);
byte[] data = bao.toByteArray();
String filePath = env.getRealPath("/uploads/photo");
//判断路径是否存在,若不存在则创建路径
File upDir = new File(filePath);
if (!upDir.exists())
{
upDir.mkdir();
}
//生成随机文件名
String saveName = Sequence.getSequence();
String fileName = saveName + ".jpg";
//写图片
File f = new File(filePath+"\\" + fileName);
DataOutputStream dos = new DataOutputStream(new FileOutputStream(f));
dos.write(data);
dos.flush();
dos.close();
response.setContentType("text/xml");
response.getWriter().write("/uploads/photo/" + fileName);
}
catch(Exception ex)
{
response.setContentType("text/xml");
response.getWriter().write("保存失败");
}
}
}
源码:/Files/obpm/onlinetakephoto.rar
原创人员:Denny
posted @
2010-08-29 21:52 obpm 阅读(5504) |
评论 (5) |
编辑 收藏
LDAP快速入门
1. LDAP简介
LDAP(轻量级目录访问协议,Lightweight Directory Access Protocol)是实现提供被称为目录服务的信息服务。目录服务是一种特殊的数据库系统,其专门针对读取,浏览和搜索操作进行了特定的优化。目录一般用来包含描述性的,基于属性的信息并支持精细复杂的过滤能力。目录一般不支持通用数据库针对大量更新操作操作需要的复杂的事务管理或回卷策略。而目录服务的更新则一般都非常简单。这种目录可以存储包括个人信息、web链结、jpeg图像等各种信息。为了访问存储在目录中的信息,就需要使用运行在TCP/IP 之上的访问协议—LDAP。
LDAP目录中的信息是是按照树型结构组织,具体信息存储在条目(entry)的数据结构中。条目相当于关系数据库中表的记录;条目是具有区别名DN (Distinguished Name)的属性(Attribute),DN是用来引用条目的,DN相当于关系数据库表中的关键字(Primary Key)。属性由类型(Type)和一个或多个值(Values)组成,相当于关系数据库中的字段(Field)由字段名和数据类型组成,只是为了方便检索的需要,LDAP中的Type可以有多个Value,而不是关系数据库中为降低数据的冗余性要求实现的各个域必须是不相关的。LDAP中条目的组织一般按照地理位置和组织关系进行组织,非常的直观。LDAP把数据存放在文件中,为提高效率可以使用基于索引的文件数据库,而不是关系数据库。类型的一个例子就是mail,其值将是一个电子邮件地址。
LDAP的信息是以树型结构存储的,在树根一般定义国家(c=CN)或域名(dc=com),在其下则往往定义一个或多个组织 (organization)(o=Acme)或组织单元(organizational units) (ou=People)。一个组织单元可能包含诸如所有雇员、大楼内的所有打印机等信息。此外,LDAP支持对条目能够和必须支持哪些属性进行控制,这是有一个特殊的称为对象类别(objectClass)的属性来实现的。该属性的值决定了该条目必须遵循的一些规则,其规定了该条目能够及至少应该包含哪些属性。例如:inetorgPerson对象类需要支持sn(surname)和cn(common name)属性,但也可以包含可选的如邮件,电话号码等属性。
2. LDAP简称对应
- o– organization(组织-公司)
- ou – organization unit(组织单元-部门)
- c - countryName(国家)
- dc - domainComponent(域名)
- sn – suer name(真实名称)
- cn - common name(常用名称)
3. 目录设计
设计目录结构是LDAP最重要的方面之一。下面我们将通过一个简单的例子来说明如何设计合理的目录结构。该例子将通过Netscape地址薄来访文。假设有一个位于美国US(c=US)而且跨越多个州的名为Acme(o=Acme)的公司。Acme希望为所有的雇员实现一个小型的地址薄服务器。
我们从一个简单的组织DN开始:
dn: o=Acme, c=US
Acme所有的组织分类和属性将存储在该DN之下,这个DN在该存储在该服务器的目录是唯一的。Acme希望将其雇员的信息分为两类:管理者(ou= Managers)和普通雇员(ou=Employees),这种分类产生的相对区别名(RDN,relative distinguished names。表示相对于顶点DN)就shi :
dn: ou=Managers, o=Acme, c=US
dn: ou=Employees, o=Acme, c=US
在下面我们将会看到分层结构的组成:顶点是US的Acme,下面是管理者组织单元和雇员组织单元。因此包括Managers和Employees的DN组成为:
dn: cn=Jason H. Smith, ou=Managers, o=Acme, c=US
dn: cn=Ray D. Jones, ou=Employees, o=Acme, c=US
dn: cn=Eric S. Woods, ou=Employees, o=Acme, c=US
为了引用Jason H. Smith的通用名(common name )条目,LDAP将采用cn=Jason H. Smith的RDN。然后将前面的父条目结合在一起就形成如下的树型结构:
cn=Jason H. Smith
+ ou=Managers
+ o=Acme
+ c=US
-> dn: cn=Jason H. Smith,ou=Managers,o=Acme,c=US
现在已经定义好了目录结构,下一步就需要导入目录信息数据。目录信息数据将被存放在LDIF文件中,其是导入目录信息数据的默认存放文件。用户可以方便的编写Perl脚本来从例如/etc/passwd、NIS等系统文件中自动创建LDIF文件。
下面的实例保存目录信息数据为testdate.ldif文件,该文件的格式说明将可以在man ldif中得到。
在添加任何组织单元以前,必须首先定义Acme DN:
dn: o=Acme, c=US
objectClass: organization
这里o属性是必须的
o: Acme
下面是管理组单元的DN,在添加任何管理者信息以前,必须先定义该条目。
dn: ou=Managers, o=Acme, c=US
objectClass: organizationalUnit
这里ou属性是必须的。
ou: Managers
第一个管理者DN:
dn: cn=Jason H. Smith, ou=Managers, o=Acme, c=US
objectClass: inetOrgPerson
cn和sn都是必须的属性:
cn: Jason H. Smith
sn: Smith
但是还可以定义一些可选的属性:
telephoneNumber: 111-222-9999
mail: headhauncho@acme.com
localityName: Houston
可以定义另外一个组织单元:
dn: ou=Employees, o=Acme, c=US
objectClass: organizationalUnit
ou: Employees
并添加雇员信息如下:
dn: cn=Ray D. Jones, ou=Employees, o=Acme, c=US
objectClass: inetOrgPerson
cn: Ray D. Jones
sn: Jones
telephoneNumber: 444-555-6767
mail: jonesrd@acme.com
localityName: Houston
dn: cn=Eric S. Woods, ou=Employees, o=Acme, c=US
objectClass: inetOrgPerson
cn: Eric S. Woods
sn: Woods
telephoneNumber: 444-555-6768
mail: woodses@acme.com
localityName: Houston
4. 配置OpenLDAP
本文实践了在 Windows 下安装配 openldap,并添加一个条目,LdapBrowser 浏览,及 Java 程序连接 openldap 的全过程。
1. 下载安装 openldap for windows,当前版本2.2.29下载地址:http://download.bergmans.us/openldap/openldap-2.2.29/openldap-2.2.29-db-4.3.29-openssl-0.9.8a-win32_Setup.exe
相关链接:http://lucas.bergmans.us/hacks/openldap/
安装很简单,一路 next 即可,假设我们安装在 c:\openldap
2. 配置 openldap,编辑 sldap.conf 文件
1) 打开 c:\openldap\sldap.conf,找到
include C:/openldap/etc/schema/core.schema,在它后面添加
include C:/openldap/etc/schema/cosine.schema
include C:/openldap/etc/schema/inetorgperson.schema
接下来的例子只需要用到以上三个 schema,当然,如果你觉得需要的话,你可以把其他的 schema 全部添加进来
include C:/openldap/etc/schema/corba.schema
include C:/openldap/etc/schema/dyngroup.schema
include C:/openldap/etc/schema/java.schema
include C:/openldap/etc/schema/misc.schema
include C:/openldap/etc/schema/nis.schema
include C:/openldap/etc/schema/openldap.schema
2) 还是在 sldap.conf 文件中,找到
suffix "dc=my-domain,dc=com"
rootdn "cn=Manager,dc=my-domain,dc=com"
把这两行改为
suffix "o=teemlink,c=cn"
rootdn "cn=Manager,o=teemlink,dc=cn"
suffix 就是看自己如何定义了,后面步骤的 ldif 文件就必须与它定义了。还要注意到这个配置文件中有一个 rootpw secret,这个 secret 是 cn=Manager 的密码,以后会用到,不过这里是明文密码,你可以用命令: slappasswd -h {MD5} -s secret 算出加密的密码 {MD5}Xr4ilOzQ4PCOq3aQ0qbuaQ== 取代配置中的 secret。
3. 启动 openldap
CMD 进入到 c:\openldap 下,运行命令 sldapd -d 1
用可以看到控制台下打印一片信息,openldap 默认是用的 Berkeley DB 数据库存储目录数据的。
4. 建立条目,编辑导入 ldif 文件
1) 新建一个 ldif(LDAP Data Interchanged Format) 文件(纯文本格式),例如 test.ldif,文件内容如下:
dn: o=teemlink
objectclass: top
objectclass: organization
o: develop
2) 执行命令:ldapadd -l test.ldif
5. 使用LDAP Browser进行访问
5.1安装LDAP Browser2.6软件,进行如下操作:
5.2显示效果
5. Java操作LDAP
5.1 用JNDI进访问
package cn.myapps.test;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
public class LdapTest {
public void JNDILookup() {
String root = "o=teemlink,c=cn";
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://192.168.0.30/" + root);
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, "cn=Nicholas,ou=develop,o=teemlink,c=cn");
env.put(Context.SECURITY_CREDENTIALS, "123456");
DirContext ctx = null;
try {
ctx = new InitialDirContext(env);
Attributes attrs = ctx.getAttributes("cn=Nicholas,ou=develop");
System.out.println("Last Name: " + attrs.get("sn").get());
System.out.println("认证成功");
} catch (javax.naming.AuthenticationException e) {
e.printStackTrace();
System.out.println("认证失败");
} catch (Exception e) {
System.out.println("认证出错:");
e.printStackTrace();
}
if (ctx != null) {
try {
ctx.close();
} catch (NamingException e) {
// ignore
}
}
}
public static void main(String[] args) {
LdapTest LDAPTest = new LdapTest();
LDAPTest.JNDILookup();
}
}
5.2 用JLDAP进访问
访问地址:http://www.openldap.org/jldap/ 并下载相关lib
import com.novell.ldap.*;
import java.io.UnsupportedEncodingException;
public class List
{
public static void main(String[] args)
{
int ldapPort = LDAPConnection.DEFAULT_PORT;
int searchScope = LDAPConnection.SCOPE_ONE;
int ldapVersion = LDAPConnection.LDAP_V3;
boolean attributeOnly = false;
String attrs[] = null;
String ldapHost = "192.168.0.30";
String loginDN = "cn=Manager,o=teemlink,c=cn";
String password = "secret";
String searchBase = "ou=develop,o=teemlink,c=cn";
String searchFilter = "objectClass=*";
LDAPConnection lc = new LDAPConnection();
try {
// connect to the server
lc.connect(ldapHost, ldapPort);
// bind to the server
lc.bind(ldapVersion, loginDN, password.getBytes("UTF8"));
LDAPSearchResults searchResults =
lc.search(searchBase, // container to search
searchScope, // search scope
searchFilter, // search filter
attrs, // "1.1" returns entry name only
attributeOnly); // no attributes are returned
// print out all the objects
while (searchResults.hasMore()) {
LDAPEntry nextEntry = null;
try {
nextEntry = searchResults.next();
System.out.println("\n" + nextEntry.getDN());
System.out.println(nextEntry.getAttributeSet());
} catch (LDAPException e) {
System.out.println("Error: " + e.toString());
// Exception is thrown, go for next entry
continue;
}
}
// disconnect with the server
lc.disconnect();
} catch (LDAPException e) {
System.out.println("Error: " + e.toString());
} catch (UnsupportedEncodingException e) {
System.out.println("Error: " + e.toString());
}
System.exit(0);
}
}
5.3 用JDBC-LDAP进访问
访问地址:http://www.openldap.org/jdbcldap/ 并下载相关lib
package jdbcldap;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class JdbcLdap {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
Class.forName("com.octetstring.jdbcLdap.sql.JdbcLdapDriver");
String ldapConnectString = "jdbc:ldap://192.168.0.30/o=teemlink,c=cn?SEARCH_SCOPE:=subTreeScope";
Connection con = DriverManager.getConnection(ldapConnectString, "cn=Manager,o=teemlink,c=cn", "secret");
String sql = "SELECT * FROM ou=develop,o=teemlink,c=cn";
Statement sat = con.createStatement();
ResultSet rs = sta.executeQuery(sql);
while (rs.next()) {
System.out.println(rs.getString(1));
}
if (con != null)
con.close();
}
}
原创人员:Nicholas
文章来源:
http://www.cnblogs.com/obpm/archive/2010/08/28/1811065.html
posted @
2010-08-28 14:38 obpm 阅读(1916) |
评论 (0) |
编辑 收藏
以子类取代类型编码
Replace Type Code with Subclasses
1. 何谓重构
1.1名词解释
对软件内部结构的㆒种调整,目的是在不改变「软件之可察行为」前提下,提高其可理解性,降低其修改成本。
1.2动词解释
使用一系列重构准则(手法),在不改变「软件之可察行为」前提下,调整其结构。
2. 为何重构
2.1「重构」改进软件设计
同样完成一件事,设计不良的程序往往需要更多代码,这常常是因为代码在不同的地方使用完全相同的语句做同样的事。因此改进设计的一个重要方向就是消除重复代码(Duplicate Code)
2.2「重构」使软件更易被理解
你的源码还有其它读者:数个月之后可能会有另一位程序员尝试读懂你的代码并做一些修改。我们很容易忘记这第二位读者,但他才是最重要的。计算器是否多花了数个钟头进行编译,又有什么关系呢?如果一个程序员花费一周时间来修改某段代码,那才关系重大— 如果他理解你的代码,这个修改原本只需一小时
2.3「重构」助你找到臭虫 ( bugs)
Kent Beck 经常形容自己的㆒句话:『我不是个伟大的程序员;我只是个有着㆒些优秀习惯的好程序员而已。』重构能够帮助我更有效地写出强固稳健(robust)的代码。
2.4「重构」助你提高编程速度
终于,前面的一切都归结到了这最后一点:重构帮助你更快速地开发程序。听起来有违反直觉。当我谈到重构,人们很容易看出它能够提高质量。改善设计、提升可读性、减少错误,这些都是提高质量。但这难道不会降低开发速度吗?我强烈相信:良好设计是快速软件开发的根本。事实上拥有良好设计才可能达成快速的开发。如果没有良好设计,或许某一段时间内你的进展迅速,但恶劣的设计很快就让你的速度慢下来。你会把时间花在调试上面,无法添加新功能。修改时间愈来愈长,因为你必须花愈来愈多的时间去理解系统、寻找重复代码。随着你给最初程序打上一个又一个的补丁(patch),新特性需要更多代码才能实现。真是个恶性循环。
3.何时重构?
重构本来就不是一件「特别拨出时间做」的事情,重构应该随时随地进行。你不应该为重构而重构,你之所以重构,是因为你想做别的什么事,而重构可以帮助你把那些事做好
3.1三次法则(The Rule of Three)
Don Roberts 给了我一条准则:第一次做某件事时只管去做;第二次做类似的事会产生反感,但无论如何还是做了;第三次再做类似的事,你就应该重构。
☆ 事不过三,三则重构。(Three strikes and you refactor.)
3.2重构时机
添加功能时一并重构
修补错误时一并重构
复审代码时一并重构
-以上章节摘抄自《重构-改善既有代码的设计》
4.平台重构案例
4.1重构动机
1. 实例模块为View,重构元素为ViewAction、ViewProcessBean、View,以下为关系图。
2. 由于View存在多种editMode(编辑模式),而每个调用的地方都需要进行type code(类型码)判断,然后再进行相应的业务逻辑处理,最终在每个调用的地方都形成了大量的if-else代码,大大减弱了代码的可读性,和逻辑清晰度。
3. 调用的地方:
4.2重构作法
如上图所示,将每种type code重构成subclass,加强了每种类型处理业务逻辑的能力。
View-版本1566代码片段:
public EditMode getEditModeType() {
if (EDIT_MODE_CODE_DQL.equals(getEditMode())) {
return new DQLEditMode(this);
} else if (EDIT_MODE_CODE_SQL.equals(getEditMode())) {
return new SQLEditMode(this);
} else if (EDIT_MODE_DESIGN.equals(getEditMode())) {
return new DesignEditMode(this);
}
return new NullEditMode(this);
}
说明:调用者无需了解具体的类型,由View自身作判断,返回EditMode接口,从而实现多态调用。
ViewProcessBean代码片段:
重构前-版本1503:
public String expDocToExcel(String viewid, WebUser user, ParamsTable params) throws Exception {
if (view.getEditMode().equals(View.EDIT_MODE_DESIGN)) {
datas = dp.queryBySQLPage(sql, params, tempPage, LINES, user.getDomainid());
} else if (view.getEditMode().equals(View.EDIT_MODE_CODE_DQL)) {
datas = dp.queryByDQLPage(dql, params, tempPage, LINES, user.getDomainid());
} else if (view.getEditMode().endsWith(View.EDIT_MODE_CODE_SQL)) {
datas = dp.queryBySQLPage(sql, params, tempPage, LINES, user.getDomainid());
}
}
重构后-版本1566:
public String expDocToExcel(String viewid, WebUser user, ParamsTable params) throws Exception {
datas = view.getEditModeType().getDataPackage(params, tempPage, LINES, user, currdoc);
//其他业务逻辑
}
5.结语
由上述案例可看到,引入subclass代替type code可以大大减少if-else判断,而且可以把责任内聚到每种type中,使代码结构更清晰易懂。
在本案例中引入了空类型概念,即NullEditMode,代码如下:
/**
*
* @author nicholas zhen
*
*/
public class NullEditMode extends AbstractEditMode implements EditMode {
public NullEditMode(View view) {
super(view);
}
public String getQueryString(ParamsTable params, WebUser user, Document sDoc) {
return "";
}
public DataPackage getDataPackage(ParamsTable params, WebUser user, Document doc) throws Exception {
return new DataPackage();
}
public DataPackage getDataPackage(ParamsTable params, int page, int lines, WebUser user, Document doc) throws Exception {
return new DataPackage();
}
public long count(ParamsTable params, WebUser user, Document doc) throws Exception {
return 0;
}
}
说明:空类型保证了调用每个方法都有默认值返回,而不需要进行非空判断,当View没有类型时,即返回默认的空类型
原创人员:Nicholas
文章来源:
http://www.cnblogs.com/obpm/archive/2010/07/13/1776856.html
posted @
2010-07-13 23:22 obpm 阅读(213) |
评论 (0) |
编辑 收藏