如鹏网 大学生计算机学习社区

CowNew开源团队

http://www.cownew.com 邮件请联系 about521 at 163.com

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  363 随笔 :: 2 文章 :: 808 评论 :: 0 Trackbacks

#

盖茨北大演讲被闹场2007年4月20日

from腾讯网。
4月20日,比尔盖茨在北大演讲,在他为一行人颁奖时,一位男子手拿海报并大声用英语叫喊反对微软垄断。

男子海报上写着“Free software”“Open resource”,抗议微软垄断,呼吁开源,事后该男子被警方带走进行调查。

据熟知内情的人士透露,此男子系CSDN的前市场总监,是个衷情于开源软件的业内人士。据传,此人甚至将名字改为带有“开源”的字样。

组图:盖茨北大演讲遭遇反垄断抗议
高举手书海报

组图:盖茨北大演讲遭遇反垄断抗议
现场被控制

组图:盖茨北大演讲遭遇反垄断抗议 

来自CSDN的信息显示,该名男子是LPI中国首席代表王洋,自号王开源,是开源软件的积极分子。

2005年王开源先生任即时科研集团总裁助理;2006年加入CSDN,参与创建OSDN开源社区,并与北京点击查看北京及更多城市天气预报大学、中国开源软件联盟合作,首次把“国际软件自由日”引进中国。离开CSDN后加入LPI(Linux Professional Institute)。




CowNew开源团队声援王开源大哥!!!
希望政府不要再被盖茨这个大资本家迷惑!!!
国家采购首选开源!!!
发展国内开源事业,做民族的脊梁!!!
from:http://blog.csdn.net/arthur5933/archive/2007/04/20/1571507.aspx

       《愿歌》



未名湖畔嚣声涩 ,
帝国列强作秀狼;
博雅先贤清风泪,
男儿七尺当自强!


       开源
2007年4月20日晨


posted @ 2007-04-20 15:16 CowNew开源团队 阅读(1802) | 评论 (20)编辑 收藏

以前写书的时候写的一个案例,如果应用于实际的项目还需要改进,应一个网友的要求贴上来,仅供参考
<?xml version="1.0" encoding="GB2312" ?>
<project name="dailybuild" default="main">
<!--tomcat的路径-->
<property name="tomcathome.dir"
    value="C:\Program Files\Apache Software Foundation\Tomcat 5.0\"/>
<!--tomcat管理控制台路径-->
<property name="tomcathome.mgr.dir" value="http://127.0.0.1:8080/manager/"/>   
<!--tomcat管理控制台用户名-->
<property name="tomcat.username" value="admin"/>   
<!--tomcat管理控制台密码-->
<property name="tomcat.password"  value=""/>
<!--tomcat中-->
<property name="cownewwebpath" value="/CownewPISWeb"/>
<!--Web应用的路径-->   
<property name="cownewwebhome.dir"
    value="${tomcathome.dir}webapps\CownewPISWeb\"/> 
<!--Web应用的源码路径(主要供稍后的编译用)-->     
<property name="cownewwebhome.java.dir"
    value="${cownewwebhome.dir}WEB-INF\java\"/>
<!--Web应用输出的class路径-->       
<property name="cownewwebhome.classes.dir"
    value="${cownewwebhome.dir}WEB-INF\classes\"/> 
<!--CVS根路径-->    
<property name="cvsRoot" value=":pserver:杨中科:123456@192.168.1.6:/cvsrep"/>    
<!--代码检出的路径-->
<property name="cvs.outtemp.dir" value="${cownewwebhome.dir}cvsout/"/>
<!--BVT测试(冒烟测试)的测试结果输出的路径-->
<property name="bvt.output.dir" value="c:/bvtreport/"/>

<taskdef name="stopTomcat" classname="org.apache.catalina.ant.StopTask">       
  <classpath>           
    <path location="${tomcathome.dir}/server/lib/catalina-ant.jar"/>       
  </classpath>   
</taskdef>
<taskdef name="startTomcat" classname="org.apache.catalina.ant.StartTask">       
  <classpath>           
    <path location="${tomcathome.dir}/server/lib/catalina-ant.jar"/>       
  </classpath>   
</taskdef>
<taskdef
name="sendBuildEmail" classname="com.cownew.dailybuild.taskdefs.DailyBuildMailTask">       
  <classpath>           
    <path location="../lib/ant-dailybuild.jar"/>       
  </classpath>   
</taskdef>
<target name="main">
<echo>停止Tomcat</echo>
<stopTomcat url="${tomcathome.mgr.dir}" username="${tomcat.username}"
    password="${tomcat.password}" path="${cownewwebpath}"/>
<echo>清除原有构建文件</echo>
<delete dir="${cownewwebhome.dir}"/>
<echo>开始从CVS下拉代码</echo>
<mkdir dir="${cvs.outtemp.dir}"/>
<cvs cvsRoot="${cvsRoot}" package="CownewPISWeb" dest="${cvs.outtemp.dir}"/>
<mkdir dir="${cownewwebhome.dir}"/>
<copy todir="${cownewwebhome.dir}">
  <fileset dir="${cvs.outtemp.dir}CownewPISWeb/WebContent/"/>
</copy>

<mkdir dir="${cownewwebhome.java.dir}"/>
<copy todir="${cownewwebhome.java.dir}">
  <fileset dir="${cvs.outtemp.dir}CownewPISWeb/src/"/>
</copy>
<delete dir="${cvs.outtemp.dir}"/>
<echo>开始编译源代码</echo>
<mkdir dir="${cownewwebhome.classes.dir}"/>
<javac srcdir="${cownewwebhome.java.dir}" destdir="${cownewwebhome.classes.dir}" >
   <classpath>
      <pathelement location="E:\保留文档\java\常用包\junit-3.8.1.jar" />
      <pathelement location="E:\保留文档\java\常用包\struts.jar" />
      <pathelement location="E:\保留文档\java\常用包\log4j.jar" />
      <pathelement location="E:\保留文档\java\常用包\dom4j.jar" />
   </classpath>
</javac>
<delete dir="${cownewwebhome.java.dir}"/>
<echo>启动Tomcat</echo>
<startTomcat url="${tomcathome.mgr.dir}" username="${tomcat.username}"
    password="${tomcat.password}" path="${cownewwebpath}"/>
<echo>开始冒烟测试</echo>
<delete>
  <fileset dir="${bvt.output.dir}"
           includes="*.*"
           defaultexcludes="false"/>
</delete>
<junit>
    <classpath>
       <pathelement location="E:\保留文档\java\常用包\junit-3.8.1.jar"/> 
       <pathelement location="${cownewwebhome.classes.dir}"/>         
    </classpath>
    <formatter type="brief" usefile="true"/>
    <batchtest todir="${bvt.output.dir}">
      <fileset dir="${cownewwebhome.classes.dir}">
          <include name="**/bvt/*.class" />
       </fileset>
    </batchtest>   
</junit>

<echo>开始发送构建结果邮件</echo>

<sendBuildEmail smtpHost="smtp.mycompany.com" smtpUserId="scmheader" smtpPassword="123456"
  senderEmail="scmheader@mycompany.com" title="日构建已经完成"
  jdbcClass="net.sourceforge.jtds.jdbc.Driver"
  dbUrl="jdbc:jtds:sqlserver://127.0.0.1/dailybuild;user=sa">
  日构建已经完成,请到此处查看构建日志:\\192.168.1.15\dailybuild\result.log
  点击此处查看冒烟测试结果: \\192.168.1.15\bvtreport\
</sendBuildEmail>
</target>
</project>

posted @ 2007-04-20 09:10 CowNew开源团队 阅读(1186) | 评论 (1)编辑 收藏

 

与VCL、AWT等框架中的图形界面框架不同,Swing创造性的采用了MVC(Model View Controller) 模式。MVC 把控件(Component)划分成三个部分:模型( Model):管理这个模块中所用到的数据和值,如某个数据的最大值、最小值、当前值等数据;视图( View):管理如何将模型显示给用户;控制器(Controller) 决定如何处理用户和该模块交互时产生的事件,如用户单击一个按钮等。Sun 出于对视图和控制器之间的依赖关系的考虑, 在 Swing 设计中将 MVC体系简化为分离模型体系 ( Separable Model Architecture) , 将其中的控制器和视图结合成 UI 代理。

界面中的每个控件都包含三种特征:

它的状态:比如一个按钮的状态

它的外观:颜色、尺寸等

它的行为:对事件作出的反应

以一个按钮为例,它有可用、不可用状态,在不同的LookAndFeel中有不同的外观显示,在鼠标按下、鼠标右击等事件中有自己独特的响应方式。如果管理按钮状态、绘制按钮外观,响应时间等任务都由按钮负责的话就违背了面向对象设计中的“单一责任原则”。Swing开发人员采用MVC模式解决了此问题,将控件的LookAndFeel同一个对象关联到一起,同时将其内容保存到另一个对象中。控制器负责控制用户输入事件。比如鼠标单击、按键操作等,它会决定将这些事件转换成模型中的改变.还是视图中的改变。例如,假定用户在文本框中按下某个键,控制器就会调用模型的“插入字符”命令。随后,模型会通知视图更新来显示新的模型。视图不用关心什么时候进行文字改变,只要模型通知它更新它就会更新。这样控制器只用与用户交互并把交互结果反映到模型中去;模型负责维护状态,当状态变化时通知视图更新显示;视图不负责用户交互的状态维护,它只是根据模型中的状态绘制不同的界面。

Swing中的大多数控件的模型是由一个名字以Model结尾的接口实现的。比如按钮对应的模型接口就是 ButtonModel,JDK中定义了ButtonModel的默认实现类DefaultButtonModel。下面是ButtonModel各个方法的说明:

boolean isArmed():如果按钮被按下,且鼠标指针仍停留在按钮上则返回true

boolean isSelected():如果按钮处于选择状态则返回true

boolean isEnabled():如果按钮可用则返回true

boolean isPressed():如果按钮被按下,但鼠标没有松开,则返回true

boolean isRollover():如果鼠标指针在按钮之上则返回true

public int getMnemonic():返回按钮的助记键

public String getActionCommand():返回命令字符串

此外还有对应的设置状态方法:public void setArmed(boolean b)、public void setSelected(boolean b)、public void setEnabled(boolean b)、public void setPressed(boolean b)、public void setRollover(boolean b)、public void setMnemonic(int key)、public void setActionCommand(String s)等。

每一个JButton都保存着一个ButtonModel对象,我们可以通过JButton的getModel方法来取得该模型对象:

JButton btn = new JButton(“test”);

ButtonModel btnModel = btn.getModel();

通过这个模型对象我们就可以得到按钮的是否可用等状态,不过这个模型是给控件开发者使用的,对于普通使用者来说无需直接调用它,JButton提供了方法来间接的取得这些属性,这一点可以从AbstractButton类的isSelected方法中看出来:

public boolean isSelected()

{

return model.isSelected();

}

Swing中大部分控件都由自己的模型,比如JList控件的ListModel、JTable的TableModel、JSpinner的SpinnerModel、JComboBox的SpinnerModel(SpinnerModel是从ListModel派生出来的),这些模型也由默认的实现,名称通常为模型名前加Default。

Swing中的大多数控件的视图是由一个名字以UI结尾的类实现的,比如按钮对应的模型接口就是 ButtonUI。由于视图在不同的LookAndFeel中有不同的展现形式,所以控件的视图对每一种LookAndFeel都提供了不同的实现。以JLabel为例,它就有MetalLabelUI、MotifLabelUI、WindowsLabelUI等对应不同LookAndFeel的实现。所有的视图都要直接或者间接的从ComponentUI抽象类派生,ComponentUI类中的方法都是供Model回调使用的,下面是ComponentUI主要方法的说明:

public void installUI(JComponent c):这个方法在ComponentUI 实例被安装到UI代理的时候被触发,用来根据LookAndFeel配置控件。它需要完成如下工作:为Color、Font、Border、Icon等类型的属性设定默认值;根据需要设置布局管理器;创建子控件;初始化监听器;为控件设置PropertyChangeListener 监听器以检测控件属性变化事件;初始化快捷键、Tab键顺序等;初始化数据;

public void uninstallUI(JComponent c):这个方法在ComponentUI 实例被从UI代理移除的时候触发。需要在此方法中撤销任何在installUI中进行的配置,要保证JComponent实例变为洁净状态(也就是没有监听器,没有LookAndFeel专有属性等)。它需要完成如下工作:从控件中移除border;从控件中移除布局管理器;从控件中移除子控件;从控件中移除事件、属性监听器、从控件中移除快捷键、Tab键顺序等;将数据标记为可以垃圾回收。

public void paint(Graphics g, JComponent c):为本视图的LookAndFeel绘制控件。

public void update(Graphics g, JComponent c):通知UI代理绘制指定控件。当特定的控件被绘制的时候此方法会被触发。这个方法的默认实现是用背景色填充控件,并且立即调用paint方法。

public Dimension getPreferredSize(JComponent c):返回当前LookAndFeel下控件的最佳尺寸。默认实现是返回null;

public Dimension getMinimumSize(JComponent c):返回当前LookAndFeel下控件的最小尺寸。默认实现是返回getPreferredSize的值;

public Dimension getMaximumSize(JComponent c):返回当前LookAndFeel下控件的最大尺寸。默认实现是返回getPreferredSize的值;

public boolean contains(JComponent c, int x, int y):判断指定的x、y坐标是否存在于当前LookAndFeel下的控件中。

public static ComponentUI createUI(JComponent c):为指定的控件返回UI代理实例。如果UI代理子类是无状态的,它也可以返回多控件共享的实例。如果UI代理子类是有状态的,则它必须为某个控件返回一个新的实例。

public int getAccessibleChildrenCount(JComponent c):返回所有可访问子控件的数量。

public Accessible getAccessibleChild(JComponent c, int i):返回指定的子控件

posted @ 2007-04-19 19:37 CowNew开源团队 阅读(3866) | 评论 (3)编辑 收藏

      目前Hibernate的辅助开发工具有很多,商业的有MyEclipse,自由工具则有HibernateTool、HibernateSynch、Middlegen等,这些工具提供了现有数据库表来生成代码和配置文件等功能,大大简化了开发。这些工具的缺陷也是非常明显的,其指导思想不明确,仅仅是一个代码生成器而已。比如HibernateTool暗示开发人员要先建立数据库表,这其实是一种数据驱动的开发模式,使得开发人员先要去思考数据是如何存储的、有哪些字段,而不是先思考系统对象之间的关系是怎么样的。
      基于此,我们开发了一个正向建模的Hibernate辅助工具,使用这个工具可以通过图形界面建立模型,然后可以由模型生成持久类、映射文件和数据库表。这样开发人员只要在工具中定义对象的模型即可,工具可以自动完成其他的工作。并且工具要保留设计的模型,这样开发人员可以迭代的修改模型,这就可以保证前期的工作成果尽可能多的被利用了。
      此版修复的Bug主要有界面布局的刷新、生成代码的正确性等,此版本在Eclipse3.2.0下测试通过,下载包包含二进制文件和源码。
      CowNew开源团队 http://www.cownew.com   

               
文档PDF格式

文档Word格式

二进制文件和源码下载>>>

posted @ 2007-04-17 18:50 CowNew开源团队 阅读(1634) | 评论 (4)编辑 收藏

2007年4月14日,CowNew开源团队组织了团队第一次北京地区聚会。本次聚会参会成员:散仙、Problem、星、Kingchou、红孔雀、曲金龙、杨中科。此外我们还邀请到了言实文化传播有限公司的执行董事屈辰晨先生、北京科技大学物流研究所的潘洪波先生和北京华美汉盛软件公司的张成先生及其同事。

 

 

 

 

posted @ 2007-04-16 15:27 CowNew开源团队 阅读(294) | 评论 (1)编辑 收藏

 

经常可以从开发人员口中听到“面向对象”这个词:

场景1、

A:我今天开始用面向对象的方法设计程序了!

B:你怎么做的?

A:我把保存文件、加载文件封装成了一个类,以后只要调用这个类就可以实现文件操作了。

场景2、

A:我开始学习Java了,面向对象的语言,你不要再学VB了,好土呀!

B:VB怎么了?

A:VB是面向过程的,已经过时了,Java中都是类,很时髦!

B:VB中也有类呀!

A:(无语)

场景3、

A:面向对象思想就是好呀,我真的离不开Java了!

B:你又用什么高超技术了?

A:我今天从一个操纵数据库的类继承了一个子类,然后重写了它的保存到数据库的方法,然后把数据通过Socket发送到了远程客户端了,而调用者根本不知道,哈哈!

场景4、

A:我推荐你用的Java不错吧?

B:真是不错,面向对象就是好,JDK里边也有好多好多的类可以用,不用像在VB里边那样要去查API文档了。

A:但是我听说现在又出了个面向方面编程,咱们看来又落伍了呀,看来做编程真的不是长久之计。

写几个类就是面向对象了吗?继承父类就是为了重用父类的代码吗?覆盖父类的方法就可以瞒天过海了吗?VB中也有类,它是面向对象吗?

1.1

类与对象

“类”和“对象”是面向对象编程中最基本的概念,从语言的角度来讲,“类”是用户自定义的具有一定行为的数据类型,“对象”则是“类”这种数据类型的变量。通俗的讲,“类”是具有相同或相似行为的事物的抽象,“对象”是“类”的实例,是是一组具有相关性的代码和数据的组合体,是有一定责任的实体。

类本身还可以进一步抽象为类型,类型是一种更高层次上的抽象,它只用来描述接口,比如抽象类和接口就是一种类型。当一个类型的接口包含另外一个类型的接口时,我们就可以说它是此类型的子类型。类型是用来标识特定接口的,如果一个对象接受某个接口定义的所有行为,那么我们就可以说该对象具有该类型。一个对象同时拥有多种类型。

面向对象编程的特性

面向对象编程有三个特性:封装,继承,多态。这三个特性从低级到高级描述了面向对象的特征。一种语言只有同时具备这三种特性才能被称为面向对象的语言。VB中也有类,它的类也支持封装和简单的继承,但是它不支持所有的继承语义和多态,因此VB只能被称为基于对象的语言。

封装是所有抽象数据类型(ADT)的特性,很多刚刚接触面向对象的人认为封装就是就是面向对象。将程序按照一定的逻辑分成多个互相协作的部分,并将对外界有用的稳定的部分暴露出来,而将会发生的改变隐藏起来,外界只能通过暴露的部分向这个对象发送操作请求从而享受对象提供的服务,而不必管对象内部是如何运行的,这就是封装。理解封装是理解面向对象的第一个步骤,40%的程序员对面向对象的理解仅停留在封装这个层次。

继承也称为派生,继承关系中,被继承的称为基类,从基类继承而得的被称为派生类或者子类。继承是保持对象差异性的同时共享对象相似性的复用。能够被继承的类总是含有并只含有它所抽象的那一类事务的共同特点。继承提供了实现复用,只要从一个类继承,我们就拥有了这个类的所有行为。理解继承是理解面向对象的第二个步骤,50%的程序员对面向对象的理解仅停留在继承这个层次。语义上的“继承”表示“是一种(is-a)”的关系。很多人体会到了继承在代码重用方面的优点,而忽视了继承的语义特征。于是很多滥用继承的情况就发生了,关于这一点我们将会在后边介绍。

多态是“允许用户将父对象设置成为一个或更多的它的子对象相等的技术,赋值后,基类对象就可以根据当前赋值给它的派生类对象的特性以不同的方式运作”(Charlie Calvert)。多态扩大了对象的适应性,改变了对象单一继承的关系。多态是行为的抽象,它使得同名方法可以有不同的响应方式,我们可以通过名字调用某一方法而无需知道哪种实现将被执行,甚至无需知道执行这个实现的对象类型。多态是面向对象编程的核心概念,只有理解了多态,才能明白什么是真正的面向对象,才能真正发挥面向对象的最大能力。不过可惜的是,只有极少数程序员能真正理解多态。

对象之间的关系

对象之间有两种最基本的关系:继承关系,组合关系。

继承关系

继承关系可以分为两种:一种是类对接口的继承,被称为接口继承;另一种是类对类的继承,被称为实现继承。继承关系是一种“泛化/特化”关系,基类代表一般,而派生类代表特殊。

组合关系。

组合是由已有的对象组合而成新对象的行为,组合只是重复运用既有程序的功能,而非重用其形式。组合与继承的不同点在于它表示了整体和部分的关系。比如电脑是由CPU、内存、显示器、硬盘等组成的,这些部件使得电脑有了计算、存储、显示图形的能力,但是不能说电脑是由CPU继承而来的。

1.2

对象之间有两种最基本的关系:继承关系,组合关系。通过这两种关系的不断迭代组合最终组成了可用的程序。但是需要注意的就是要合理使用这两种关系。

派生类是基类的一个特殊种类,而不是基类的一个角色。语义上的“继承”表示“is-a”(是一种)的关系,派生类“is-a”基类,这是使用继承关系的最基本前提。如果类A是类B的基类,那么类B应该可以在任何A出现的地方取代A,这就是“Liskov代换法则(LSP)”。如果类B不能在类A出现的地方取代类A的话,就不要把类B设计为类A的派生类。

举例来说,“苹果”是“水果”的派生类,所以“水果是植物的果实”这句话中的“水果”可以用“苹果”来代替:“苹果是植物的果实”;而“苹果”不是“香蕉”的派生类,因为“香蕉是一种种子退化的了的植物果实”不能被“苹果”替换为“苹果是一种种子退化的了的植物果实”。

举这个例子好像有点多余,不过现实的开发中却经常发生“苹果”从“香蕉”继承的事情。

某企业中有一套信息系统,其中有一个“客户(Customer)”基础资料,里边记录了客户的名称、地址、email等信息。后来系统要进行升级,增加一个“供应商(Supplier)”基础资料,开发人员发现“供应商”中有“客户”中的所有属性,只是多了一个“银行帐号”属性,所以就把“供应商”设置成“客户”客户的子类。

图 2.1

到了年终,老板要求给所有的客户通过Email发送新年祝福,由于“供应商”是一种(is-a)“客户”,所以系统就给“供应商”和“客户”都发送了新年祝福。第二天很多供应商都感动流涕的给老板打电话“谢谢老板呀,我们供应商每次都是求着贵公司买我们的东西,到了年终你们还忘不了我们,真是太感谢了!”。老板很茫然,找来开发人员,开发人员这才意识到问题,于是在发送Email的程序里做了判断“如果是供应商则不发送,否则发送”,一切ok了。到了年初,老板要求给所有很长时间没有购买他们产品的“客户”,打电话进行问候和意见征集。由于“供应商”是一种(is-a)“客户”,所以第二天电话里不断出现这样的回答:“你们搞错了吧,我们是你们的供应商呀!”。老板大发雷霆,开发人员这才意识到问题的严重性,所以在系统的所有涉及到客户的地方都加了判断“如果是供应商则……”,一共修改了60多处,当然由于疏忽遗漏了两处,所以后来又出了一次类似的事故。

我们可以看到错误使用继承的害处了。其实更好的解决方案应该是,从“客户”和“供应商”中抽取一个共同的基类“外部公司”出来:

图 2.2

这样就将“客户”和“供应商”之间的继承关系去除了。

派生类不应大量覆盖基类的行为。派生类具有扩展基类的责任,而不是具有覆盖(override)基类的责任。如果派生类需要大量的覆盖或者替换掉基类的行为,那么就不应该在两个类之间建立继承关系。

让我们再来看一个案例:

一个开发人员要设计一个入库单、一张出库单和一张盘点单,并且这三张单都有登帐的功能,通过阅读客户需求,开发人员发现三张单的登帐逻辑都相同:遍历单据中的所有物品记录,然后逐笔登到台帐上去。所以他就设计出了如下的程序:

图 2.3

把登帐逻辑都写到了“库存业务单据”这个抽象类中,三张单据从这个类继承即可。过了三个月,用户提出了新的需求:盘点单在盘点过程中,如果发现某个货物的盘亏量大于50则停止登帐,并向操作人员报警。所以开发人员在盘点单中重写了“库存业务单据”的“登帐”方法,实现了客户要求的逻辑。又过了半个月,客户要求出库登帐的时候不仅要进行原先的登帐,还要以便登帐一边计算出库成本。所以开发人员在出库单中重写了“库存业务单据”的“登帐”方法,实现了客户要求的逻辑。到了现在“库存业务单据”的“登帐”方法的逻辑只是对“入库单”有用了,因为其他两张单据都“另立门户”了。

这时候就是该我们重新梳理系统设计的时候了,我们把“库存业务单据”的“登帐”方法设置成抽象方法,具体的实现代码由具体子类自己决定:

图 2.4

注意此处的“库存业务单据”中的“登帐”方法是斜体,在UML中表示此方法是一个抽象方法。这个不难理解,每张单据都肯定有登帐行为,但是每张单据的登帐行为都有差异,因此在抽象类中定义类的“登帐”方法为抽象方法以延迟到子类中去实现。

继承具有如下优点:实现新的类非常容易,因为基类的大部分功能都可以通过继承关系自动赋予派生类;修改或者扩展继承来的实现非常容易;只要修改父类,派生的类的行为就同时被修改了。

初学面向对象编程的人会认为继承真是一个好东西,是实现复用的最好手段。但是随着应用的深入就会发现继承有很多缺点:继承破坏封装性。基类的很多内部细节都是对派生类可见的,因此这种复用是“白箱复用”;如果基类的实现发生改变,那么派生类的实现也将随之改变。这样就导致了子类行为的不可预知性;从基类继承来的实现是无法在运行期动态改变的,因此降低了应用的灵活性。

继承关系有很多缺点,如果合理使用组合则可以有效的避免这些缺点,使用组合关系将系统对变化的适应力从静态提升到动态,而且由于组合将已有对象组合到了新对象中,因此新对象可以调用已有对象的功能。由于组合关系中各个各个对象的内部实现是隐藏的,我们只能通过接口调用,因此我们完全可以在运行期用实现了同样接口的另外一个对象来代替原对象,从而灵活实现运行期的行为控制。而且使用合成关系有助于保持每个类的职责的单一性,这样类的层次体系以及类的规模都不太可能增长为不可控制的庞然大物。因此我们优先使用组合而不是继承。

当然这并不是说继承是不好的,我们可用的类总是不够丰富,而使用继承复用来创建一些实用的类将会不组合来的更快,因此在系统中合理的搭配使用继承和组合将会使你的系统强大而又牢固。

1.3

接口的概念

接口是一种类型,它定义了能被其他类实现的方法,接口不能被实例化,也不能自己实现其中的方法,只能被支持该接口的其他类来提供实现。接口只是一个标识,标识了对象能做什么,至于怎么做则不在其控制之内,它更像一个契约。

任何一个类都可以实现一个接口,这样这个类的实例就可以在任何需要这个接口的地方起作用,这样系统的灵活性就大大增强了。

接口编程的实例

SQL语句在各个不同的数据库之间移植最大的麻烦就是各个数据库支持的语法不尽相同,比如取出表的前10行数据在不同数据库中就有不同的实现。

MSSQLServer:Select top 10 * from T_Table

MySQL:select * from T_Table limit 0,10

Oracle:select * from T_Table where ROWNUM <=10

我们先来看一下最朴素的做法是怎样的:

首先定义一个SQL语句翻译器类:

public class Test1SQLTranslator

{

private int dbType;

public Test1SQLTranslator(int dbType)

{

super();

this.dbType = dbType;

}

public String translateSelectTop(String tableName, int count)

{

switch (dbType) {

case 0:

return "select top " + count + " * from " + tableName;

case 1:

return "select * from " + tableName + " limit 0," + count;

case 2:

return "select * from " + tableName + " where ROWNUM<=" + count;

default:

return null;

}

}

}

然后如下调用

public static void main(String[] args)

{

String tableName = "T_Table";

int count = 10;

int dbType = 0;

Test1SQLTranslator translator = new Test1SQLTranslator(dbType);

String sql = translator.translateSelectTop(tableName,count);

System.out.println(sql);

}

如果要增加对新的数据库的支持,比如DB2,那么就必须修改Test1SQLTranslator类,增加一个对DB2的case语句,这种增加只能是在编辑源码的时候进行添加,无法在运行时动态添加。再来看一下如果用基于接口的编程方式是如何实现的。

首先,定义接口ISQLTranslator,这个接口定义了所有SQL翻译器的方法,目前只有一个翻译Select top的方法:

public interface ISQLTranslator

{

public String translateSelectTop(String tableName, int count);

}

接着我们为各个数据库写不同的翻译器类,这些翻译器类都实现了ISQLTranslator接口:

public class MSSQLServerTranslator implements ISQLTranslator

{

public String translateSelectTop(String tableName, int count)

{

return "select top " + count + " * from " + tableName;

}

}

public class MySQLTranslator implements ISQLTranslator

{

public String translateSelectTop(String tableName, int count)

{

return "select * from " + tableName +" limit 0,"+count;

}

}

public class OracleSQLTranslator implements ISQLTranslator

{

public String translateSelectTop(String tableName, int count)

{

return "select * from " + tableName+" where ROWNUM<="+count;

}

}

如下调用:

public static void main(String[] args)

{

String tableName = "T_Table";

int count = 10;

ISQLTranslator translator = new MSSQLServerTranslator();

String sql = translator.translateSelectTop(tableName, count);

System.out.println(sql);

}

运行以后,打印出了:

select top 10 from T_Table

可以看到,不同的数据库翻译实现由不同的类来承担,这样最大的好处就是可扩展性极强,比如也许某一天出现了了支持中文语法的数据库,我要为它做翻译器只需再增加一个类:

public class SinoServerTranslator implements ISQLTranslator

{

public String translateSelectTop(String tableName, int count)

{

return "读取表"+tableName+"的前"+count+"行";

}

}

修改调用代码:

public static void main(String[] args)

{

String tableName = "T_Table";

int count = 10;

ISQLTranslator translator = new SinoServerTranslator();

String sql = translator.translateSelectTop(tableName, count);

System.out.println(sql);

}

运行后控制台打印出:

读取表T_Table的前10行

这里的translator 可以随意实例化,只要实例化的类实现了ISQLTranslator 就可以了,这个类也可以通过配置文件读取,甚至是其他类传递过来的,这都无所谓,只要是实现了ISQLTranslator 接口它就能正常工作。

如果要给SQL语句加上验证功能,也就是翻译的时候首先验证一下翻译的结果是否能在数据库中执行,我们就可以采用偷天换日的方式来进行。

首先创建一个VerifyTranslator类:

public class VerifyTranslator implements ISQLTranslator

{

private ISQLTranslator translator;

private Connection connection;

public VerifyTranslator(ISQLTranslator translator, Connection connection)

{

super();

this.translator = translator;

this.connection = connection;

}

public String translateSelectTop(String tableName, int count)

{

String sql = translator.translateSelectTop(tableName, count);

PreparedStatement ps = null;

try

{

ps = connection.prepareStatement(sql);

ps.execute();

} catch (SQLException e)

{

DbUtils.close(ps);

return "wrong sql";

}

return sql;

}

}

这个类接受一个实现了ISQLTranslator 接口的变量和数据库连接做为构造参数,最重要的是这个类本身也实现了ISQLTranslator 接口,这样它就完全能“伪装”成SQL翻译器来行使翻译的责任了,不过它没有真正执行翻译,它把翻译的任务转发给了通过构造函数传递来的那个翻译器变量:

String sql = translator.translateSelectTop(tableName, count);

它自己的真正任务则是进行SQL语句的验证:

ps = connection.prepareStatement(sql);

再次修改调用代码:

public static void main(String[] args)

{

String tableName = "T_Table";

int count = 10;

ISQLTranslator translator = new VerifyTranslator(

new SinoServerTranslator(), getConnection());

String sql = translator.translateSelectTop(tableName, count);

System.out.println(sql);

}

运行后控制台打印出:

wrong sql

下面这段代码看上去是不是很眼熟呢?

ISQLTranslator translator = new VerifyTranslator(new SinoServerTranslator(), getConnection());

这段代码和我们经常写的流操作非常类似:

InputStream is = new DataInputStream(new FileInputStream(new File(“c:/boot.ini”)));

这就是设计模式中经常提到的“装饰者模式”。

针对接口编程

从上面的例子我们可以看出,当代码写到:

String sql = translator.translateSelectTop(tableName, count);

的时候,代码编写者根本不关心translator这个变量到底是哪个类的实例,它只知道它调用了接口约定支持的translateSelectTop方法。

当一个对象需要与其他对象协作完成一项任务时,它就需要知道那个对象,这样才能调用那个对象的方法来获得服务,这种对象对另一个协作对象的依赖就叫做关联。如果一个关联不是针对具体类,而是针对接口的时候,任何实现这个接口的类都可以满足要求,因为调用者仅仅关心被依赖的对象是不是实现了特定接口。

当发送的请求和具体的请求响应者之间的关系在运行的时候才能确定的时候,我们就称之为动态绑定。动态绑定允许在运行期用具有相同接口的对象进行替换,从而实现多态。多态使得对象间彼此独立,所有的交互操作都通过接口进行,并可以在运行时改变它们之间的依赖关系。

针对接口编程,而不是针对实现编程是面向对象开发中的一个非常重要的原则,也是设计模式的精髓!

针对接口编程有数不清的例子,比如在Hibernate中,集合属性必须声明为Set、Map、List等接口类型,而不能声明为HashSet、HashMap、ArrayList等具体的类型,这是因为Hibernate在为了实现LazyLoad,自己开发了能实现LazyLoad功能的实现了Set、Map、List等接口的类,因为我们的属性的类型只声明为这些属性为这些接口的类型,因此Hibernate才敢放心大胆的返回这些特定的实现类。

现实的开发过程中有如下一些违反针对接口编程原则的陋习:

陋习1

ArrayList list = new ArrayList();

for(int i=0;i<10;i++)

{

list.add(……);

}

这里使用的是ArrayList的add方法,而add方法是定义在List接口中的,因此没有必要声明list变量为ArrayList类型,修改如下:

List list = new ArrayList();

for(int i=0;i<10;i++)

{

list.add(……);

}

陋习2

public void fooBar(HashMap map)

{

Object obj = map.get(“something”);

……

}

在这个方法中只是调用Map接口的get方法来取数据,所以就不能要求调用者一定要传递一个HashMap类型的变量进来。修改如下:

public void fooBar(Map map)

{

Object obj = map.get(“something”);

……

}

这样修改以后用户为了防止传递给fooBar方法的Map被修改,用户就可以这样调用了:

Map unModMap = Collections.unmodifiableMap(map);

obj.fooBar(unModMap);

Collections.unmodifiableMap是JDK提供的一个工具类,可以返回一个对map的包装,返回的map是不可修改的,这也是装饰者模式的典型应用。

试想如果我们把接口声明为public void fooBar(HashMap map)用户还能这么调用吗?

1.4 抽象类

抽象类的主要作用就是为它的派生类定义公共接口,抽象类把它的部分操作的实现延迟到派生类中来,派生类也能覆盖抽象基类的方法,这样可以很容易的定义新类。抽象类提供了一个继承的出发点,我们经常定义一个顶层的抽象类,然后将某些位置的实现定义为抽象的,也就是我们仅仅定义了实现的接口,而没有定义实现的细节。

一个抽象类应该尽可能多的拥有共同的代码,但是不能把只有特定子类才需要的方法移动到抽象类中。Eclipse的某些实现方式在这一点上就做的不是很好,Eclipse的一些界面类中提供了诸如CreateEmailField之类的方法来创建界面对象,这些方法并不是所有子类都用得到的,应该把它们抽取到一个工具类中更好。同样的错误在我们的案例的JCownewDialog中也是存在的,这个类中就提供了CreateOKBtn、CreateCanceBtn两个方法用来创建确定、取消按钮。

在设计模式中,最能体现抽象类优点的就是模版方法模式。模版方法模式定义了一个算法的骨架,而具体的实现步骤则由具体的子类类来实现。JDK中的InputStream类是模版方法的典型代表,它对skip等方法给出了实现,而将read等方法定义为抽象方法等待子类去实现。后边案例中的PISAbstractAction等类也是模版方法的一个应用。

在实际开发中接口和抽象类从两个方向对系统的复用做出了贡献,接口定义了系统的服务契约,而抽象类则为这些服务定义了公共的实现,子类完全可以从这些抽象类继承,这样就不用自己实现自己所不关心的方法,如果抽象类提供的服务实现不满足自己的要求,那么就可以自己从头实现接口的服务契约。

posted @ 2007-04-10 15:14 CowNew开源团队 阅读(1602) | 评论 (8)编辑 收藏

    CowNew开源团队是目前国内较活跃的开源团队之一,我们致力于基础平台的开发以及知识推广,目前已经有数个成熟的基础产品问世,并与国内数家网站、出版社建立了知识推广的合作关系。目前团队处于成长的转型期,急需有志之士加盟共同为中国的开源事业奋斗。
    我们需要如下几类队友:
    (1)基础平台开发工程师
     要求:对软件开发有很强的兴趣;有较多的时间能够投入到开源开发中来;有较强的计算机学科基础(编译原理、数据结构、操作系统等)或者熟悉Eclipse、NetBeans等平台的插件开发;能够胜任基础平台的开发;
     职责:SQL翻译器、SQL调优器、数据库监控、开发工具等基础平台的开发;
    (2)技术类图书作者
    要求:对软件开发有很强的兴趣;有较强的计算机学科基础(编译原理、数据结构、操作系统等);有实际的开发经验;有较好的书面表达能力;有较多的时间能够投入到书籍编写中来;
    职责:计算机学科基础类图书的编写;
    (3)外联人员
    要求:有较强的沟通能力和组织协调能力;有校园活动的组织经验;有较多的时间能够投入到团队的宣传工作中来;
    职责:团队校园活动的组织以及团队的对外宣传;
 
    注意:我们的团队目前只是一个非盈利性、非实体组织,目前仍然在发展的探索中,我们无法在短时间之类让您看到经济回报,团队成员也全部是利用业余时间从事这项工作的,所以如果您需要的一个能立即给您带来经济回报的全职工作,那么我们的团队并不适合您。CowNew开源团队的明天是美好的,如果您有创业精神,如果您有充足的时间和热情投入到这份具有前途的事业中的话,那么我们欢迎您的加入。
    团队在4月14日将会举办一次北京地区的聚会活动,希望新加入的队友能够参加我们的活动。
    有意加入团队的朋友请将您要加入的职位和您的基本情况通过Email发送给我们,我们的Email是:about521@163.com
    http://www.cownew.com
     thanks to dudu and other administrators.
  
posted @ 2007-04-10 14:33 CowNew开源团队 阅读(1097) | 评论 (0)编辑 收藏

      最近要做一个Python的基于Eclipse的界面设计器,因此我对各种GUI设计工具做了一下分析,发现GUI设计工具也有一个门派。
      在那个懵懂的年代,一切界面代码都是要开发人员手工书写,这无疑增加了开发难度,Delphi、VB等工具的出现扭转了这个局面,使用这些工具开发人员只要在控件面板上拖拖拽拽就可以完成界面的设计,做到了“所见即所得”的开发方式。仔细分析,GUI设计工具有如下几个门派:基于界面文件的纯代码生成、代码生成与界面文件结合、无界面文件方式。

      基于界面文件的纯代码生成:NetBeans是这类工具的典型代表(如果我没记错的话JBuilder也是这样实现的),NetBeans中与界面设计有关的有两个文件:.java文件和.form文件。.form文件中是以XML格式描述界面布局和控件的属性等信息;.java文件则是通过解析.form文件生成的代码,生成的界面代码主要位于initComponents方法中,这个方法在NetBeans IDE中是无法手工编辑的。在用户拖拉控件的时候,NetBeans就将拖拉的控件描述增加到.form文件中,并且即时将新的代码生成到.java文件中。这样实现的好处有如下几点:IDE实现容易,IDE的开发人员只要关注于如何将界面信息转化为.form文件和如何将.form文件解析生成.java代码即可,无需关心用户修改.java代码造成的反向解析问题;.java文件可以脱离.form而存在,也就是.form文件只是在设计期有意义,而在运行期是无用的。缺点是:用户无法手工修改生成的代码。
     代码生成与界面文件结合:Delphi和VB是这类工具的典型代表。以Delphi为例,在Delphi中新建以后界面以后将会存在两个文件:.dfm和.pas,.dfm描述了界面布局和控件的属性等信息,.pas则定义了控件的变量和事件处理函数。在编译的时候.dfm被编译到可执行文件中,运行的时候动态解析.dfm文件来构建界面。与NetBeans不同的就是.dfm文件是有运行期的意义的,如果没有.dfm文件文件,程序将无法编译运行。这样的方式通常只适用于Delphi、VB这样代码和IDE结合过于紧密的语言,很难将生成的代码进行手工修改。
  无界面文件方式:Eclipse的Visual Editor是最经典的例子。使用Visual Editor进行GUI绘制的时候,只存在一个.java文件,Visual Editor将用户绘制的界面直接解析为.java代码,如果用户修改了.java代码,Visual Editor会运行一个虚拟机,在虚拟机中运行用户修改后的文件,得到运行时的程序界面,然后将这个界面绘制到窗口设计器中。这样做可以将所有的界面信息都集成到一个文件中,并且支持用户手工修改生成的代码;由于设计器中的界面是通过另外一个虚拟机运行而得到的,在界面设计器中看到的界面就是运行时的界面,这样保证了真正的“所见即所得”。这样做的坏处也是明显的,由于需要重新启动一个虚拟机,导致了速度很慢,资源占用比较高,使用Visual Editor的时候经常造成Eclipse内存不足退出。   
 
      我在开发界面设计器的早期采用的基于界面文件的纯代码生成方式,系统中有一个.aui文件和对应生成的.py源代码文件,后来由于系统需求(主要是要求允许开发人员修改生成的代码),我就准备改用无界面文件方式。如果采用Visual Editor的无界面文件方式难度是比较大的,而且会导致资源占用太大,因此我采用了另外一种思路,也就是在内存中为每个界面维护一个对象模型(树状结构),在用户绘制界面的时候去修改这个对象模型,在用户保存界面的时候去解析这个对象模型生成.py源代码;在由.py源代码加载绘制设计器中的界面的时候,首先通过解析.py 源代码生成源代码的抽象语法树(AST),然后解析这个AST生成界面的对象模型,这样就可以很轻松的绘制界面了。这样做不仅有Visual Editor的优点,而且占用资源比较小;不过由于手工修改代码的千差万别,如果开发人员修改的代码采用了比较生僻的语法,有可能造成用户修改的代码无法正确的解析为对象模型,造成.py源代码加载绘制设计器中的界面的时候发生异常,解决这个问题的唯一一个办法就是建议开发人员尽量采用常用的代码来修改生成的界面代码。
      由于HTML代码本身就是一个树状模型,无需进行代码和模型间的转换,所以网页设计器就不存在上边说的这些帮派了。

 http://www.cownew.com CowNew开源团队 
 杨中科 email:about521爱特163.com
posted @ 2007-04-08 17:24 CowNew开源团队 阅读(3249) | 评论 (4)编辑 收藏

   IWorkbenchPage page = Activator.getActivePage();

   IAdapterManager mgr = Platform.getAdapterManager();
   String propPageName = IPropertySheetPage.class.getName();
   PropertySheetPage proppage = (PropertySheetPage) mgr.loadAdapter(
     page, propPageName);
   if (proppage != null)
   {
       proppage.refresh();
   }

posted @ 2007-03-28 15:22 CowNew开源团队 阅读(706) | 评论 (0)编辑 收藏

      可能大家常常会有这样的疑问,为什么我发的帖子始终没有人回复呢?这种感觉一定很让人沮丧吧?实际上,并不是没有人愿意伸出自己的手,但是这前提是,你要掌握提问的智慧,必须激起看你帖人的回答欲望。

      在提出技术问题前,检查你有没有做到:
1. 通读帮助文档,试着自己找答案。
2. 通过搜索引擎搜索。
3. 自己尝试进行问题解决。

      当你提出问题的时候,首先要说明在此之前你干了些什么,这将有助于树立你 的形象:你不是一个妄图不劳而获的乞讨者,不愿浪费别人的时间。一个问题如果提的好那么即使大家不知道怎么解决,也会帮你出谋划策,和你一起解决;如果一个问题提得不好,那么即使再简单,也没人搭理你!

      周全的思考,准备好你的问题,草率的发问只能得到草率的回答,或者根本得 不到任何答案。越表现出在寻求帮助前为解决问题付出的努力,你越能得到实 质性的帮助。

      决不要自以为够资格得到答案,你没这种资格。毕竟你没有为这种服务支付任何报酬。你要自己去“挣”回一个答案,靠提出一个有内涵的,有趣的,有思维激励作用的问题--一个对社区的经验有潜在贡献的问题,而不仅仅是被动的从他人处索要知识--去挣到这个答案。

      这是对自己的尊重,也是对别人的尊重。

posted @ 2007-03-28 10:59 CowNew开源团队 阅读(255) | 评论 (0)编辑 收藏

仅列出标题
共30页: First 上一页 12 13 14 15 16 17 18 19 20 下一页 Last