Change
在无知的时候,我们常常会笑他人的肤浅。但终会明白,肤浅的是自己。

2010年8月24日

摘要: 最近在做一个EDI项目,主要流程就是客户以HttpPost或Webservice的方式向我们公司下订单,订单内容是以XML格式表示,我这边需要做的操作是:一: 验证请求是否合法(双方密钥)二: 验证请求内容是否正确且符合一定的格式要求三: 对订单进行处理 验证用户的请求是否合法以及对订单的处理就不说了,我今天主要说的是一种优雅、美观、清爽、干净的验证方式 对于XML的内容,我这边的处理方式是...阅读全文
posted @ 2010-08-24 00:07 Funeral 阅读(1750) 评论(27) 编辑

2009年4月20日

       现在很多程序员在面试的时候都遇到过这个问题---<猫叫了,老鼠跑了,主人醒了...>,实现一个连动效果,我也遇到过,感觉这道面试题目挺经典的,挺考验面向对象设计(OOD)的能力,虽然是个很简单的例子,但要考虑到程序的扩展性。比如说有新的需求,要求后面再加上狗叫了,那些写的过死且繁琐的代码就要来次大地震了;再比如说又变需求了,猫叫了是因为被跳蚤咬的,那跳蚤就成为了导火线,就算是用事件和接口写出的扩展性很强的程序,也会有点蔫了......

       这么一连串的反应是由一个行为所引起的,或者是猫叫,亦或者是一个按钮的点击引发,如何能让这一连串反映的扩展性更强,能更坚强的面对新的需求。这就需要一个更佳的思路,更佳的设计模式,今天无意想起了这个问题,也根据我的思路写了一套模式,下面就详细的说下我的想法:

       无论是猫叫,还是老鼠跑,都是一个行为,我们把这个行为抽象出一个基类:

    

 

Code

 

      现在我们再建一个中间层,用来处理这些行为:

 

 

Code

 

       现在我们一一实现猫、老鼠和主人(从基类继承):

 

 1 namespace NewCatAndMouse
 2 {
 3     public class Cat:BaseObject
 4     {
 5         public override void Action()
 6         {
 7             Console.Write(this.Name+"(猫)大吼一声!"+"\n");
 8         }
 9     }
10 }

 

 

 1 namespace NewCatAndMouse
 2 {
 3     public class Mouse:BaseObject
 4     {
 5         public override void Action()
 6         {
 7             Console.Write(this.Name+"(老鼠)仓惶逃跑!"+"\n");
 8         }
 9     }
10 }

 

 

 1 namespace NewCatAndMouse
 2 {
 3     public class Master:BaseObject
 4     {
 5         public override void Action()
 6         {
 7             Console.Write(this.Name+"(主人)猛然惊醒!" + "\n");
 8         }
 9     }
10 }

 

      三个实现类完成了。现在一一实例化,组合调用?不,那样客户端会显的臃肿而丑陋,有人说:代码是门技术,更是门艺术。所以我们的客户端代码应当越简洁越好。所以,我们把需要的东西写在配置文件里:

 

 

Code

 

 

      然后我们再做一个类来处理配置文件:

 

 

Code

 

       刚才说为了客户端的干净整洁,不要把过多的实例化放在客户端,所以我们就用反射来实例化类:

 

Code

 

       下面就是客户端代码了:

 

Code

 

         这样就可以了,如果需要新的子类直接继承基类,再在配置文件添加一个子类属性就可以了。而且可以再配置文件里自由的组合而无需改动客户端的代码,符合了开放--封闭原则。

         但由于用了反射,所以性能会有些差。而且如果实现类需要有新功能,就得在基类添加,如果功能太多基类就会变的臃肿不堪。所以,它也是有局限性的,最好是派生类不多而且行为较为统一。

         可能有人会说:那么简单的代码也好意思发上来。我想说:功能总能实现,就看怎样实现。这只是一个思路,由于更多的人把自己总结的不错的设计思路分享出来,所以我们才能进步。

         最近看设计模式有些上瘾,所以手痒也来凑凑热闹,但毕竟学程序的时间太短,经验太浅,所以可能有很多问题。希望大家帮我指正,希望能和大家一起努力,共同进步。

         附:让板砖来的更激烈些吧!

 

posted @ 2009-04-20 17:25 Funeral 阅读(2409) 评论(50) 编辑

2009年3月20日

    记得<倚天屠龙记>中有这样一段情节:张三丰向张无忌传授一套太极剑法,一路剑法使完,竟无一人喝彩,各人尽皆诧异:"这等慢吞吞、软绵绵的剑法,如何用来对敌过招"。还以为是张真人有意放慢了招数,好让张无忌瞧个明白。只听张三丰问道:“孩儿,你瞧明白了没有”,无忌答道:“看清楚了”。张三丰道:“都记得了没有?”,张无忌答道:“已忘记了一小半”。张三丰道:“好,那也难为你了。你自己去想想吧 。”张无忌低头默想。过了一会,张三丰问道:“现下怎样了?”张无忌道:“已忘了一大半了。"周颠等人皆急:“刚学的剑法都忘了一大半,这可如何迎敌”。便请张三丰重新传授一遍,张三丰微笑再使出一路相同的剑法,张无忌沉思一会,睁开眼:“我已忘的干干净净”。众人皆惊唯张三丰独喜,随即张无忌拿剑迎敌,大胜。


    金庸这笔用意之深,使得透露出一个真理:忘记。张无忌学太极剑,不记招式,只是细看剑招中“神在剑先,绵绵不绝”之意。看完一路剑法,已忘记了一小半。低头默想之后,已忘记了一大半`。再看张三丰演练一遍,再经沉思玩味,终于忘得干干净净。当全部忘记之时,也是学成之时,缍以之力克强敌。由记得转化为有如本能一般,终能不受原来招式所限,随意出招自成章法。


    让我想到现代背誉为中国IT商业领头羊、企业家的楷模的马云先生的一段语录:“三年以前我送一个同事去读MBA,我跟他说,如果毕业以后你忘了所学的东西,那你已经毕业了。如果你天天还想着所学的东西,那你就还没有毕业。学习MBA的知识,但要跳出MBA的局限。”


    忘记其形,悟得其意,乃至返璞归真,神乎其技。


    真正做到这种忘记何其难!需要你有着聪慧的头脑和超于常人的思维方式,跳出思维的局限和思想的束缚,从而达到另一个完全不同的境界。很多人十分用功,拼命强记,结果真的是“心有拘囿”,大受原来学说文字的拘束,真意反而完全错过,越是努力,所受缚束越深,令人深感可惜。


    佛教禅宗有句佛偈:“看山是山,看水是水,是为第一境界;看山不是山,看水不是水,方为第二境界;看山还是山,看水还是水,才为最高境界”,跟无忌学剑有着异曲同工之妙。


    张无忌是武侠小说中的人物,是虚拟出来的。普通人没有他那么变态的悟性,但无论是学武还是学文,其本质和境界都有着潜在的相似点,都要经历一个从简到繁,再从繁到简的过程。无论是哲学、文学还是科学,或是其他种种学科,我们刚入门、了解到一些皮毛的时候,或多或少的都会有一种“不过如此”、“原来这么简单”的浮躁心理,只是因为了解的太肤浅,是“看山是山,看水是水”的境界。当我们接触时间长了,遇到无数个问题和挫折的时候,开始逐渐了解到它的深意,就不再有刚开始的心态,而开始小心谨慎,好学善思,这时就是进入了“看山不是山,看水不是水”的境界,我们开始分析山为什么是山?水为什么是水?它们是由什么组成的?为何展现出这种不同的颜色和形态?第二境界是一个带着无数疑问,深入分析思考的过程。


    当我们在第二境界停留的够久,对一种学科了解的足够深的时候,多年的量变积累引起了质变,完成了一个质的飞跃,我们就达到了“看山还是山,看水还是水”的最高境界。到了这个境界,我们就不会去考虑山为什么是山、水为什么是水了,因为已经能从一个更高层次的角度上来看待它们,甚至创造它们。


    但,第三境界,却不是那么容易能达到的。我们大多数人,都停留在第二境界中苦苦的追寻,再寻觅的路途上迷失方向,或是经受不住挫折打击而停止前行。第二境界是一个迷宫,是一片布满了谜团和危险的原始森林,是一条蜿蜒崎岖、荆棘密布的道路,有着无数的分岔口,很多人穷尽一生也无法寻得正确的道路,在沐浴着真理的阳光的梦境中化为茫茫的白雾。也有人误以为成功的走了出来,拿到了真理的王冠,但公正的天平总会称量出赝品。


    正如马云所说:“今天很残酷,明天更残酷,后天很美好。但大多数人死在了明天晚上”。今天,明天,后天,比作这三个境界也是恰如其分。


    人类求知和探索的步伐从未停止过,翻开历史的书页,上面总会记载着人类中那凤毛麟角的几个。在秋水边逍遥游的庄子,在寂静夜空下沉思的苏格拉底.....等等(PS:为什么都是哲学家,难道就像修仙小说里说的那样,有无数个门派,但总只有那么一两个才是真正能得道成仙、修成正果的。或许也正是哲学的思想就是关于“天地人”的思考),无论怎么说,既然人活一世,就脱离不了这身皮囊,达到精神上的真正超脱和自由。哲学家也有饿肚子的时候,所以我并不是认为只有研究哲学才能达到最高境界。法国有句谚语:“人类一思考,上帝就发笑。”上帝给第二境界的人们布下了太多的陷阱与迷雾,所以他认为人类是永远走不出来的。所以当人们思考如何达到真正的最高境界时,上帝就象猎人看着猎物一样嘲笑它们的愚蠢,但终有一天,人类的思想超越了这个境界,那时估计上帝就笑不出来了。我并不赞同这句话,因为我认为它扼杀了人们的创造力,上帝只是一个代名词,代表着创造一切的伟大力量,但我坚信人类终会拥有这个力量(扯远了,扯远了......) 


    言归正传,我们大多数人都在第二阶段苦苦追寻,所以需要多研究一下前人的足迹,前人的经验和智慧,会对我们思想的道路提供很多正确的指引。让我们学会闭上双眼,去感受真理的方向,忘记那些路途上的障碍......只有真正学会了忘记,境界才能真正的升华。


    有人给程序员也分了几种境界,分别是:
第一阶段

此阶段主要是能熟练地使用某种语言。这就相当于练武中的套路和架式这些表面的东西。

第二阶段

此阶段能精通基于某种平台的接口(例如我们现在常用的Win 32的API函数)以及所对应语言的自身的库函数。到达这个阶段后,也就相当于可以进行真实散打对练了,可以真正地在实践中做些应用。

第三阶段

此阶段能深入地了解某个平台系统的底层,已经具有了初级的内功的能力,也就是“手中有剑,心中无剑”。

第四阶段

此阶段能直接在平台上进行比较深层次的开发。基本上,能达到这个层次就可以说是进入了高层次。这时进入了高级内功的修炼。比如能进行VxD或操作系统的内核的修改。这时已经不再有语言的束缚,语言只是一种工具,即使要用自己不会的语言进行开发,也只是简单地熟悉一下,就手到擒来,完全不

像是第一阶段的时候学习语言的那种情况。一般来说,从第三阶段过渡到第四阶段是比较困难的。为什么会难呢?这就是因为很多人的思想转变不过来。

第五阶段

此阶段就已经不再局限于简单的技术上的问题了,而是能从全局上把握和设计一个比较大的系统体系结构,从内核到外层界面。可以说是“手中无剑,心中有剑”。到了这个阶段以后,能对市面上的任何软件进行剖析,并能按自己的要求进行设计,就算是MS Word这样的大型软件,只要有充足的时间,也一定会设计出来。

第六阶段

此阶段也是最高的境界,达到“无招胜有招”。这时候,任何问题就纯粹变成了一个思路的问题,不是用什么代码就能表示的。也就是“手中无剑,心中也无剑”。


     哎,自我分析一下,我这个初出江湖的小菜鸟才刚达到第一境界,在程序这条道路上还有很长的路途要走,只能依仗着各位程序员前辈的指引和时刻提醒自己不要误入歧途了。啥时候能达到忘记代码、“手中无剑,心中也无剑”的境界呢,估计忘记代码很容易,但形成一种程序的思维就困难的多了(^-^)。

     附:刚才没有排版,很乱很丑影响阅读,对不住各位大大了

posted @ 2009-03-20 12:13 Funeral 阅读(2735) 评论(40) 编辑

2009年3月16日

  编程中经常会用到XML,.Net FrameWork提供了专门对XML进行处理的DLL,里面提供了很多对XML处理的方法,在这里简单介绍一下XPath的使用方法.

  XPath 使用路径表达式来选取 XML 文档中的节点或者节点集.类似于用正则表达式对文本进行目的性匹配.

  首先我们写一个普通的XML文档

<?xml version="1.0" encoding="utf-8" ?>
<school>
  <class>
    <number>1</number>
    <teacher property="English">Mr Sun</teacher>
    <student_count>50</student_count>
  </class>
  <class>
    <number>2</number>
    <teacher property="Chinese">Mrs Li</teacher>
    <student_count>35</student_count>
  </class>
  <class>
    <number>3</number>
    <teacher property="Math">Mr Zhang</teacher>
    <student_count>20</student_count>
  </class>
</school>

 

 XPath基本用法

表达式 描述
nodename 选取此节点的所有子节点
/ 从根节点选取
// 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置
. 选取当前节点
.. 选取当前节点的父节点
@ 选取属性

实例

在下面的表格中,我们已列出了一些路径表达式以及表达式的结果:

路径表达式 结果
school 选取 school元素的所有子节点
/school

选取根元素 school

注释:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径!

school/class 选取所有属于 school的子元素的 class元素。
//class 选取所有 book 子元素,而不管它们在文档中的位置。
school//book 选择所有属于 school元素的后代的 class元素,而不管它们位于 school之下的什么位置。
//@property 选取所有名为 property的属性。

 

  我们做一个控制台应用程序,来用XPath操作这个XML:

namespace XPathTest
{
    class Program
    {
        static void Main(string[] args)
        {
           
            XmlDocument doc = new XmlDocument();//创建一个XMLDocument对象
            doc.Load("..\\TestXML.xml");//根据XML文件的路径加载XML文件
        }

       
    }
}

 

  好了,现在我们需要得到number为1的班级里的所有信息:

   XmlNodeList list=doc.SelectNodes("//class[number=1]");   //获得XML里子节点number为1的节点class

   Console.Write(list[0]["number"].Name+":"+list[0]["number"].InnerText+"\n");

   Console.Write(list[0]["teacher"].Name+":"+list[0]["teacher"].InnerText+"\n");

   Console.Write(list[0]["student_count"].Name+":"+list[0]["student_count"].InnerText+"\n");

   Console.Read();

显示的结果为:

  number:1

  teacher:Mr Sun

  student_count:50

 

  可能会有朋友说道:我也没看到哪里简单啊,我还不如用While(XmlReader.Read())呢.别急,XPath的优势还没体现出来

 

  现在我们又变需求了,我们要找语文老师班上的学生人数,也就是property为Chinese的teacher的class的student_count(有点绕):

  XmlNodeList list = doc.SelectNodes("//class[teacher[@property='English']]");//匹配teacher节点里property为English的class

  Console.Write(list[0]["student_count"].Name + ":" + list[0]["student_count"].InnerText);

  Console.Read();

 

显示的结果:

  student_count:35

 

  我们又需要找班级人数不大与40的所有班级信息:

  XmlNodeList list=doc.SelectNodes("//class[student_count<40]");//匹配student_count小于40的class

  for(int i=0;i<list.Count;i++)

{

  Console.Write("匹配的第"+i+"个节点信息");

  Console.Write(list[i]["number"].Name+":"+list[i]["number"].InnerText+"\n");

  Console.Write(list[i]["teacher"].Name+":"+list[i]["teacher"].InnerText+"\n");

  Console.Write(list[i]["student_count"].Name+":"+list[i]["student_count"].InnerText+"\n");

  Console.Read();

}

 

显示的结果:

  匹配的第1个节点信息:

  number:2

  teacher:Mrs Li

  student_count:35

  匹配的第2个节点信息:

  number:3

  teacher:Mr Zhang

  student_count:35

 

   是不是很简单,这就是XPath的优势:不需要一次次的遍历,只需用一句表达式就能得到想要的结果

 

还有一些XPath的基本语法:

实例

在下面的表格中,我们列出了带有谓语的一些路径表达式,以及表达式的结果:(谓语是在方括号里的匹配条件)

路径表达式 结果
/school/class[1] 选取属于 school子元素的第一个 class元素。
/school/class[last()] 选取属于 school子元素的最后一个 class元素。
/school/class[last()-1] 选取属于 school子元素的倒数第二个 class元素。
/school/class[position()<3] 选取最前面的两个属于 school元素的子元素的 class元素。
//teacher[@property] 选取所有拥有名为 property的属性的 teacher元素。
//teacher[@property='English'] 选取所有 teacher元素,且这些元素拥有值为 English 的 property属性。
/school/class[student_count>35.00] 选取所有 school元素的 class元素,且其中的 student_count元素的值须大于 35.00。
/school/class[student_count>35.00]/title 选取所有 school元素中的 class元素的 teacher 元素,且其中的 student_count元素的值须大于 35.00。

选取未知节点

XPath 通配符可用来选取未知的 XML 元素。

通配符 描述
* 匹配任何元素节点
@* 匹配任何属性节点
node() 匹配任何类型的节点

实例

在下面的表格中,我们列出了一些路径表达式,以及这些表达式的结果:

路径表达式 结果
/school/* 选取 school元素的所有子节点
//* 选取文档中的所有元素
//property[@*] 选取所有带有属性的 property元素。

选取若干路径

通过在路径表达式中使用“|”运算符,您可以选取若干个路径。

实例

在下面的表格中,我们列出了一些路径表达式,以及这些表达式的结果:

路径表达式 结果
//class/teacher | //class/number 选取所有 class 元素的 teacher 和 number 元素。
//teacher | //number 选取所有文档中
/school/class/teacher | //number 选取所有属于 school元素的 class元素的 teacher元素,以及文档中所有的 number元素。
 
 
  XPath还有多个内置函数,分别是对字符串,值,日期等的处理和比较.这里就不一一说明了,XPath的魅力还不止如此.本人是初学者,希望能和大家一起努力
posted @ 2009-03-16 13:08 Funeral 阅读(558) 评论(1) 编辑

2009年3月9日

关于TCP通道的Remoting的基本使用方法:

一:首先导入命名空间System.Runtime.Remoting,然后
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;

建一个简单的测试接口和它的实现类
namespace RemotingTest
{
      public interface ISubject
      {
          string Say(string name);
      }
     
      public class Subject:MarshalByRefObject,ISubject
      {
          public string Say(string name)
          {
               return "Hi,"+name;
          }
      }
}
//因为Remoting传递对象是以引用的方式,所以所传递的远程对象类必须继承 MarshalByRefObject

建一个WinForm窗体作为服务器端,拖一个Button,在Button的点击事件里写上:
class ServerFrm
{
   private void Server_Click(object sender,EventArgs e)
  {

     //监听端口8001
     TcpChannel channel = new TcpChannel(8001); 

     channel.ChannelName="TestChannel";

     //注册这个TCP协议通道
     ChannelServices.RegisterChannel(channel,false);
     //在TCP协议通道里将Subject注册为已知类型,传递一段叫“SayHi”的Uri
     RemotingConfiguration.RegisterWellKnownServiceType(typeof(Subject),"SayHi",WellKnownObjectMode.SignleCall);
     MessageBox.Show("服务器开始监听");
  }
}

现在开始做客户端进行测试,先建一个接口ISubject

namespace RemotingTest
{
      public interface ISubject
      {
          string Say(string name);
      }
 
}
(注意,命名空间和接口名要和服务器端的完全相同)

同样建一个WinForm窗体,拖一个Button,在Button的点击事件里写上:

class ClientFrm
{
    private void Client_Click(object sender,EventArgs e)
    {
       string name = "Bob";
       //同样注册一个和服务器端通信的通道
       ChannelServices.RegisterChannel(new TcpChannel());
       //通过Activator.GetObject方法获得远程Remoting对象,这里的Url为本地8001端口的SayHi(Uri)
       RemotingTest.ISubject sub = (RemotingTest.ISubject)Activator.GetObject(typeof(RemotingTest.ISubject),"tcp://localhost:8001/SayHi");
       MessageBox.Show(sub.Say(name));
    }
}

二:在Remoting中的远程对象中,如果还要调用或传递某个对象,例如实体类,或者结构,则该实体类或结构则必须实现可序列化Attribute[SerializableAttribute],比如:

[Serializable]
public class UserInfo
{
     public UerInfo()
     {

     }
    
     private string name;
     private string sex;
     private int age;
    
     public string Name
     {
      get {return name;}
      set {name = value;}
     }

     public string Sex
     {
      get {return sex;}
      set {sex = value;}
     }

     public int Age
     {
      get {return age;}
      set {age = value;}
     }
}

将该远程对象以类库的方式编译成Dll,这个Dll将分别放在服务器端和客户端,以添加引用

三:注销通道
如果要关闭Remoting的服务,则需要注销通道,也可以关闭对通道的监听。在Remoting中当我们注册通道的时候,就自动开启了通道的监听。而如果关闭了对通道的监听,则该通道就无法接受客户端的请求,但通道仍然存在,如果你想再一次注册该通道,会抛出异常。
     
      //获得当前已注册的通道
      IChannel[] channels = ChannelServices.RegisteredChannels;
     
      foreach(IChannel channel in channels)
      {
         //关闭名称为TestChannel的通道
         if(channel.ChannelName == "TestChannel")
         {
             TcpChannel tcp = (TcpChannel)channel;
             //关闭监听
             tcp.StopListening(null);
             //注销通道
             ChannelServices.UnregisterChannel(tcp);
         }
      }


还有一种方法,相对上面这个较为复杂一点,但会大大简化服务器端和客户端的代码,因为要把需要的信息写在XML文件里。

按照上面的测试接口和类方法不变,用另一种方法实现Remoting的通信:

一:在服务器端建一个应用程序配置文件App.config,添加内容如下:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.runtime.remoting>
    <application>
      <channels>
        <channel ref="tcp" port="8001">
        </channel>
      </channels>
      <service>
        <wellknown type="RemotingTest.Subject,Server" objectUri="Subject.rem"  mode="SingleCall"/>
      </service>
    </application>
    <customErrors mode="Off" />
  </system.runtime.remoting>
</configuration>

其中channel节点指定了通信通道的类型和端口号,wellkonwn节点指定了type类型的格式:(命名空间.类,程序集名称)。

然后我们在服务器端的按钮点击事件里写上:
       private void Server_Click(object sender,EventArgs e)
       {
           using(System.Runtime.Remoting.RemotingConfiguration.Configur(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile,false))
             {
                  MessageBox.Show("服务器开始监听");
             }
       }

就这样一句代码,无论在XML里增加多少个wellknown节点,都能一次性读取出来

我们在客户端也建一个App.config,添上这样几句:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="Subject" value="tcp://localhost:8001/Subject.rem"/>
  </appSettings>
</configuration>

然后在客户端的按钮点击事件里写上:
        private void Client_Click(object sender,EventArg e)
        {
           RemotingTest.ISubject sub = (RemotingTest.ISubject)Activator.GetObject(typeof(RemotingTest.ISubject),System.Configuration.ConfigurationManager.AppSettings.Get("Subject"));
          
           Message.show(sub.Say("Bob"));
        }


OK,大功告成。

Http通道和Tcp通道的大体没什么区别,这里就不多做介绍了。本人也是Remoting的初学者,希望能和大家共同学习,共同进步!

posted @ 2009-03-09 17:45 Funeral 阅读(252) 评论(0) 编辑