页面

2010年6月30日星期三

typedef的四个用途和两个陷阱

用途一: 
定义一种类型的别名,而不只是简单的宏替换。可以用作同时声明指针型的多个对象。比如: 
char* pa, pb;  // 这多数不符合我们的意图,它只声明了一个指向字符变量的指针,  
// 和一个字符变量; 
以下则可行: 
typedef char* PCHAR;  // 一般用大写 
PCHAR pa, pb;        // 可行,同时声明了两个指向字符变量的指针 
虽然: 
char *pa, *pb; 
也可行,但相对来说没有用typedef的形式直观,尤其在需要大量指针的地方,typedef的方式更省事。 

用途二: 
用在旧的C代码中(具体多旧没有查),帮助struct。以前的代码中,声明struct新对象时,必须要带上struct,即形式为: struct 结构名 对象名,如: 
struct tagPOINT1 

    int x; 
    int y; 
}; 
struct tagPOINT1 p1;  

而在C++中,则可以直接写:结构名 对象名,即: 
tagPOINT1 p1; 

估计某人觉得经常多写一个struct太麻烦了,于是就发明了: 
typedef struct tagPOINT 

    int x; 
    int y; 
}POINT; 

POINT p1; // 这样就比原来的方式少写了一个struct,比较省事,尤其在大量使用的时候 

或许,在C++中,typedef的这种用途二不是很大,但是理解了它,对掌握以前的旧代码还是有帮助的,毕竟我们在项目中有可能会遇到较早些年代遗留下来的代码。 

用途三: 
用typedef来定义与平台无关的类型。 
比如定义一个叫 REAL 的浮点类型,在目标平台一上,让它表示最高精度的类型为: 
typedef long double REAL;  
在不支持 long double 的平台二上,改为: 
typedef double REAL;  
在连 double 都不支持的平台三上,改为: 
typedef float REAL;  
也就是说,当跨平台时,只要改下 typedef 本身就行,不用对其他源码做任何修改。 
标准库就广泛使用了这个技巧,比如size_t。 
另外,因为typedef是定义了一种类型的新别名,不是简单的字符串替换,所以它比宏来得稳健(虽然用宏有时也可以完成以上的用途)。 

用途四: 
为复杂的声明定义一个新的简单的别名。方法是:在原来的声明里逐步用别名替换一部分复杂声明,如此循环,把带变量名的部分留到最后替换,得到的就是原声明的最简化版。举例: 

1. 原声明:int *(*a[5])(int, char*); 
变量名为a,直接用一个新别名pFun替换a就可以了: 
typedef int *(*pFun)(int, char*);  
原声明的最简化版: 
pFun a[5];  

2. 原声明:void (*b[10]) (void (*)()); 
变量名为b,先替换右边部分括号里的,pFunParam为别名一: 
typedef void (*pFunParam)(); 
再替换左边的变量b,pFunx为别名二: 
typedef void (*pFunx)(pFunParam); 
原声明的最简化版: 
pFunx b[10]; 

3. 原声明:doube(*)() (*e)[9];  
变量名为e,先替换左边部分,pFuny为别名一: 
typedef double(*pFuny)(); 
再替换右边的变量e,pFunParamy为别名二 
typedef pFuny (*pFunParamy)[9]; 
原声明的最简化版: 
pFunParamy e;  

理解复杂声明可用的"右左法则":从变量名看起,先往右,再往左,碰到一个圆括号就调转阅读的方向;括号内分析完就跳出括号,还是按先右后左的顺序,如此循环,直到整个声明分析完。举例: 
int (*func)(int *p); 
首先找到变量名func,外面有一对圆括号,而且左边是一个*号,这说明func是一个指针;然后跳出这个圆括号,先看右边,又遇到圆括号,这说明(*func)是一个函数,所以func是一个指向这类函数的指针,即函数指针,这类函数具有int*类型的形参,返回值类型是int。 
int (*func[5])(int *); 
func右边是一个[]运算符,说明func是具有5个元素的数组;func的左边有一个*,说明func的元素是指针(注意这里的*不是修饰func,而是修饰func[5]的,原因是[]运算符优先级比*高,func先跟[]结合)。跳出这个括号,看右边,又遇到圆括号,说明func数组的元素是函数类型的指针,它指向的函数具有int*类型的形参,返回值类型为int。 

也可以记住2个模式: 
type (*)(....)函数指针  
type (*)[]数组指针  
--------------------------------- 

陷阱一: 
记住,typedef是定义了一种类型的新别名,不同于宏,它不是简单的字符串替换。比如: 
先定义: 
typedef char* PSTR; 
然后: 
int mystrcmp(const PSTR, const PSTR); 

const PSTR实际上相当于const char*吗?不是的,它实际上相当于char* const。 
原因在于const给予了整个指针本身以常量性,也就是形成了常量指针char* const。 
简单来说,记住当const和typedef一起出现时,typedef不会是简单的字符串替换就行。 

陷阱二: 
typedef在语法上是一个存储类的关键字(如auto、extern、mutable、static、register等一样),虽然它并不真正影响对象的存储特性,如: 
typedef static int INT2; //不可行 
编译将失败,会提示"指定了一个以上的存储类"。 

2010年6月28日星期一

一种单例的写法

给构造函数传一个内部类参数
package{
    public class Manager{
        public var instance:Manager=null;
        
        public function Manager(private:Private){
            if (private != null)
            {
                if (instance == null)
                {
                    instance = this;
                }
            }
            else
            {
                throw new Error("Manager" + "单例");
            }
        }
    }
    
    class Private{
    
    }
}

2010年6月12日星期六

javascript中的几种异步实现方式

 
<!doctype html> 
<html lang="en"> 
<head> 
  <title>Async performance test</title> 
  <meta charset="UTF-8"> 
  <style> 
    pre {
      background-color: #eeeeee;
      padding: 20px;
    }
    div.test {
      margin: 20px;
      padding: 10px;
      border: solid black 1px;
    }
  </style> 
</head> 
<body> 
<div class="test"> 
<h3>setTimeout (slow, takes about 10 sec)</h3> 
<pre class="source"> 
function async(callback) {
  setTimeout(callback, 0);
}
</pre> 
<p> 
<input type="button" value="run" onclick="runtest(this.parentNode.parentNode)"> 
</p> 
</div> 
 
<div class="test"> 
<h3>img.onerror (data:uri)</h3> 
<pre class="source"> 
function async(callback) {
  var img = new Image;
  img.addEventListener('error', callback, false);
  img.src = 'data:,foo';
}
</pre> 
<p> 
<input type="button" value="run" onclick="runtest(this.parentNode.parentNode)"> 
</p> 
</div> 
 
<div class="test"> 
<h3>script.onreadystatechange</h3> 
<pre class="source"> 
function async(callback) {
  var script = document.createElement("script");
  script.type = "text/javascript";
  script.src  = "javascript:";
  script.onreadystatechange = function () {
    document.body.removeChild(script);
    callback();
  }
  document.body.appendChild(script);
}
</pre> 
<p> 
<input type="button" value="run" onclick="runtest(this.parentNode.parentNode)"> 
</p> 
</div> 
 
<div class="test"> 
<h3>script.onload (data:uri)</h3> 
<pre class="source"> 
function async(callback) {
  var script = document.createElement('script');
  script.onload = function() {
    document.body.removeChild(script);
    callback();
  }
  script.src = 'data:text/javascript,';
  document.body.appendChild(script);
}
</pre> 
<p> 
<input type="button" value="run" onclick="runtest(this.parentNode.parentNode)"> 
</p> 
</div> 
 
<div class="test"> 
<h3>xhr.onreadystatechange (data:text/plain,foo)</h3> 
<pre class="source"> 
function async(callback) {
  var xhr = new XMLHttpRequest;
  xhr.open('GET','data:text/plain,foo',true);
  xhr.onreadystatechange = function() {
    xhr.onreadystatechange = null;
    callback();
  };
  xhr.send(null);
}
</pre> 
<p> 
<input type="button" value="run" onclick="runtest(this.parentNode.parentNode)"> 
</p> 
</div> 
 
<div class="test"> 
<h3>self.postMessage</h3> 
<pre class="source"> 
function async(callback) {
  var n = ++async.count;
  window.addEventListener('message',function(e){
    if (e.data == n) {
      window.removeEventListener('message', arguments.callee,false);
      callback();
    }
  },false);
  window.postMessage(n, location.protocol + "//" + location.host);
}
async.count = 0;
</pre> 
<p> 
<input type="button" value="run" onclick="runtest(this.parentNode.parentNode)"> 
</p> 
</div> 
 
<script> 
function runtest(div) {
  var pre = div.getElementsByTagName('pre')[0];
  eval(pre.textContent || pre.innerText); // load async function
  var p = div.getElementsByTagName('p')[0];
 
  var t = new Date, n = 1000, i = 0, flag = true;
  try {
    test();
  } catch (e) {}
  setTimeout(function() {if (flag) p.innerHTML = 'failed'; flag = false}, 200);
 
  function test() {
    if (i++ < n) {
      async(test);
      if (flag && i == 2) {
        p.innerHTML = 'running';
        flag = false;
      }
    } else {
      p.innerHTML = "Average delay was " + (new Date - t)/n + "ms, over " + n + " iterations.";
    }
  }
}
</script> 
</body> 
</html> 

2010年6月3日星期四

面向异步消息的Web应用(AMOWA)

    第一反应:新瓶装旧药。不过在IT界,很多时候都是同样的技术不同的概念而衍生出一种新的事物,像AJAX,SOAP等,但世界就是这样一步步发展的,那些旧技术套上新的外衣会创造出一个新的生态圈,而在这个新的生态圈中又会滋生出新的技术,这就是进化!
    言归正传,Asynchronous-Messaging Oriented Web Application,这是一个很早的的技术了,大概在04年就提出来吧!只是在今天偶尔看到了,就再回味一下,从08年开始参加工作其实就接触的全是异步,什么消息啦,事件啦等,所以觉得一切就是理所应当,今天看到这个概念的提出,始知道什么技术都是有个过程的。
    说起来其实这个概念是由两个技术来支撑,消息与异步。消息就是把所有的调用都包装成消息,用消息来通知对方,而不是调用,比如:
以下是一个AMOWA接口的伪代码
* 客户端将操作包装成消息并发送:
    clientOperation.sendMessageBundle(msgBundle, callback);
    callback定义了消息的处理策略。
* AMOWA Gateway负责获取前台发送的消息,并处理
    messageBundle = buildMessage(request); //将Request包装成MessageBundle
    returnedMSGBundle = process(messageBundle); // 处理消息包
    sendReturnedBundle(returnedMSGBundle );  //发送处理结果
* 在process方法内部,简单的调用业务逻辑层的处理方式:
    bizDTO = bizService.doSomething();
    return bizDTO;
AMOWA Engine将会将bizDTO序列化为系统能够辨认的消息格式,生成消息包。

而在消息包中加入回调函数即构成了异步的消息处理

2010年6月2日星期三

flex与服务端通信的方式



    我们可以在基本的网络连接之上使用多种不同类型的管道进行客户端与服务器端的通信。基本远程过程调用使用的是标准AMF管道。
另一种通信形式就是消息,这样应用就可以推送来自于服务器端的消息并进行近乎实时的通信。代表性应用就是聊天服务器、拍卖客户端及协作服务。
Data services处理消息的主要方式就是轮询(polling)。由于HTTP上的标准通信并不会一直打开通信管道,这样一个轮询管道就会让客户端请求一 直等待服务器端,直到数据可用为止,其等待时间从几毫秒到几分钟不等。这么做就模拟了从服务器端推送数据的过程。
有两种基本的轮询管道:短轮询与长轮询。其主要区别在于服务器端等到客户端数据变得可用时所需时间的多少。
一种更高级的管道是流式AMF(streaming AMF)。它会打开到服务器端的HTTP连接并让服务器以流的方式在该管道上传输消息(消息的数量没有限制)。这么做就无需客户端轮询了,同时还能使用标 准的网络配置。该方式最接近于实时流。流式AMF的挑战在于它使用了HTTP 1.1的持续连接,而不同的浏览器对其的实现方式却不同。
最后一种管道就是RTMP(实时消息协议)管道了,目前只有LiveCycle DS对其提供了支持。Adobe最近宣布将要发布RTMP规范,由此我猜想它最终将会得到其他产品的支持。
设计RTMP的目的是在双向管道上以流的方式处理大量多媒体和数据。RTMP的一个主要好处是可以一直打开与客户端的连接,这样就可以推送服务器端的数据了。凭借这一点,RTMP可用于Comet风格的通信和实时的数据推送。
RTMP有三种形式。一种是基于TCP并使用1935端口,其底层实现要求在客户端浏览器上初始化连接。由于使用了非标准的端口,这样客户端防火墙经常会阻止其运行。
RTMP的另两种形式在HTTP请求内封装了RTMP消息,这样协议就可以穿越防火墙并使用标准的端口。这两种形式分别是RTMPT(用在标准的HTTP上)及RTMPTS(用在安全的HTTPS上)。
在Flex中,所有对服务器的调用都是异步执行的,因此这些管道都不会对客户端性能造成任何影响。然而他们却对服务器端性能有一定的影响,尤其是在 同时打开多个客户端连接的情况下更是如此。例如,流式AMF会导致服务器端打开大量并发的客户端连接,这也就意味着会产生多个线程。但如前所述,多个线程 的影响微乎其微。
所有的客户端连接都可以配置默认管道和备选管道,如果默认管道失败则可以切换到备选管道上。根据服务器端处理的不同通信类型,我们可以指定不同的管道链。例如,可以指定RTMP管道,但如果该连接失败,就回到长轮询管道。

结论

相对于Blaze DS来说,LiveCycle DS的真正优势在于其支持与数据管理,而额外的端点和管道所带来的优势却是颇具争议的。根据我们在Gorilla Logic所完成的项目来看,根本无需使用NIO端点或是RTMP。但从技术角度来看,没什么是确定的。我倒是想多点项目经历