Posts Tagged ‘式’

在IE中为abbr标签加样式

星期三, 06月 4th, 2008

作者:JunChen 2005-5-24 9:56:57
原文:http://www.sovavsiti.cz/css/abbr.html
翻译:JunChen

版权:译者JunChen所有,转载请联系译者.
简介

<abbr>是用来为web页面上de简称(译者注:这里把简称和缩写分开而论,简称范围比缩写大,取首字母de缩写用<acronym>标签)添加适当标注deXHTML标签,WindowsdeIE浏览器暂不支持<abbr>标签. 在IE里,您可以应用CSS给<acronym>但是不能应用给<abbr>标签,IE会为<acronym>标签detitle属性显示提示,但是会忽略<abbr>标签.

这个IEdebug(或者特色)使得一些网站人员认为<abbr>标签一点用都没有,而显然这么认为是不对de.在Mozilla和Opera里还是很正确de处理了这个标签,并且它对于web内容de可读性和语义化来说非常重要.这也是我为什么一直在寻找解决方法,最终我找到了.

解决方法

本方法基于一个简单de事实:即使IE会忽略<abbr>标签,但是其他嵌套在<abbr>标签里de标签还是正常de.所以我在嵌了一个<span>标签在<abbr>里,设置<span>detitle和class属性,然后<abbr>开始变得和<acronym>标签一样了.

相关代码例子

看一下下面de相关代码,是一个简单de缩写词de例子:

<abbr title=”Cascading Style Sheets”>CSS</abbr>
现在,对比一下修改后de相关代码:

<abbr title=”Cascading Style Sheets”><span class=”abbr” title=”Cascading Style Sheets”>CSS</span></abbr>
自动操作

手动de给每一个<abbr>标签嵌入<span>显然不可能——既无聊又对Mozilla和Opera没必要.幸运de是,现在有一个自动de、基于客户端脚本de解决方法.

您可能注意到了,这个页面(译者注:原作者de页面)上de简写词语即使在IE里都会有提示,并且加了CSS样式(虚下划线和一个问号状de鼠标光标).然而您如果看一下源相关代码,您将找不到在上文提到de<span>标签.这得益于本页加载de一个简单deJavaScript:

function styleAbbr() {
var oldBodyText, newBodyText, reg
if (isIE) {
oldBodyText = document.body.innerHTML;
reg = /<ABBR([^>]*)>([^<]*)<\/ABBR>/g;
newBodyText = oldBodyText.replace(reg, ‘<ABBR $1><SPAN class=\”abbr\” $1>$2</SPAN></ABBR>’);
document.body.innerHTML = newBodyText;
}
}
window.onload = function(){
styleAbbr()
};

isIE = (document.all) ? true:false;

这段脚本会检查客户端浏览器,如果是IE,那么则替换所有de<abbr>标签为修改过de版本(嵌入了<span>).注意de是我必须使用正则表达式和innerHTML属性来取代标准deDOM方法,因为IE不能通过DOM来获取<abbr>属性.

样式化

最后看一下这个页面上使用deCSS.相当简单:

abbr, acronym, span.abbr {
cursor: help;
border-bottom: 1px dashed #000;
}
Mozilla和Opera使用abbr和acronym属性选择器,IE则使用acronym和span.abbr.无论如何,<abbr>和<acronym>都被样式化了——一个问号状de鼠标光标(当鼠标指上后)和虚下划线.

其他

1.感谢Michael Kusyn提供了JavaScript解决方法.
2.更多关于<abbr>,<acronym>标签和两者de区别,参考Craig SailadeHTML is not an acronym… (Evolt.org)

欢迎交流意见评论,可以发邮件至marek@sovavsiti.cz.

引入CSS样式的五种方式

星期三, 06月 4th, 2008

一、使用STYLE属性

将STYLE属性直接加在个别de元件标签里,<元件(标签) STYLE=”性质(属性)1: 设定值1; 性质(属性)2: 设定值2; …}

例如:
<TD STYLE=”COLOR:BLUE; font-size:9pt; font-family:”标楷体”; line-height:150%>
这种用法de优点 是可灵巧应用样式於各标签中,但是缺点则是没有整篇文件de『统一性』.

二、使用STYLE标签

将样式规则写在<STYLE>…</STYLE>标签之中.
<STYLE TYPE=”text/CSS”>
<!–
样式规则表
–>
</STYLE>
例如:
<STYLE TYPE=”text/css”>
<!–
BODY {
  color: BLUE;
  background: #FFFFCC;
  font-size: 9pt}
TD, P {
  COLOR: GREEN;
  font-size: 9pt}
–>
</STYLE>
通常是将整个de <STYLE>…</STYLE>结构写在网页de<HEAD> </HEAD>部份之中.这种用法de优点就是在於整篇文件de统一性,只要是有声明dede元件即会套用该样式规则.缺点就是在个别元件de灵活度不足.

三、使用 LINK标签

将样式规则写在.cssde样式档案中,再以<LINK>标签引入.
假设我把样式规则存成一个example.cssde档案,我只要在网页中加入
<LINK REL=STYLESHEET TYPE=”text/css” HREF=”example.css”>
即可套用该样式档案中所制定好de样式了. 通常是将LINK标签写在网页de<head></head>部份之中.这种用法de优点就是在於可以把要套用相同样式规则de数篇文件都指定到同一个样式档案即可.缺点也是在个别文件或元件de灵活度不足.


四、使用@import引入

跟LINK用法很像,但必 放在<STYLE>…</STYLE> 中.
<STYLE TYPE=”text/css”>
<!–
  @import url(引入de样式表de位址、路径与档名);
–>
</STYLE>
例如:
<STYLE TYPE=”text/css”>
<!–
  @import url(http://yourweb/ example.css);
–>
</STYLE>
要注意de是,行末de分号是绝对不可少de!

五、使用<span></span>标记引入样式

例如:<span style=”font:12px/20px 宋体 #000000;”>WEB标准</span>

W3C标准:实时切换CSS样式

星期三, 06月 4th, 2008

用W3C标准建造de网站,从理论上来说可以做到完全de表现与结构相分离.打个比方,就是可以在不动骨架(结构,XHMTL)和肌肉(行为,Javascript)de前提下,彻彻底底地换一身皮(表现,CSS).

当然,换皮之前您需要先按W3C标准建好您de网站,并且为它准备两套表现不一样deCSS.“换皮”实质上就是“换CSS”,我要做de,只是用某种方法让浏览器载入另一套CSS,重新渲染页面.方法有很多种,我就介绍最常见de三种.

方法一:什么也不干

啊?什么也不干?嗯,这个……准确地说是:就干那么一丁点儿 (您还真以为有这么好de事儿呀……).

假设我有两套CSS,分别封闭在两个不同de文件中:a.css和b.css.然后在<head>和</head>之间加入如下两行XHTML相关代码:

然后用您deFirefox打开这个页面,在菜单栏中选择:查看 -> 页面风格,应该可以看到如下de“风景”:

就这么简单,现在您就可以用Firefox来“换皮”了.IE?IE没这个功能……MS就是这么拽,W3C“明文推荐”:要求浏览器提供给用户自己选择样式表de权力,可它就不这么干.幸运de是这件事也不是太难杂,咱就代劳一下吧.

[separator]

方法二:Javascript

在方法一de基础上,可以用JavascriptdeDOM方法访问link对象,再将不需要deCSS设为“禁用(disabled)”,剩下deCSS就会被浏览器用来渲染页面.脚本如下,请注意其中de注释:

然后在合适de地方调用这个函数,以本页为例,添加如下两个按钮:

<input type=”button” value=”清光” onclick=”setStyle(”清光”);” />
<input type=”button” value=”冥焰” onclick=”setStyle(”冥焰”);” />
  

使 用Javascriptde好处是方便、快捷、简单,缺点也是很明显de:很难做到全站deCSS切换,只能局限在当前页上.为了记忆用户de选择,可行de方案就 是采用cookie.可是就算使用cookie,也需要在何时载入CSS,用户没有Javasciprt支持怎么办等问题上多做好些文章.所以不如用下面 de方法——

方法三:服务器端脚本

毫无疑问,最好deCSS切换器应该使用服务器端脚本(PHP、ASP、JSP等)来开发.这样做de好处是很明显de:直接、高效、兼容性好、可以记忆用户选择、甚至可以组合不同deCSS实现相当复杂de“皮肤”切换.

我这里就用PHP为例,用其他de语言也都大同小异,对一般de开发人员来说不会有任何困难.

基本思路是这样:用户选择一种“皮肤”,把用户de选择记入cookie(记入数据库也一样,不过这样系统开销会大一些),用户访问网站上de任何一个页面时,再从cookie(或数据库)中读出之前用户de选择,载入相应deCSS文件(这里还是以方法一中讲到dea.css和b.css为例).

创建一个名为switcher.phpde文件,内容如下:

<php$style =
$_GET["style"];setcookie(’’style”,$style,time() 31536000,”/”,”.site.com”,”0”)
;header(”location:”.$_SERVER[''HTTP_REFERER'']);?>
 

这段脚本先读取query数据,然后把参数stylede值记入cookie,最后返回上一页.接下来我就可以创建两个用于切换样式de链接了,并且放在合适de页面上,比如首页或用户管理后台(注意把其中desite.com换成您de域名):

<a href=”switcher.php?style=a”>主题A</a>
<a href=”switcher.php?style=b”>主题B</a>
  

点击任意一个链接,相应de就会把“a”或“b”记入cookie,然后就需要一段脚本来读取这个cookie值并且输出XHTML来引入对应deCSS:

<php if(isset($_COOKIE["style"])){$style= $_COOKIE["style"];}else{$style= “a”;//默认采用主题A?}?>
<link rel=”stylesheet” type=”text/css”
title=”当前选择de主题” href=”<?php echo $style ?>.css” />

每一个需要切换样式de页面都要加上这段相关代码,所以直接把它加入网站de头文件中就行了.当然您可以根据自己de需要修改这个脚本,但万变不离其宗,总de思路应该是不变de.

<script type=”text/javascript”>
function setStyle(title) {

//预定义变量
var i, links;

//用DOM方法获得所有delink元素
links?= document.getElementsByTagName(”link”);
for(i=0; links[i]; i ) {

//判断此link元素derel属性中是否有style关键字
//即此link元素是否为样式表link
//同时判断此link元素是否含有title属性
if(links[i].getAttribute(”rel”).indexOf(”style”) != -1
&& links[i].getAttribute(”title”)) {

//先不管三七二十一把它设为disabled
links[i].disabled = true;

//再判断它detitle中是否有我指定de关键字
if(links[i].getAttribute(”title”).indexOf(title) != -1)

//如果有则将其激活
links[i].disabled = false;
}
}
}
</script>

<link rel=”stylesheet” type=”text/css”
title=”主题A” href=”a.css” />

<link rel=”alternate stylesheet” type=”text/css”
title=”主题B” href=”b.css” />?

2个页面间不通过Session与url的传值方式

星期二, 06月 3rd, 2008

下面是全部相关代码,已经编译通过.
Chuandi(传递)是名字空间

WebForm1:
<%@ Page language=”c#” Codebehind=”WebForm1.aspx.cs” Inherits=”chuandi.WebForm1″ %>
<HTML>
<HEAD>
<title>WebForm1</title>
</HEAD>
<body>
<form id=”Form1″ method=”post” runat=”server”>
<asp:TextBox id=”TextBox1″ runat=”server”></asp:TextBox>
<asp:Button id=”Button1″ runat=”server” Text=”传”></asp:Button>
</form>
</body>
</HTML>
using System;
namespace chuandi
{
public class WebForm1 : System.Web.UI.Page
{
protected System.Web.UI.WebControls.TextBox TextBox1;
protected System.Web.UI.WebControls.Button Button1;
public string Text1
{
get
{
return this.TextBox1.Text;
}
}
private void Page_Load(object sender, System.EventArgs e)
{}
override protected void OnInit(EventArgs e)
{
InitializeComponent();
base.OnInit(e);
}
private void InitializeComponent()
{
this.Button1.Click = new System.EventHandler(this.Button1_Click);
this.Load = new System.EventHandler(this.Page_Load);
}
private void Button1_Click(object sender, System.EventArgs e)
{
Server.Transfer(”WebForm2.aspx”);
}
}
}


WebForm2:
<%@ Page language=”c#” Codebehind=”WebForm2.aspx.cs” Inherits=”chuandi.WebForm2″ %>
<%@ Reference Page=”WebForm1.aspx” %>
<HTML>
<HEAD>
<title>WebForm2</title>
</HEAD>
<body>
<form id=”Form1″ method=”post” runat=”server”>
<asp:Label id=”Label1″ runat=”server”>Label</asp:Label>
<asp:Button id=”Button1″ runat=”server” Text=”返回”></asp:Button>
</form>
</body>
</HTML>
using System;
namespace chuandi
{
public class WebForm2 : System.Web.UI.Page
{
protected System.Web.UI.WebControls.Button Button1;
protected System.Web.UI.WebControls.Label Label1;
public chuandi.WebForm1 wf1;
private void Page_Load(object sender, System.EventArgs e)
{
if(!IsPostBack)
{
wf1=(chuandi.WebForm1)Context.Handler;
Label1.Text=”上页传来de是:” wf1.Text1;
}
}
override protected void OnInit(EventArgs e)
{
InitializeComponent();
base.OnInit(e);
}
private void InitializeComponent()
{
this.Button1.Click = new System.EventHandler(this.Button1_Click);
this.Load = new System.EventHandler(this.Page_Load);
}
private void Button1_Click(object sender, System.EventArgs e)
{
Server.Transfer(”WebForm1.aspx”);
}
}

Java源码分析:深入探讨Iterator模式

星期一, 06月 2nd, 2008

java.util包中包含了一系列重要de集合类.本文将从分析源码入手,深入研究一个集合类de内部结构,以及遍历集合de迭代模式de源码实现内幕.
  下面我先简单讨论一个根接口Collection,然后分析一个抽象类AbstractList和它de对应Iterator接口,并仔细研究迭代子模式de实现原理.
  本文讨论de源相关代码版本是JDK 1.4.2,因为JDK 1.5在java.util中使用了很多泛型相关代码,为了简化问题,所以我还是讨论1.4版本de相关代码.
  集合类de根接口Collection
  Collection接口是所有集合类de根类型.它de一个主要de接口方法是:
  boolean add(Object c)
  add()方法将添加一个新元素.注意这个方法会返回一个boolean,但是返回值不是表示添加成功与否.仔细阅读doc可以看到,Collection规定:如果一个集合拒绝添加这个元素,无论任何原因,都必须抛出异常.这个返回值表示de意义是add()方法执行后,集合de内容是否改变了(就是元素有无数量,位置等变化),这是由具体类实现de.即:如果方法出错,总会抛出异常;返回值仅仅表示该方法执行后这个Collectionde内容有无变化.
  类似de还有:
  boolean addAll(Collection c);
  boolean remove(Object o);
  boolean removeAll(Collection c);
  boolean remainAll(Collection c);
  Object[] toArray()方法很简单,把集合转换成数组返回.Object[] toArray(Object[] a)方法就有点复杂了,首先,返回deObject[]仍然是把集合de所有元素变成de数组,但是类型和参数ade类型是相同de,比如执行:
  String[] o = (String[])c.toArray(new String[0]);
  得到deo实际类型是String[].
  其次,如果参数ade大小装不下集合de所有元素,返回de将是一个新de数组.如果参数ade大小能装下集合de所有元素,则返回de还是a,但ade内容用集合de元素来填充.尤其要注意de是,如果ade大小比集合元素de个数还多,a后面de部分全部被置为null.
  最后一个最重要de方法是iterator(),返回一个Iterator(迭代子),用于遍历集合de所有元素.
  用Iterator模式实现遍历集合
  
  Iterator模式是用于遍历集合类de标准访问方法.它可以把访问逻辑从不同类型de集合类中抽象出来,从而避免向客户端暴露集合de内部结构.
  例如,如果没有使用Iterator,遍历一个数组de方法是使用索引:
  for(int i=0; i
  而访问一个链表(LinkedList)又必须使用while循环:
  while((e=e.next())!=null) { … e.data() … }
  以上两种方法客户端都必须事先知道集合de内部结构,访问相关代码和集合本身是紧耦合,无法将访问逻辑从集合类和客户端相关代码中分离出来,每一种集合对应一种遍历方法,客户端相关代码无法复用.
  更恐怖de是,如果以后需要把ArrayList更换为LinkedList,则原来de客户端相关代码必须全部重写.
  为解决以上问题,Iterator模式总是用同一种逻辑来遍历集合:
  for(Iterator it = c.iterater(); it.hasNext(); ) { … }
  奥秘在于客户端自身不维护遍历集合de”指针”,所有de内部状态(如当前元素位置,是否有下一个元素)都由Iterator来维护,而这个Iterator由集合类通过工厂方法生成,因此,它知道如何遍历整个集合.
  客户端从不直接和集合类打交道,它总是控制Iterator,向它发送”向前”,”向后”,”取当前元素”de命令,就可以间接遍历整个集合.
  首先看看java.util.Iterator接口de定义:
  public interface Iterator {
  boolean hasNext();
  Object next();
  void remove();
  }
  依赖前两个方法就能完成遍历,典型de相关代码如下:
  for(Iterator it = c.iterator(); it.hasNext(); ) {
  Object o = it.next();
  // 对ode操作…
  }
  在JDK1.5中,还对上面de相关代码在语法上作了简化:
  // Type是具体de类型,如String.
  for(Type t : c) {
  // 对tde操作…
  }

  每一种集合类返回deIterator具体类型可能不同,Array可能返回ArrayIterator,Set可能返回SetIterator,Tree可能返回TreeIterator,但是它们都实现了Iterator接口,因此,客户端不关心到底是哪种Iterator,它只需要获得这个Iterator接口即可,这就是面向对象de威力.
   Iterator源码剖析
  让我来看看AbstracyList如何创建Iterator.首先AbstractList定义了一个内部类(inner class):
  private class Itr implements Iterator {
  …
  }
  而iterator()方法de定义是:
  public Iterator iterator() {
  return new Itr();
  }
  因此客户端不知道它通过Iterator it = a.iterator();所获得deIteratorde真正类型.
  现在我关心de是这个申明为privatedeItr类是如何实现遍历AbstractListde:
  private class Itr implements Iterator {
  int cursor = 0;
  int lastRet = -1;
  int expectedModCount = modCount;
  }
  Itr类依靠3个int变量(还有一个隐含deAbstractListde引用)来实现遍历,cursor是下一次next()调用时元素de位置,第一次调用next()将返回索引为0de元素.lastRet记录上一次游标所在位置,因此它总是比cursor少1.
  变量cursor和集合de元素个数决定hasNext():
  public boolean hasNext() {
  return cursor != size();
  }
  方法next()返回de是索引为cursorde元素,然后修改cursor和lastRetde值:
  public Object next() {
  checkForComodification();
  try {
  Object next = get(cursor);
  lastRet = cursor ;
  return next;
  } catch(IndexOutOfBoundsException e) {
  checkForComodification();
  throw new NoSuchElementException();
  }
  }
  expectedModCount表示期待demodCount值,用来判断在遍历过程中集合是否被修改过.AbstractList包含一个modCount变量,它de初始值是0,当集合每被修改一次时(调用add,remove等方法),modCount加1.因此,modCount如果不变,表示集合内容未被修改.
  Itr初始化时用expectedModCount记录集合demodCount变量,此后在必要de地方它会检测modCountde值:
  final void checkForComodification() {
  if (modCount != expectedModCount)
  throw new ConcurrentModificationException();
  }
  如果modCount与一开始记录在expectedModeCount中de值不等,说明集合内容被修改过,此时会抛出ConcurrentModificationException.
  这个ConcurrentModificationException是RuntimeException,不要在客户端捕获它.如果发生此异常,说明程序相关代码de编写有问题,应该仔细检查相关代码而不是在catch中忽略它.
  但是调用Iterator自身deremove()方法删除当前元素是完全没有问题de,因为在这个方法中会自动同步expectedModCount和modCountde值:
  public void remove() {
  …
  AbstractList.this.remove(lastRet);
  …
  // 在调用了集合deremove()方法之后重新设置了expectedModCount:
  expectedModCount = modCount;
  …
  }
  要确保遍历过程顺利完成,必须保证遍历过程中不更改集合de内容(Iteratorderemove()方法除外),因此,确保遍历可靠de原则是只在一个线程中使用这个集合,或者在多线程中对遍历相关代码进行同步.
  最后给个完整de示例:
  Collection c = new ArrayList();
  c.add(”abc”);
  c.add(”xyz”);
  for(Iterator it = c.iterator(); it.hasNext(); ) {
  String s = (String)it.next();
  System.out.println(s);
  }
  如果您把第一行相关代码deArrayList换成LinkedList或Vector,剩下de相关代码不用改动一行就能编译,而且功能不变,这就是针对抽象编程de原则:对具体类de依赖性最小.

基于Java的代理设计模式

星期一, 06月 2nd, 2008

 一、引子
  我去科技市场为自己de机器添加点奢侈de配件,很多DIYer都喜欢去找代理商,因为在代理商那里拿到de东西不仅质量有保证,而且价格和售后服务上都会好很多.客户通过代理商得到了自己想要de东西,而且还享受到了代理商额外de服务;而生产厂商通过代理商将自己de产品推广出去,而且可以将一些销售服务de任务交给代理商来完成(当然代理商要和厂商来共同分担风险,分配利润),这样自己就可以花更多de心思在产品de设计和生产上了.
  在美国,任何企业de产品要想拿到市场上去卖就必须经过代理商这一个环节,否则就是非法de.看来代理商在商业运作中起着很关键de作用. 不小心把话题扯远了,回过头来,那么在我de面向对象de程序设计中,会不会有代理商这样de角色呢?来看这篇文章de人肯定不会说:没有!
  那么就跟着这篇文章来看看代理模式de奇妙吧.
  二、定义和分类
  代理模式在设计模式中de定义就是:为其他对象提供一种代理以控制对这个对象de访问.说白了就是,在一些情况下客户不想或者不能直接引用一个对象,而代理对象可以在客户和目标对象之间起到中介作用,去掉客户不能看到de内容和服务或者增添客户需要de额外服务.
  那么什么时候要使用代理模式呢?在对已有de方法进行使用de时候出现需要对原有方法进行改进或者修改,这时候有两种改进选择:修改原有方法来适应现在de使用方式,或者使用一个“第三者”方法来调用原有de方法并且对方法产生de结果进行一定de控制.第一种方法是明显违背了“对扩展开放、对修改关闭”(开闭原则),而且在原来方法中作修改可能使得原来类de功能变得模糊和多元化(就像现在企业多元化一样),而使用第二种方式可以将功能划分de更加清晰,有助于后面de维护.所以在一定程度上第二种方式是一个比较好de选择!
  当然,话又说回来了,如果是一个很小de系统,功能也不是很繁杂,那么使用代理模式可能就显得臃肿,不如第一种方式来de快捷.这就像一个三口之家,家务活全由家庭主妇或者一个保姆来完成是比较合理de,根本不需要雇上好几个保姆层层代理:)
  根据《Java与模式》书中对代理模式de分类,代理模式分为8种,这里将几种常见de、重要de列举如下:
  1. 远程(Remote)代理:为一个位于不同de地址空间de对象提供一个局域代表对象.比如:您可以将一个在世界某个角落一台机器通过代理假象成您局域网中de一部分.
  2. 虚拟(Virtual)代理:根据需要将一个资源消耗很大或者比较复杂de对象延迟de真正需要时才创建.比如:如果一个很大de图片,需要花费很长时间才能显示出来,那么当这个图片包含在文档中时,使用编辑器或浏览器打开这个文档,这个大图片可能就影响了文档de阅读,这时需要做个图片Proxy来代替真正de图片.
  3. 保护(Protect or Access)代理:控制对一个对象de访问权限.比如:在论坛中,不同de身份登陆,拥有de权限是不同de,使用代理模式可以控制权限(当然,使用别de方式也可以实现).
  4. 智能引用(Smart Reference)代理:提供比对目标对象额外de服务.比如:纪录访问de流量(这是个再简单不过de例子),提供一些友情提示等等.
  代理模式是一种比较有用de模式,从几个类de“小结构”到庞大系统de“大结构”都可以看到它de影子.
  三、结构

  代理模式中de“代理商”要想实现代理任务,就必须和被代理de“厂商”使用共同de接口(您可以想象为产品).所以自然而然您会想到在java中使用一个抽象类或者接口(推荐)来实现这个共同de接口.于是代理模式就有三个角色组成了:
  1.抽象主题角色:声明了真实主题和代理主题de共同接口.
  2.代理主题角色:内部包含对真实主题de引用,并且提供和真实主题角色相同de接口.
  3.真实主题角色:定义真实de对象.
  使用类图来表示下三者间de关系如下:


  当然,图上所示de是代理模式中de一个具体情况.而代理模式可以非常灵活de使用其他方式来实现,这样就与图上所示有很大de区别.
  也许,现在您已经对代理模式已经有了一个宏观de认识了,下面我来看看怎么实际de使用代理模式.
  四、举例
  以论坛中已注册用户和游客de权限不同来作为第一个例子:已注册de用户拥有发帖,修改自己de注册信息,修改自己de帖子等功能;而游客只能看到别人发de帖子,没有其他权限.为了简化相关代码,更好de显示出代理模式de骨架,我这里只实现发帖权限de控制. 首先我先实现一个抽象主题角色MyForum,里面定义了真实主题和代理主题de共同接口——发帖功能.
  相关代码如下:
  public interface MyForum
  {
  public void AddFile();
  }
  这样,真实主题角色和代理主题角色都要实现这个接口.其中真实de主题角色基本就是将这个接口de方法内容填充进来.所以在这里就不再赘述它de实现.我把主要de精力放到关键de代理主题角色上.代理主题角色相关代码大体如下:
  public class MyForumProxy implements MyForum
  {
  private RealMyForum forum ;
  private int permission ; //权限值
  public MyForumProxy(int permission)
  {
  forum = new RealMyForum()
  this.permission = permission ;
  }
  //实现de接口
  public void AddFile()
  {
  //满足权限设置de时候才能够执行操作
  //Constants是一个常量类
  if(Constants.ASSOCIATOR == permission)
  {
   forum.AddFile();
  }
  else
   System.out.println(”You are not a associator of MyForum ,please registe!”);
  }
  }
  这样就实现了代理模式de功能.当然您也可以在这个代理类上添加自己de方法来实现额外de服务,比如统计帖子de浏览次数,记录用户de登录情况等等.
  还有一个很常见de代理模式de使用例子就是对大幅图片浏览de控制.在我常见de网站上面浏览图文de信息时,不知道您有没有注意到,图片位置放置de是经过缩小de,当有人要仔细de查看这个图片时,可以通过点击图片来激活一个链接,在一个新de网页打开要看de图片 .这样对于提高浏览速度是很有好处de,因为不是每个人都要去看仔细图上de信息.这种情况就可以使用代理模式来全面实现.这里我将思路表述出来,至于实现由于工作原因,就不表述了,至于这种方式在B/S模式下de真实可行性,我没有确认过,只是凭空de想象.如果不是可行de方式,那这个例子可以放到一个C/S下来实现,这个是绝对没有问题de,而且在很多介绍设计模式de书和文章中使用.两种方式de实现有兴趣de可以来尝试一下.
  我在浏览器中访问网页时是调用de不是真实de装载图片de方法,而是在代理对象中de方法,在这个对象中,先使用一个线程向浏览器装载了一个缩小版de图片,而在后台使用另一个线程来调用真实de装载大图片de方法将图片加载到本地,当您要浏览这个图片de时候,将其在新de网页中显示出来.当然如果在您想浏览de时候图片尚未加载成功,可以再启动一个线程来显示提示信息,直到加载成功.
  这样代理模式de功能就在上面体现de淋漓尽致——通过代理来将真实图片de加载放到后台来操作,使其不影响前台de浏览.
  五、总结
  代理模式能够协调调用者和被调用者,能够在一定程度上降低系统de耦合度.不过一定要记住前面讲de使用代理模式de条件,不然de话使用了代理模式不但不会有好de效果,说不定还会出问题de

Jsp结合XML XSLT将输出转换为Html格式

星期一, 06月 2nd, 2008

 我知道 XML XSLT就可以直接输出到支持XMLde浏览器上,如IE 5.0以上,但是,我还要考虑到有不少浏览器不直接支持XML,在这种情况下,我需要在服务器上进行转换成html输出到浏览器,这种临时过渡办法恐怕要在一段时间内一直要使用.   使用Jsp 加上tablib标识库,我可以完成这种转换.

  著名open source项目组jakarta.apache.org推出de系列标识库中,就有这个功能detanglib:http://jakarta.apache.org/taglibs/doc/xsl-doc/intro.html

  按照jakarta配置方法,有点繁琐,需要修改或定义Web.xml,本人经过摸索,使用下列相当简单de办法,就可以使Jsp能成功运行XSL这个标识库了.

  xsl标识库有三个关键包:
   xerces.jar 可以在http://xml.apache.org/中得到
   xalan.jar 可以在http://xml.apache.org/中得到
   xsl.jar 从http://jakarta.apache.org/taglibs/doc/xsl-doc/intro.html得到

  1.将这三个包放置到Tomcatdecommon/lib目录下,或者直接放入Classpath环境中.

  2.在JSP中调用标识库:

  原来Jakarta推荐方法是:


<%@taglib uri=”http://jakarta.apache.org/taglibs/xsl-1.0″ prefix=”xsl” %>

  这就需要在/WEB-INF/web.xml下定义一下http://jakarta.apache.org/taglibs/xsl-1.0指向.如:


<taglib>
<taglib-uri>http://jakarta.apache.org/taglibs/xsl-1.0</taglib-uri>
<taglib-location>/WEB-INF/xsl.tld</taglib-location>
</taglib>

  这种做法虽然很标准,但是,如果您de容器一直使用tomcat,就完全不必了.

  我de做法是:


<%@taglib uri=”xsl.jar” prefix=”xsl” %>

  我以JakartadeXSL taglib附带deApply.jsp为例,正好了解一下Jsp XML XSLT三者之间de关系:

  Apply.jsp


<%@taglib uri=”xsl.jar” prefix=”xsl” %>
<html>
<head>
<title>Employee List</title>
</head>
<body bgcolor=”white”>

<p>下面展示了Jspde四种组合XML XSLTde方法:
<p>下面使用apply方法,将已经存在deemployees.xml和employeeList.xsl结合在一起

<xsl:apply xml=”/xml/employees.xml” xsl=”/xml/employeeList.xsl”/>
<hr>


<p>下面是使用已经存在employeeList.xsl 然后在Jsp中自己直接写入XML数据.


<xsl:apply xsl=”/xml/employeeList.xsl”>
<?xml version=”1.0″ encoding=”ISO-8859-1″?>
<employees>
<employee id=”123″>
<first-name>John</first-name>
<last-name>Doe</last-name>
<telephone>800-555-1212</telephone>
</employee>
<employee id=”456″>
<first-name>Jane</first-name>
<last-name>Smith</last-name>
<telephone>888-555-1212</telephone>
</employee>
<employee id=”789″>
<first-name>George</first-name>
<last-name>Taylor</last-name>
<telephone>555-555-1212</telephone>
</employee>
</employees>
</xsl:apply>
<hr>

<p>下面使使用include调用de办法,这样一个XSLT样式可以适应不同deXML文件.

<xsl:apply xsl=”/xml/employeeList.xsl”>
<xsl:include page=”/xml/employees.xml”/>
</xsl:apply>
<hr>

<p>下面是使用import方法,在page-scope(类似scope=”page”)中导入XML文件</p>

<xsl:import id=”data” page=”/xml/employees.xml”/>
<xsl:apply nameXml=”data” xsl=”/xml/employeeList.xsl”/>

</body>

  在上面程序中,展示了四种Jsp组合XML XSLTde方法,基本可以满足我de需要.注意上面deXML文件路径是”/xml/”,这是相对Tomcat容器de绝对路径.

  我简单看一下employeeList.xsl和employees.xml内容:

  employeeList.xsl类似html中deCSS,主要是对XML中数据显示方式进行定义:

<?xml version=”1.0″?>
<xsl:stylesheet version=”1.0″ xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”>
<xsl:template match=”employees”>
<table border=”1″ width=”100%”>
<tr>
<th>ID</th>
<th>Employee Name</th>
<th>Phone Number</th>
</tr>
<xsl:for-each select=”employee”>
<tr>
<td>
<xsl:value-of select=”@id”/>
</td>
<td>
<xsl:value-of select=”last-name”/>,
<xsl:value-of select=”first-name”/>
</td>
<td>
<xsl:value-of select=”telephone”/>
</td>
</tr>
</xsl:for-each>
</table>
</xsl:template>

</xsl:stylesheet>

employees.xml

<?xml version=”1.0″ encoding=”ISO-8859-1″?>


<employees>
 <employee id=”123″>
  <first-name>John</first-name>
  <last-name>Doe</last-name>
  <telephone>800-555-1212</telephone>
 </employee>

 <employee id=”456″>
  <first-name>Jane</first-name>
  <last-name>Smith</last-name>
  <telephone>888-555-1212</telephone>
 </employee>

  <employee id=”789″>
  <first-name>George</first-name>
  <last-name>Taylor</last-name>
  <telephone>555-555-1212</telephone>
 </employee>
</employees>

  如果我在employees.xml顶部加入:


<?xml:stylesheet type=”text/xsl” href=”catalog.xsl”?>

  用支持XMLdeIE 5.0以上浏览器调用,其显示页面就和Apply.jsp显示页面是一样de.


PHP学习之PHP表达式

星期一, 06月 2nd, 2008

PHP表达式
  表达式是PHP最重要de组成元素.在PHP 3.0中,几乎您所写de任何东西都是表达式.表达式de最简单但精确de定义是"有一个值de任何东西".    一个简单de例子是常量和变量.
当您写 "$a = 5"时, 您给 $a 赋了值 ‘5′ . (在这种情况下, ‘5′ 是一个整形常量). 在此,您是希望把 $a 赋值成 5.所以写 $b = $a 时,您希望de结果就是 $b = 5 .即 $a 是一个值为 5 de表达式.   复杂表达式de简单例子是函数.
比如说,考虑以下函数:    function foo()     {      return 5;     }   
如果您认为写$c = foo()实际上和写 $c = 5是一样de,那么您是对de.函数是值为其返回值de表达式.因为foo() 返回5, 所以表达式 ‘foo()’de值是 5 .   
PHPde值当然不限于整形,且通常都不是.PHP支持三类值de类型: 整形值,浮点值和字符串值.PHP 支持两种混合类型(non-scalar): 数组和对象.这两种类型de值可以赋给变量或从函数中返回.   
PHP 3是一种面向表达式de语言, 所以几乎所有东西都是表达式.
考虑我已经讨论de例子, ‘$a = 5′.很容易看出这里有两个值,整形常量 ‘5′de值,和也被赋为 5 de变量 $a de值.但是这里实际上还有一个附加de值,就是赋值语句本身de值.
赋值语句本身de值就是被赋给de值,本例中是 5. 事实上,它意味着不考虑 ‘$a = 5′要做什么,它是一个值为 5 de表达式.这样,写诸如 ‘$b = ($a = 5)’de语句,就象 ‘$a = 5; $b = 5;’ (每条语句末尾有一个分号).因为赋值de顺序是从右向左de您也可以写成 ‘$b = $a = 5′.   
   表达式计算方向de另一个好例子是先加、后加及先减、后减.PHP/FI和多数其他语言de用户可能很熟悉 variable 和 variable–.这是自加和自减操作.在 PHP/FI 2 中,语句 ‘$a ‘ 没有值(它不是表达式), 这样您就既不能赋值给它也不能通过任何办法使用它.PHP 3 把它们变成了和 C 中一样de表达式从而增强了自加和自减运算de能力.
和 C 中类似,PHP 3中也有两种类型de自加—-先加和后加.先加和后加de本质都是变量自加,对于变量本身de作用是一样de.不同点是自加表达式de值.形如 ‘ $variable’de先加, 计算变量自加后de值(PHP先做变量自加,再读取它de值,也就叫做 ‘先加’).形如’$variable ‘de后加, 则先计算原始变量$variablede值,然后才进行自加 (PHP在读取变量de值后再做自加,因此叫做’后加’).   
最常见de表达式是比较表达式.这种表达式计算结果是 0 或 1,分别意味着 FALSE 或是 TRUE.
PHP 支持 > (大于), >= (大于等于), == (等于), < (小于) 及 <= (小于等于).这种表达式通常用在条件执行里 , 例如 IF 语句.   
  在这里我最后要讨论de表达式是混合赋值表达式.您已经知道如果要使 $a 加一,您可以简单de写一句 ‘$a ‘ 或者 ‘ $a’.但是如果要增加de值比1大怎么办呢,例如使它加 3 ? 您可以多写几次’$a ‘ ,但这 显然不是一种高效或令人乐于接受de办法.另外一个通常de办法是写 ‘$a = $a 3′.先计算’$a 3′ de值,在赋回到 $a, 这样 $a 就加上 3 了.在PHP 3 中,您可以象在其他几种语言(例如 C)一样把它简写,这样更清晰、快洁易懂. 把当前变量 $a 加 3 可以写成 ‘$a = 3′.这句话de意思是 "取出 $a de值, 把它加 3 , 在赋给 $a".这除了使得语句简短、清晰,还使它执行de更快.表达式 ‘$a = 3′de值,和一个严格de赋值语句一样,是所赋de值.注意:不是 3, 而是 $a 加 3 de值(这才是赋给$a de).任何de双操作符 可以被用于这种赋值操作模式,例如 ‘$a -= 5′ (变量 $a 减去 5 ), ‘$b *= 7′ ( 变量 $b 乘以 7),等等.   
   最后值得提到de是表达式de真值.很多时候(主要是在条件执行和循环时),您并不关心表达式de特定值 , 而只是注意它代表TRUE 还是 FALSE (PHP 没有专用de布尔类型).PHP中使用类似perlde方法计算表达式de真值.任何非零de值为 TRUE,零为 FALSE .请一定注意负零de值是非零值,并被视为 TRUE ! 空字符串可 字符串 "0" 为 FALSE; 所其他de字符串为 TRUE .对于非数量值(数组和对象) - 如果它de值不含任何元素为 FALSE, 否则为 TRUE.

Session的工作方式

星期一, 06月 2nd, 2008

下面我来看一下Session是如何工作de.不知您是否知道通过Cookie来实现身份认证de吧.首先生成一个独一无二deCookie作为用户身份de标志,并在数据库中进行注册.然后通过用户传递来deCookie和数据库中注册deCookie进行对照以确定用户de身份.
Sessionde工作原理也是这样.
首先,PHP为建立Sessionde用户产生一个独一无二de字符串,用来标志这个用户desession.一般将这个字符串称作Session Id.然后“sess”+Session Id为文件名(例如一个Session ID为111,那么文件名为sess_111)在服务器de文件系统中建立一个文件,在文件中保存用户在Session所定义de全局变量de变量名和值.然后再将Session Id作为一个名为PHPSessiondeCookie保存在用户端de文件系统中.
然后,当用户再次连接服务器访问一个PHP脚本时,PHP从用户发来dePEESession这个Cookie中得到用户所在SessiondeSession Id,并根据Session Id从服务器de文件系统中保存Session信息de文件.最后从这个文件中读出用户在上次连接时所设置de全局变量de值.
因此,我可以看到Sessionde工作原理和我上一节所介绍de身份认证de工作原理是一样de.所不同de只是Session将信息保存在了服务器de文件系统中,而我将信息保存在了数据库中.当然使用Session好处就是数据de保存和获取是由PHP自动完成de,而直接使用Cookiede话就需要自己动手进行数据de保存和获取.
Session利用Cookiede身份标志功能,将用户在浏览网站时需要保存de信息保存在服务器上.这样Session既克服了HTTP协议de缺陷,又防止了信息de泄漏,而且方便了编程者de使用,是一个非常好de解决方案.不过,Sessionde功能只有PHP4支持,PHP3是不支持Sessionde.因此使用PHP3进行网站构建de读者只能采用直接使用Cookiede方式.

将数字格式的计算结果转为汉字格式

星期一, 06月 2nd, 2008

有没有想过将数字格式de计算结果转化为汉字格式de? 有人会问"干嘛要转, 数字形式不是蛮好嘛", 可是当这个数字很长de时候就不太容易读出来了吧, 就算是有千分位de分隔符也不易顺口说出, 因为这个符号是位英语行方便de, 不是适合我de读法. 那就自己写一个函数来完成这项任务吧.
将下列相关代码加到您de网页里, 通过num2chi()函数de调用就可以实现上述功能了, 快来试一试吧. 为了各位能读懂这段相关代码, 在下特意加入详细注解, 还请老鸟们不要嫌罗唆, ^_^.
//———————-FUNCTION BEGIN—————————–
//——————————————-
//函数名: num2chi()
//叁 数: 一数字
//返回值: 一字符串
//功 能: 将难读de长串数字转为顺口读出de汉字
//作 者: chen.anson
//站 点: HTTP://dreamer.oso.com.cn
//——————————————-
function num2chi(result) {
var chiresult = ""; //定义返回值叁数chiresult为字符形式
result = result.toString(); //将result转为字符形式
result = result.toLowerCase();
resultlen = result.length; //定义resultlen为resultde长度
tempresult = result; //定义中间变量tempresult
for (i=1;i<=resultlen;i ) //将字符串tempresult中de全部数字替换为汉字
{
tempresult = tempresult.replace("1","一");
tempresult = tempresult.replace("2","二");
tempresult = tempresult.replace("3","三");
tempresult = tempresult.replace("4","四");
tempresult = tempresult.replace("5","五");
tempresult = tempresult.replace("6","六");
tempresult = tempresult.replace("7","七");
tempresult = tempresult.replace("8","八");
tempresult = tempresult.replace("9","九");
tempresult = tempresult.replace("0","零");
tempresult = tempresult.replace(".","点");
tempresult = tempresult.replace("e ","幂");
}
while(tempresult.indexOf("零零")!=-1) //避免字符串tempresult中出现"零零", 但又不能改变字符串长度
{
tempresult = tempresult.replace("零零","位零");
}
resultlen = tempresult.length; //再次确认tempresultde长度, 因"e "->"幂"会引起长度变化
for (i=1,j=1,k=1;i<=resultlen;i ) //开始转换, i为位数确认叁数, j为"十百千"确认叁数, k为"万亿"确认叁数
{
//防止尾数为零, 如八拾零, 二拾零万
if (tempresult.charAt(resultlen-1)=="零"&&i==1)
chiresult = "位";
else if (tempresult.charAt(resultlen-i)=="零"&&j==1)
chiresult = "位" chiresult;
//——————————–
//避免把"幂"和"点"当做实际位数, 而且单位确认变量重新计数
else if (tempresult.charAt(resultlen-i)=="幂")
{
j=1;k=1;chiresult = tempresult.charAt(resultlen-i) chiresult;continue;
}
else if (tempresult.charAt(resultlen-i)=="点")
{
j=1;k=1;chiresult = tempresult.charAt(resultlen-i) chiresult;continue;
}
//————————————–
else
chiresult = tempresult.charAt(resultlen-i) chiresult;
//添加数字单位
if (tempresult.charAt(resultlen-i-1)!="位"&&tempresult.charAt(resultlen-i-1)!="零"&&tempresult.charAt(resultlen-i-1)!="幂")
{
if (j==1&&i<resultlen) chiresult = "拾" chiresult;
else if (j==2&&i<resultlen) chiresult = "百" chiresult;
else if (j==3&&i<resultlen) chiresult = "千" chiresult;
}
if (j==4&&i<resultlen) j=0;
if (k==4&&i<resultlen&&tempresult.charAt(resultlen-i-1)!="幂") chiresult = "万" chiresult;
else if (k==8&&i<resultlen&&tempresult.charAt(resultlen-i-1)!="幂") {k=0;chiresult = "亿" chiresult;}
//———–
j ;k ;
}
while(chiresult.indexOf("位")!=-1) //避免字符串chiresult中出现"位"
{
chiresult = chiresult.replace("位","");
}
if (chiresult.substr(0,2)=="一拾") //避免出现"一拾二"等情况
chiresult = chiresult.substring(1,chiresult.length);
//幂和小数点後de数字应直接读出, 而没有单位
if (chiresult.search("幂")>=0&&chiresult.search("点")>=0)
{
rebegin = chiresult.substring(0,chiresult.indexOf("点"));
relast = chiresult.substring(chiresult.indexOf("幂"),chiresult.length);
remid = chiresult.substring(chiresult.indexOf("点"),chiresult.indexOf("幂"));
for (i=1;i<=remid.length;i )
{
remid = remid.replace("拾","");
remid = remid.replace("百","");
remid = remid.replace("千","");
remid = remid.replace("万","");
remid = remid.replace("亿","");
}
chiresult = rebegin remid relast;
}
else if (chiresult.search("幂")<0&&chiresult.search("点")>=0)
{
rebegin = chiresult.substring(0,chiresult.indexOf("点"));
relast = chiresult.substring(chiresult.indexOf("点"),chiresult.length);
for (i=1;i<=relast.length;i )
{
relast = relast.replace("拾","");
relast = relast.replace("百","");
relast = relast.replace("千","");
relast = relast.replace("万","");
relast = relast.replace("亿","");
}
chiresult = rebegin relast;
}
if (chiresult.search("幂")>=0) //将"幂"替换为"乘以拾de", 这样可以直接读出
{
chiresult = chiresult.replace("幂","乘以拾de");
chiresult = chiresult "次方";
}
return chiresult;
}
//———————-FUNCTION END——————————-

将下面这两条语句放到script块中试运行一下看看结果对不对, 另外可以访问我de主页http://dreamer.oso.com.cn 在休闲广场里有一个彩票页面, 就是用这段相关代码实现de, 欢迎光临.
hi=’4648000567542450084.16415846E 766600050′;
document.write(hi "<br>" num2chi(hi));