2007年9月19日星期三

抽象类与接口

java里面,在什么情况下你会选择抽象类而不是接口?通常情况下,你能否解释抽象类的含义并说明什么时候使用他们?当你开始设计一个独立的系统的时候,当你准备深入面向对象编程的时候你一定会问自己这些问题。这个问题,乍一看是一个写程序的细节问题,但其实这是一个设计的问题。接口与抽象类的运用能够使你的程序变得更加的抽象,从而更加的灵活,更能够适应变化的需求。我想,这一点至关重要。


从语法的角度来看,抽象类和接口的差别好像仅仅是有没有具体方法的问题,当让这也是一个事实。这种微小的差别使得我们在使用他们的时候差生了疑问。我们会觉得尽然有抽象类为什么还需要接口,如果我把抽象类中的所有方法都定义为抽象的那不是和接口是一样的了么?是的,如果仅从语法的层面来看的却是这样。但事实上他们的用途是完全不同的。


抽象类被设计出来是要用于被继承。当我们设计任何一个抽象类的时候我们都要明白这一点,这个抽象类是被用来继承的。它必然是一个完成某种功能的树的一个根节点。我们可以看一下java中的util里的AbstractList这个抽象类。如下:

java.lang.Object
继承者 java.util.AbstractCollection<E>
继承者 java.util.AbstractList<E>
直接已知子类:
AbstractSequentialList, ArrayList, Vector
我们从来没有直接使用过这个AbstractList。但是我们常用它的子类,比如说Vector。乍一看好像util里面这个抽象类毫无用处,但事实上,如果我们需要自己实现一个特殊的List而有不想封装或者不能封装ArrayList或者Vector的话我们就可以从这继承,来写自己的List。
抽象类必然是一个功能体系的根,它实在有太多的好处。最根本的一条是它能够提供一些规约,用来约束由他派生出来的子类。比如一些共用的资源,共用的算法什么的。例如,假设你所在的公司开发了一套鸭子模拟游戏,它可以模拟各种不同的鸭子,在水上游泳,同时还能发出“嘎嘎”的叫声。我们就可以这样来设计,一个抽象的Duck基类,有发出“嘎嘎”叫声的Quack方法和在水里游泳的Swim方法,同时它还有一个抽象的 Display方法,每一个Duck子类(如MallardDuck、RedheadDuck)都将之重写,以便实现自己与众不同的外观。如下图:

你们公司有很多竞争对手,他们可不是吃素的,在日益增大的市场压力下,你们老板做出了一个决定,要改进这个游戏,让游戏里的鸭子飞起来,成功的话,一定可以打败所有的人。哦,这个艰巨的任务就交给你了。接到任务,你马上就开始了。这还不容易?在Duck基类里加一个Fly方法,这样所有的Duck子类都可以获得这个方法,所有的鸭子都可以飞了。太简单了,这就是抽象类的威力呀。

所以,抽象类就应该这么用。它生来就是要被继承的。如果不需要继承也就同样不需要抽象类。当然我们遇到的问题不可能如此的简单,往往具体的问题需要具体的分析才能真正的解决。而这个解决也同样不可能是完美的。这里我又想提一提Martin Fowler的《The New Methodology》这篇文章,里面谈到了很多。因为我们的软件不可能都像NASA的航天飞机的软件开发项目那样拥有不变而且确定的需求和在这个基础上完美的设计的。所以对我们系统的合理抽象可以说是至关重要的。就如同上面的鸭子项目一样,合理的抽象使我们面对变化而从容不迫。试想如果我们没有这个抽象会怎么样?一个类一个类的去改代码?那我们面对的就是一片森林了。项目因此就有可能陷入泥潭。

那么我们再来说说接口,接口有什么用处呢?其实接口也是为了应对变化而出现的。不过这个变化和上面的不同,接口应对的是可以预知的变化。也就是说,在我们设计系统的时候,我们知道这个地方一定会有变化,我们就可以采用接口的方式把这个变化保留下来。当然接口的目的是要和其他的对象进行配合的,如果你预料到的这个变化和系统的其他部分没有什么关系的话,那我认为你没有必要考虑它了。举个例子:java.io中的
FilenameFilter极为典型。我们只需要实现boolean accept(File dir, String name)方法,系统就可以根据我们的要求来进行过滤了。这里我还想提一个典型的错误接口设计,那就是常数接口,吧一堆常数放到接口里面去,需要的时候就让自己的对象来实现。这是一种晕头的设计。我的建议是不妨另写一个类就起名叫Constents好了,把所有的常数放到它里面。

其实抽象类和接口在jdk里面是在是非常的多。很多类包的设计都是含有若干接口若干抽象类的。可以知道,实际的系统往往是比较复杂的,如何设计其实取决于我们如何理解的系统,理解的是否正确。另外我们的设计也要充分合理的考虑到变化,不要去搞一锤子买卖。在软件设计中这个很不现实。毕竟人们对一个系统的认识不可能一步到位的。当然要应对变化,仅仅是抽象还是不够的,但这毕竟是最基本的。

困惑

昨天在查看自己原来写的程序的时候发现自己似乎模型建立的不够多。很多功能似乎还是由不同的方法相互配合来完成的。而不是对象之间的协作。这种情况让我觉得似乎很不愉快。我想这是一个对象划分粒度的问题。

面向对象要求封装,其实程序对于算法的依赖似乎就没有终结过。封装解决的是重用的问题,而不是算法的问题。通过对象的认识和分析,似乎我们找到了解决问题的简单方法,但其实没有。没有简单的方法,而利用模型来分析问题只是一种普遍的解决问题的方法,不一定局限与程序的设计上面。而恰恰程序设计方面所用的模型就很不直观。比如UML。UML解决建模问题,不仅仅针对与程序设计。而我个人认为,用UML来描述程序是很不直观的。特别是算法,用UML模型来描述算法,还不如用自然语言写在纸上或者用数学的描述方法来的更直观。

所以模型是出现在程序之外的。可以在任何地方,但最终要在你的脑里。
我们现在写的很多程序,其实用不太到很多的算法,大多数的情况都是将数据移动一下,或者做一些判断在移动一下。这些实在是太简单,用不到太多的数学或者物理知识。但是如果我们要写一个虚拟现实的仿真程序,恐怕就没有那么简单了。

2007年5月30日星期三

为客户实施报表

不幸

辛辛苦苦的为客户设计和编写了一套报表工具,却还要十分可怜的为客户实施。虽然也可以检测自己的程序,但是要知道,写程序的一般是不愿意给自己做测试的。唉。实施是个无聊之极的活。但是还得干。

2007年5月18日星期五

NetBeans难以搞定的字体


netbeans 的字体是一个令人伤心的问题。实际上netbeans的字体完全依赖于jdk的。而jdk又依赖于系统。由于我的系统字体有一些问题。主要是不知为何变得及其的不美观。所以只好去修改jdk的配置。总之是一个令人晕而又晕的问题。

jdk的字体依赖于jre。在jre的lib下。有字体的设置文件。修改设置文件即可。记得要去掉properties后面的src。

另外希望修改输出字体的人,有一个简单的办法,就是直接删除自己系统中的18030字体。

2007年5月10日星期四

Java泛型编程

JDK1.5 令我们期待很久,可是当他发布的时候却更换版本号为5.0。这说明Java已经有大幅度的变化。本文将讲解JDK5.0支持的新功能-----Java的泛型.

1、Java泛型

其实Java的泛型就是创建一个用类型作为参数的类。就象我们写类的方法一样,方法是这样的method(String str1,String str2 ),方法中参数str1、str2的值是可变的。而泛型也是一样的,这样写class Java_Generics<K,V>,这里边的K和V就象方法中的参数str1和str2,也是可变。下面看看例子:

//code list 1
import Java.util.Hashtable;
class TestGen0<K,V>{
public Hashtable<K,V> h=new Hashtable<K,V>();
public void put(K k, V v) {
h.put(k,v);
}
public V get(K k) {
return h.get(k);
}
public static void main(String args[]){
TestGen0<String,String> t=new TestGen0<String,String>();
t.put("key", "value");
String s=t.get("key");
System.out.println(s);
}
}

正确输出:value

这只是个例子(Java中集合框架都泛型化了,这里费了2遍事.),不过看看是不是创建一个用类型作为参数的类,参数是K,V,传入的“值”是String类型。这个类他没有特定的待处理型别,以前我们定义好了一个类,在输入输入参数有所固定,是什么型别的有要求,但是现在编写程序,完全可以不制定参数的类型,具体用的时候来确定,增加了程序的通用性,像是一个模板

呵呵,类似C++的模板(类似)。

1.1. 泛型通配符

下面我们先看看这些程序:

//Code list 2
void TestGen0Medthod1(List l) {
for (Object o : l)
System.out.println(o);
}

看看这个方法有没有异议,这个方法会通过编译的,假如你传入String,就是这样List<String>。

接着我们调用它,问题就出现了,我们将一个List<String>当作List传给了方法,JVM会给我们一个警告,说这个破坏了类型安全,因为从List中返回的都是Object类型的,而让我们再看看下面的方法。

//Code list 3
void TestGen0Medthod1(List<String> l) {
for (Object o : l)
System.out.println(o);
}

因为这里的List<String>不是List<Object>的子类,不是String与Object的关系,就是说List<String>不隶属于list<Object>,他们不是继承关系,所以是不行的,这里的extends是表示限制的。

类型通配符是很神奇的,List<?>这个你能为他做什么呢?怎么都是“?”,它似乎不确定,他总不能返回一个?作为类型的数据吧,是啊他是不会返回一个“?”来问程序员的?JVM会做简单的思考的,看看代码吧,更直观些。

//code list 4
List<String> l1 = new ArrayList<String>();
li.add(“String”);
List<?> l2 = l1;
System.out.println(l1.get(0));

这段代码没问题的,l1.get(0)将返回一个Object。

1.2. 编写泛型类要注意:

1) 在定义一个泛型类的时候,在 “<>”之间定义形式类型参数,例如:“class TestGen<K,V>”,其中“K” , “V”不代表值,而是表示类型。

2) 实例化泛型对象的时候,一定要在类名后面指定类型参数的值(类型),一共要有两次书写。例如:

TestGen<String,String> t=new TestGen<String,String>();

3) 泛型中<K extends Object>,extends并不代表继承,它是类型范围限制。

2、泛型与数据类型转换

  2.1. 消除类型转换

  上面的例子大家看到什么了,数据类型转换的代码不见了。在以前我们经常要书写以下代码,如:

//code list 5
import Java.util.Hashtable;
class Test {
 public static void main(String[] args) {
  Hashtable h = new Hashtable();
  h.put("key", "value");
  String s = (String)h.get("key");
  System.out.println(s);
 }
}

  这个我们做了类型转换,是不是感觉很烦的,并且强制类型转换会带来潜在的危险,系统可能会抛一个ClassCastException异常信息。在JDK5.0中我们完全可以这么做,如:

//code list 6
import Java.util.Hashtable;
class Test {
 public static void main(String[] args) {
  Hashtable<String,Integer> h = new Hashtable<String,Integer> ();
  h.put("key", new Integer(123));
  int s = h.get("key").intValue();
  System.out.println(s);
 }
}

   这里我们使用泛化版本的HashMap,这样就不用我们来编写类型转换的代码了,类型转换的过程交给编译器来处理,是不是很方便,而且很安全。上面是 String映射到String,也可以将Integer映射为String,只要写成HashTable<Integer,String> h=new HashTable<Integer,String>();h.get(new Integer(0))返回value。果然很方便。

  2.2 自动解包装与自动包装的功能

   从上面有没有看到有点别扭啊,h.get(new Integer(123))这里的new Integer(123);好烦的,在JDK5.0之前我们只能忍着了,现在这种问题已经解决了,请看下面这个方法。我们传入一个int这一基本型别,然 后再将i的值直接添加到List中,其实List是不能储存基本型别的,List中应该存储对象,这里编译器将int包装成Integer,然后添加到 List中去。接着我们用List.get(0);来检索数据,并返回对象再将对象解包装成int。恩,JDK5.0给我们带来更多方便与安全。

//Code list 7
public void autoBoxingUnboxing(int i) {
 ArrayList<Integer> L= new ArrayList<Integer>();
 L.add(i);
 int a = L.get(0);
 System.out.println("The value of i is " + a);
}

  2.3 限制泛型中类型参数的范围

  也许你已经发现在code list 1中的TestGen<K,V>这个泛型类,其中K,V可以是任意的型别。也许你有时候呢想限定一下K和V当然范围,怎么做呢?看看如下的代码:

//Code list 8
class TestGen2<K extents String,V extends Number>
{
 private V v=null;
 private K k=null;
 public void setV(V v){
  this.v=v;
 }
 public V getV(){
  return this.v;
 }
 public void setK(K k){
  this.k=k;
 }
 public V getK(){
  return this.k;
 }
 public static void main(String[] args)
 {
  TestGen2<String,Integer> t2=new TestGen2<String,Integer>();
  t2.setK(new String("String"));
  t2.setV(new Integer(123));
  System.out.println(t2.getK());
  System.out.println(t2.getV());
 }
}

   上边K的范围是<=String ,V的范围是<=Number,注意是“<=”,对于K可以是String的,V当然也可以是Number,也可以是Integer,Float, Double,Byte等。看看下图也许能直观些请看上图A是上图类中的基类,A1,A2分别是A的子类,A2有2个子类分别是A2_1,A2_2。

  然后我们定义一个受限的泛型类class MyGen<E extends A2>,这个泛型的范围就是上图中兰色部分。

  这个是单一的限制,你也可以对型别多重限制,如下:

class C<T extends Comparable<? super T> & Serializable>

   我们来分析以下这句,T extends Comparable这个是对上限的限制,Comparable< super T>这个是下限的限制,Serializable是第2个上限。一个指定的类型参数可以具有一个或多个上限。具有多重限制的类型参数可以用于访问它的每个 限制的方法和域。

  2.4. 多态方法

//Code list 9
class TestGen {
 <T extends Object> public static List<T> make(T first) {
  return new List<T>(first);
 }
}


2007年5月8日星期二

面向对象设计散谈
  1. 初衷

最早开始接触面向对象的设计应该是在今年6月份皖北项目中。在这之前我做的程序可以说完全谈不上设计,更谈不上面向对象。大多是一些jsp页面,将数据从一个地方存到另一个地方而已。皖北的情况有所不同,当时用户对业务有需求,不是能够用jsp页面或者以某种方式移动数据所能解决的。用户的要求可以被理解为建立一个树状的模型,模型中的每一个上级节点的值是它所对应的下级节点的值的合计。为了实现这个需求,开始了第一次面向对象设计的实践。


面向对象的程序设计的核心原则是“开—闭”原则。开闭原则讲的是系统应该对扩充开对修改闭。这很好理解,就如同我们盖楼房,大家都盖3层。如果我的设计考虑到了日后的扩充问题,我按5层的标准设计。可想而知,如果日后房子不够了要增加层数,我就直接在3层上面加几层就可以了。而其他的人可能要把楼拆了重盖。


面向对象设计的其他原则有:里氏代换,依赖倒转等。而“开-闭”原则则是这些原则的核心,其他的一系列的原则好像都是为了实现开闭而提出的。包括很多,比如在设计的时候要使对象尽可能的内聚,同时减少耦合;要适当的抽象,复用的部分被提取到抽象类中;要尽可能的针对接口编程,或者说针对变化编程。而且封装的意义也已经扩大了,封装不仅仅指数据的隐藏而是任何形式的隐藏,事实上封装的是变化,也就是说系统变化的部分将会被封装。这说起来很简单,而实际上要求我们改变我们分析问题的思路和看待问题的视角,而所作的一切都是为了和开闭原则靠拢,最终的目的是设计出灵活的健壮的易于理解并且高效的系统。


设计模式的提出,很大程度上也是为了实现这个目标。提到模式我想起了我放假回家时打麻将的经历,牌桌上的朋友其实也不比我聪明,学历也没我高,可是我就是打不过他们,他们对于麻将的技巧十分的娴熟,甚至可以通过手中的牌和打出的牌来推测各家手中的情况。同时他们也有很多术语,什么“夹三条”,“边二万”等等。失败的原因在于,我一年几乎只打一次麻将,而他们几乎天天都在牌桌上。他们对于麻将的认识和我相比是有很大的区别的,他们了解很多“模式”不需要计算,直接从手中的牌就能看出怎么打,而我却需要很多计算,才能决定如何出牌,最终失败成必然。人天生就具有认识模式的能力,模式是一种针对特定系统的高级的抽象,而对于模式的理解与运用,只有通过实践才能最终纯熟。


既然提到了模式,我还想谈谈上帝模式。其实也许程序设计的最高境界就是这个上帝模式。在我们的系统中,有一个全能的上帝,它可以完成系统所需要的所有任务。其他的程序只需要向他“祈祷”(发送一个请求)就可以得到所需要的结果。当然,世界上没有全能的上帝,程序里面也没有,我所知道的系统,没有一个是这样设计的,也无法这样设计。事实上,软件设计模拟的是人类的意识,特别是人类的认识活动,分析活动。系统必然会被分解,形成很多概念,或者说是实体,也就是对象。我们总会从中去寻找我们所熟悉的东西,然后通过类比,分析,归纳等方式最终认识一个系统。认识的过程是反复的渐进的,程序设计也一样,这就是为什么要提出重构的原因。


  1. ETL中间表导入程序

    1. 概述

我们主要要讲的是这个简单的etl中间表导入程序。这是淮北项目数据分析系统的一部分。这个程序的目的是从一张定义好的数据库中的表里面讲数据按照某一个规则到如到相关的事实表和维表中去。涉及到的主要问题有:


  1. 事务操作。

  2. 同步的增量问题。

  3. 实现不同的插入数据库方法,需要适当的抽象。

  4. 日志。

  5. 以恰当的方式制定规则。

这些问题是设计时要考虑的。


下图是系统最高抽象的一个类图:

1

系统设计整体分为三个部分,分别是:

  1. 数据抽取部分

数据抽取部分的功能是将数据用源数据库抽取出来,当然这里源数据库中的数据是按照我们的要求存放的,也就是说是一种清洗过的数据。通过数据的抽取,生成系统的元数据(MetaData)。这一部分对应的类就是Generater


  1. 数据包装过滤部分

系统的数据核心是MetaData(元数据),图一中的Container接口其实是一个数据容器,它和它里面包装的数据一起实现了系统的元数据。


  1. 数据操作部分

系统的操作核心是类MetaTransformer(元数据转换器),它其实是一个非常简单的类,他所描述的对象的功能也十分的简单,它的作用仅仅是把数据和操作连接起来。SourceType(源类型)是对数据源类型的一种抽象操作。针对不同的数据源类型,产生不同的操作。这种类型的区分,实际上是由操作人员指定的。对程序来说,它将按照一个固定的规则来产生SourceType的实现者。


下面我们对上面几个类的相互依赖做一个分析。上面这个类图是用eclipse的一个免费插件euml2分析出来的,这个插件尤其擅长分析依赖关系。在图一里面,除了MetaTransformer这个类以外,都是单向的依赖关系。类之间没有出现循环依赖。MetaTransformer之所以和很多类有依赖关系,是因为它本身就是承担连接数据和操作的责任,必须把数据与操作连接起来,所以它必须和数据部分以及操作部分关联。没有循环依赖,其实是一个很重要的设计细则,这样也十分有利与单元测试。

    1. MetaData (元数据)

2

元数据中保存了操作时所需要的主要数据。这样设计的目的是将数据提取和数据插入两部分的功能分开。同时让他们相互独立。在设计的时候有一个原则就是要尽可能的使程序的耦合性降低内聚性增高。其实就是讲要将程序中能够独立出来的功能尽可能的独立出来,这样做也是满足开闭原则的必要条件,如果你不这样做,程序耦合度极高,牵一发而动全身是不可能满足开闭原则的。


元数据是整个系统的一个核心,所有的操作都会围绕着它来进行。从设计上来看这个元数据类,好像就是《重构》里面提到的一种叫“纯稚的数据类”的坏味道。但我却不得不这样做,否则我的数据就会被打散放在数据库里了。所以我就是需要这么一个纯稚的数据类,这样也方便我操作数据。MetaData和它的容器Container一起构成了系统逻辑上的数据部分。这里有几点要注意,MetaData要设计的尽量简洁,内容越少越好,最好只有变化的数据和它的一个标识。而且它应该采用单例模式,保证它是唯一的,在系统的运行期间只有一个实例存在。


另外它有可能会非常的巨大,如果这样可能会对系统的健壮性有影响。因此在构建的时候应该有一个限制,目前这一部分功能没有实现。


这里面还要提到一下这个Container的抽象,Container抽象的目的主要是考虑到性能的问题。目前使用的数据容器是用Vector来实现的,众所周知,Vector的效率比较低下,但是使用起来顺手,如果考虑到性能的要求的话,这个数据容器还可以考虑用其他的方式来实现。当然这种抽象也应该通过一种配置由工厂方法来生成子类。但是这个也没有实现,所以这个抽象是不完整的。


另外还要说明的一点,就是MetaData这个对象是可以序列化出来的。借助jdk的功能,这个对象可以序列化成一个xml文件。这个功能没有严格的设计,实现的方法十分简单就是使用jdk里面的java.beans.XMLDecoderjava.beans.XMLEncoder这两个类,其中一个是编码,一个是解码。通过他们可以把对象生成一个xml文档。

    1. Generater (生成器)

3

生成器的作用是将数据从一个源按照指定的规则抽取出来。如果纯粹是针对数据库操作的,其实也没有抽象的必要,这样设计其实是考虑如果需要从其他的数据源抽取的时候可以直接增加一个子类而不必改程序其他的部分。当然要实现这个目的至少还要配备一个工厂方法,以一种方式(通常是配置或者用户界面的指定)来决定到底是什么数据源。这个工厂方法没有实现,因为觉得出现这种需求的情况可能性比较小。所以其实这个抽象是聋子的耳朵——纯粹的摆设而已,没有什么实际的意义。


生成器里面有一个比较重要的操作就是要实现增量查询。我采用的方法是将源表中的时间戳序列化出来,成一个xml文档。这样做的坏处是较为复杂,好处是有了一个将源表中需要的数据导出的方法。另外仅操作源和目的无关,有助于减少系统之间不必要的耦合性。这里面还有一些设计的细节,比如不同的数据库对日期的操作是不同的,这里需要区分数据库,区分的方法采用webber.core里面的方法。就是通过分析配置文件配置项来产生多态。这里不细说了。

    1. MetaTransformer (元数据转换器)

4

数据转换的核心就是SourceType这个类型。它被设计为一个接口。因为我虽然知道源数据的类型,但是不能保证源数据如同我们想象的那么单纯,它有可能会比较复杂和特殊。所以我做了一个接口,这样可以保证SourceType类型在使用的时候系统其他部分不会受SourceType内部变化的影响。如果我们需要为SourceType增加一种功能,可以写一个实现这个接口的类就可以了,而系统的其他部分则不会受到影响。当然,由于SourceType的工厂方法负责根据条件产生它的子类,所以这个工厂方法也会受到影响,但总的来说,变化是可以控制的。不会扩散到系统的其他部分。


具体看一下这个抽象层次的设计。有一个抽象类SourceTypeDim用来描述维的操作。因为根据需求源数据的存放有三种可能:它是事实(SourceTypeDimBase)、他是维度(SourceTypeDimNone)、他是具有父子关系语意的维度(SourceTypeDimClass)。注意,这里使用了面向对象设计原则里面的依赖倒转原则,就是说复用的部分放在了抽象类里面。也就是说,元数据存放的三种可能所对应的三种操作其公用的部分被放在了抽象类里面。这样做的好处是减少冗余的代码,坏处是提高了抽象和具体的耦合性。所以这里面就有一个抽象层次划分的问题。我的设计明显少一个层次。应该设计为两个平行的抽象类,一个代表没有语意的维度描述,一个代表有语意的维度描述。但是由于有语意的描述只有一种情况,所以就暂时没有这么做。


具体什么时候实例化什么子类是由一个简单工厂方法SourceTypeDimFactory(参看图一)来决定的。它会读取配置文件,根据配置文件里面的描述来决定实例化那一个子类。


工厂方法——以上老是提到工厂方法。工厂方法是设计模式中的一种对象构建型模式,就是为生成子类所准备的。工厂方法的好处是把子类的产生变成一种规则,以后我们只要通过这种规则来构建子类,就不用担心其他的问题了。

例如:

这是我用的方法,为SourceType这个类型实例化子类。我是通过制定了一个类名的规则来实例化子类的。这是一种偷懒的很不好的方法。比较好的方法应该是通过配置文件,采用注册的机制,安全的产生子类。我这样做是因为这个继承关系十分的简单,也就是说SourceType这个类型的产品十分单一,特别的简单,所以工厂也相对简单了。

    1. 系统的其他组成

系统的其他组成主要有:XML描述文件、系统配置文件(未实现)、日志系统、系统全局常量、工具箱。


XML描述文件,用来描述数据源的特征,包括提取的sql语句,以及字段的含义。


系统配置文件,用来实现系统的一些配置,比如Container使用哪个子类,日志是否需要在后台黑窗中显示等。


日志系统,使用jdk提供的日志类,为系统增加日志。


系统全局常量,作用和配置文件一样,由于没有实现配置文件,就用它替代了配置文件。


工具箱,包含了一些需要的工具。比如一些转换,计算等等。

  1. 小结

在这篇文档里面,我提出了我对于面向对象程序设计的一些想法,这些想法很多都是幼稚的,不成熟的。虽然我也读了很多关于设计方面的书籍,但是我觉得程序的设计始终都是一种需要实践的艺术,动手比什么都重要。设计的核心其实还是人本身。一个系统设计的好还是坏,关键在于设计者对于系统的理解程度,没有理解就淡不上分析更谈不上设计。


正如我前面所谈到的人的认识过程是一个渐进的过程,所以重构在设计里面占一个极大的分量。上述的程序实际就是经过多次重构才最终成型的,而且目前也有很多的不足。因为这是一个客观的情况,所以我们在设计的时候切忌对象间的耦合度过高。耦合度越低越利于重构。同时我们也不要妄想一下子设计出一个完美的系统,毕竟人也不是上帝。此外还要学会利用别人已经完成的功能,在我的程序里面XML解析,日志生成都是使用了别人的成果。不要想着大包大揽,毕竟站在巨人的肩膀上才更容易成功。




2007年5月7日星期一

哭泣的美女与剧痛的嗓子

昨天晚上老婆又痛哭流涕了。原因是我有的时候非常狠心。原来我对她的伤害她不曾忘记,她对于我的不满在不断的升级。生活就是要不断的谅解,人不能总生活在过去的阴影里面。谁没有犯过错误?这个世界上没有完美无缺的人,因此我们也不能要求完美无缺。我就懂得这一点,我从来不要求什么完美无缺的。不断撕扯过去的伤口,那么人永远也不能恢复健康。健康的人才是有用的人。

其实这也没有什么,我有的时候觉得自己很对不住我的老婆。主要原因就是我不够强势。

其实我老婆这个人是一个普普通通的人我喜欢。她很敏感,容易受到伤害。

原谅别人是一种美德。不能原谅的话就要离开。如同能活着就活着,不能的话就去死。

嗓子剧痛无比,晚上反反复复的做这一个同样的梦。为什么让我这么痛苦?我从来不曾伤害别人。当然我知道这个不是理由。不爱自己就是一种罪过。

男人不能没有女人,如同女人不能没有男人一样。这是一个什么道理呢?不知道。我也不懂。就觉得这是一个规律。

2007年4月18日星期三

2007年4月18日-现实与梦幻

我时常迷惑在现实与梦幻之间。原因说来也简单,就是现实不能为我带来快感。而梦幻似乎可以,至少我能够将自己想像成一个伟大的了不起的人物。然而这一切又有什么意义呢?古时候有一个黄粱美梦的故事,这个故事的主角在梦中完成了他的另一个人生,不知道当他醒来的时候,他是会有什么感想呢?人一定q要有所追求。当我在我的梦境中流连忘返的时候,我知道自己没有追求,不知道自己想要干什么,做什么。前一段时间我去了相山上的那个正在翻修的庙宇,在庙的里面,刚一进庙门的时候,墙上就有一些所谓的劝世歌和醒世诀无非都是一些告诉我们通常我们所追求的都是空的无聊的话。不过说句实话,我的的确确没有什么追求。难道我想死么?我也不知道。我好困惑。

我是一个需要快乐的人,没有快乐我觉得自己活着没有什么意义了。然而,快乐在哪里呢?

当我坐在这里思考的时候,我觉得自己感觉非常的不好,有一种孤独的痛苦在折磨着我。


2007年4月16日星期一

控欲

人要控制自己的欲望,要有所追求

很多话说起来容易,但是做起来比较难,有的时候甚至做也不知道如何去做,这恐怕是一件非常遗憾的事情。就如这篇文章的开头一样,说着容易而且很有道理,但是去做却很难。当一个人没有什么特定的追求的时候,我想他可以简单一些,追求名利。追求利是最朴素的追求,因为人要生存,生存依赖于物质,利就是物质。所以追求名利是人的本能,也是社会进步的基本

你可能不知道你要追求什么,其实这没有关系,好好的过好每一天,做好自己的本职工作,不抱怨,不牢骚。这是基本。然后就是追求。这是从无到有的过程,首先什么也没有,然后渐渐什么都有了。因为人首先要了解自己,要体悟生活。要了解这个世界。人生存在世界上,鉴于认识有意识的,所以实际上你所关注的不可能仅仅是当代现在发生的事情。我这样说,是因为人要了解自己是很困难的。人生在一个社会环境中,这个环境很复杂,而且时间的延伸增加了这种复杂性。算了,不说了,也许你已经了解我的意思了。呵呵。

人无论高下都有追求,只是追求的境界有所不同,这个境界其实是对个人而言的。也没有好坏高下之分。

2007年4月12日星期四

至乐无乐,至誉无誉

无的思想是一种奇高的境界。正如孙子所说:“善战者,不战而屈人之兵。”;而善战者无赫赫之功,因为他能够防患于未然,在问题还没有发生的时候,就把问题解决了。这些心灵的修养之道来自于古代,战国时期,距今已有2000多年了。能不感慨么?

无为之为

无并非真正的无,是精神的无,而不是物质的无。人不可能离开物质,但是人的精神是独立的。人的精神其实是物质世界的反映,人的一切都不可能离开物质。我们所说的道,其实也是物质世界的规律,所谓真理,指的也是物质世界的真理。不管你怎么想,你是不可能离开这个物质世界的。你的精神可以和物质对抗,但是你的身体不行。人的精神也依托于物质存在。而物质是什么的?物质是没有意识的。它依规律而行,没有自己的意识。天地不仁以万物为刍狗。相对于物质世界,精神就是无,是不存在的。是我们自创的规律。呵呵,说不清楚。我都不知道自己在说些什么,但这些就是自己的一些感悟。很难确切的描述出来。借用庄子中的一句话:“若夫不刻意而高,无仁义而修,无功名而治,无江海而闲,不导引而寿,无不忘也,无不有也,淡然无极而众美从之。此天地之道,圣人之德也。”这种行为很符合自然之道。所以每个人都要找到自己的道,每个人存在的意义都是不同的,这个需要你自己去寻找。

道常无为,而无不为

这是老子说的。我特别想问,天地是否也有追求。天地是没有追求的,因为天地是没有意识的。物质世界依法而动,它是没有意识的。而人不同,人可以有追求,因为人有意识,人不可能无为而无不为。

事实上我觉得道家的思想其实是很唯物的。它说的无,不是什么都不干,而是要求你顺应自然,依规律办事。做事情要符合客观规律,这样才能把事情干好。

逍遥而游,无为而为

君子误国 :公但知小人能误国,不知君子之尤能误国也。小人误国犹可解救,若君子而误国,则未之何矣。何也?彼盖自以为君子而本心无愧也。故其胆益壮而志益决,孰能止之。

2007年4月9日星期一

好吧 ,其实我想说这仅仅是我的一个测试。

自我以及欲望

我和我的欲望

多数情况下,我似乎希望了解欲望的缘由,其实大多的时候,欲望没有缘由,仅仅是一种心理和潜意识的需求而已。目前我接受这方面的知识是非常的稀少的。由于所知甚少所以难以明了什么。我到底希望的到什么,而我又能够得到什么呢?

每个人都有自己的欲望,我不知道别人怎么样,仅仅就我自己而言,我觉得我自己还是比较正常的人。我对金钱有欲望,对性有欲望,大多数时候性的欲望还要大于金钱的欲望,因为性的欲望比较容易得到满足,而且还能带给人快感,这种快感不是金钱能够代替的。

其实,任何时候,虚妄的东西都不会是好的。我们应该着眼于实际,看看现实世界中有没有能够让自己得到满足的东西?满足什么?自己的虚荣心。说道虚荣,其实每个人都有虚荣心。没有虚荣的人其实是不存在的。有关系么?其实没关系。虚荣也好,也许正好是我们所需要的。

有的时候我们看历史,读小说,都是为了得到某种体验,或者说是一种虚幻的快感,当然这种快感也不一定是虚幻的。古人说:“书中自有黄金屋,书中自有人如玉。”我 觉得这种描述好像是在说小说,读小说有意义么?没有,读小说完全是为了一种娱乐呀。人需要娱乐么?我觉得不一定需要,但是人不能总生活在压力下面。固然人需要有所追求,但是有的时候也需要放松。

我只是想说,其实对于我们的欲望我们要量力而为,不能放纵,不能纵欲。同时应该利用自己的虚荣去获得让自己进步的力量。自古以来人们常常提到要修心养性,但是如何做到?一直以来也没有一个什么固定的好的方法可以使用。不过我也不知道。所以也没法说。

前一段时间看小说,一个牛人写的:徐公子胜××;有文化,虽然是网络娱乐文学但是写的比一般的yy强得多了。我觉得写书就应该有那样的水平。小说也不应定都不好吧。这个书中在谈道和如何修炼。神通我当然是不信的。就算有一般人也不可能很容易的得到,还是安心工作赚钱来的实际一些。但是修炼如果是一种内心的感受我觉得很多时候还是比较有道理的。所谓修行就是一个过程,认识真的过程。什么叫真?可以解释为真理。真理就是恒古不变的道理尽可以是唯物的,也可以是唯心的,没有强求一定是什么。修炼追求的当然不是什么神通,神通怎么会有呢?提高自己的能力还是比较实际的。比如说理解能力和计算能力。而其实这些也不是那么好得到的,也同样需要自己去努力。因此一个有所追求的人还有什么时间去娱乐自己呢?没有时间。当你把大把的时间奢侈浪费掉之后,也许你会后悔。

这仅仅是一些散记,我想我其实也没有什么太理论化的东西而自己所理解的哲学的体系,比如说认识论和辩证法以及唯物论都是前人的思想就算自己理解了也不能够算作是自己的创造吧。

一切从实际出发我看到了什么?仅仅是时间的流逝么?