<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>流水孟春</title>
	<atom:link href="http://yulans.cn/feed" rel="self" type="application/rss+xml" />
	<link>http://yulans.cn</link>
	<description>Web技术,LAMP,Nginx,Web2.0,前端技术</description>
	<lastBuildDate>Fri, 04 May 2012 22:43:40 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1</generator>
		<item>
		<title>垃圾智能手机不宜经常连接到笔记本电脑</title>
		<link>http://yulans.cn/crab/%e5%9e%83%e5%9c%be%e6%99%ba%e8%83%bd%e6%89%8b%e6%9c%ba%e4%b8%8d%e5%ae%9c%e7%bb%8f%e5%b8%b8%e8%bf%9e%e6%8e%a5%e5%88%b0%e7%ac%94%e8%ae%b0%e6%9c%ac%e7%94%b5%e8%84%91</link>
		<comments>http://yulans.cn/crab/%e5%9e%83%e5%9c%be%e6%99%ba%e8%83%bd%e6%89%8b%e6%9c%ba%e4%b8%8d%e5%ae%9c%e7%bb%8f%e5%b8%b8%e8%bf%9e%e6%8e%a5%e5%88%b0%e7%ac%94%e8%ae%b0%e6%9c%ac%e7%94%b5%e8%84%91#comments</comments>
		<pubDate>Fri, 04 May 2012 22:43:40 +0000</pubDate>
		<dc:creator>cmpan</dc:creator>
				<category><![CDATA[喷口水]]></category>

		<guid isPermaLink="false">http://yulans.cn/?p=961</guid>
		<description><![CDATA[低端智能手机容易把笔记本电脑显示屏烧坏。 之前笔记本显示器坏了一次，开始屏幕在抖动，到后来待机后在动鼠标，显示屏就不亮了，只能重启电脑。到后来屏幕都不亮了。 今天手机用USB接上笔记本后，发现屏幕有闪动状况，顿悟肯定不是手机辐射影响屏幕，赶紧拔下来，换充电器充电。 还不如多加几百，弄个中高端的平板手机，否则常用电脑来玩手机的话，到时候修电脑的钱还要多。]]></description>
			<content:encoded><![CDATA[<p>低端智能手机容易把笔记本电脑显示屏烧坏。<br />
之前笔记本显示器坏了一次，开始屏幕在抖动，到后来待机后在动鼠标，显示屏就不亮了，只能重启电脑。到后来屏幕都不亮了。<br />
今天手机用USB接上笔记本后，发现屏幕有闪动状况，顿悟肯定不是手机辐射影响屏幕，赶紧拔下来，换充电器充电。</p>
<p>还不如多加几百，弄个中高端的平板手机，否则常用电脑来玩手机的话，到时候修电脑的钱还要多。</p>
]]></content:encoded>
			<wfw:commentRss>http://yulans.cn/crab/%e5%9e%83%e5%9c%be%e6%99%ba%e8%83%bd%e6%89%8b%e6%9c%ba%e4%b8%8d%e5%ae%9c%e7%bb%8f%e5%b8%b8%e8%bf%9e%e6%8e%a5%e5%88%b0%e7%ac%94%e8%ae%b0%e6%9c%ac%e7%94%b5%e8%84%91/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>如何写一份交互说明文档</title>
		<link>http://yulans.cn/%e5%bc%80%e5%8f%91%e6%96%b9%e6%b3%95/%e5%a6%82%e4%bd%95%e5%86%99%e4%b8%80%e4%bb%bd%e4%ba%a4%e4%ba%92%e8%af%b4%e6%98%8e%e6%96%87%e6%a1%a3</link>
		<comments>http://yulans.cn/%e5%bc%80%e5%8f%91%e6%96%b9%e6%b3%95/%e5%a6%82%e4%bd%95%e5%86%99%e4%b8%80%e4%bb%bd%e4%ba%a4%e4%ba%92%e8%af%b4%e6%98%8e%e6%96%87%e6%a1%a3#comments</comments>
		<pubDate>Thu, 15 Mar 2012 15:45:09 +0000</pubDate>
		<dc:creator>cmpan</dc:creator>
				<category><![CDATA[开发方法]]></category>

		<guid isPermaLink="false">http://yulans.cn/?p=943</guid>
		<description><![CDATA[转载请注明来处，关注我，请点击：http://weibo.com/heidixie 离开交互圈已经有段时间了。但由于博客还在，还是能够偶尔收到一些邮件，上周有位同学问我：我在求职，我看到很多招聘说明上需要交互设计师编写界面交互设计文档，请问界面交互设计文档是什么文档？怎么编写呢 这让我想起来2009年自己在项目里也大力推行过交互说明文档（在下文中，简称为DRD），格式倒没什么限制，交互设计师自己写到界面上也行，单独文档成文也行，总之就是让交互设计师能够将界面承载不了的信息通过文档沉淀下来，降低项目里的沟通成本和风险。今天整理电脑，翻出以前的PPT，分享之。 这将涉及到几个问题： 一. 什么是交互说明文档（DRD）？ 所谓DRD即是用来承载交互说明，并交付给前端、测试以及开发工程师参考的文档。 在项目中，交互设计师的主要产出物可能依次是：site map，page flow，wireframes。有的大型项目前期，交互设计师有可能还会产出用户需求分析文档（与PD产出的市场需求文档不一样的是，URD更多侧重于对目标用户的需求分析）。 DRD则很少有人专门撰写。如果需要对交互设计进行说明，聪明的交互设计师往往会直接标注在线框图里，或者在项目中不断和前端工程师和开发工程师口口相传，反复验收，不断迭代修改来确保所有的交互设计意图最终得以呈现。 二. 为什么要写？ DRD 非项目必需环节，一般情况下也不会为交互设计师专门留出相应的时间预估。没有这份文档，项目也会继续，但是可能项目会为此承担不必要的沟通成本和时间成 本。严重的话，项目的质量也会受到影响。所以写与不写，交互设计师需要做把握，时间被统一包含在“线框图”环节内——如果你要写，请在评估时预留1-2天 的时间。 那么，结合我过去的经历，谈一下此文档的必要性。 下图是一个产品开发项目基本的流程。 敏 捷开发意味着很多不同角色的流程需要并行操作。如果等到产品经理的FRD已经全部敲定，交互设计师再开始去画线框图，固然会减少沟通成本和返工风险，但是 同时意味着交互设计师的很多想法不被采纳。如果产品经理再强一些，他甚至会在FRD里连原始的DEMO也一并绘制出来了，功能性的需求和界面交互的需求有 时无法区分太清楚——比如他会在FRD里直接要求每页条目40条，超过40条即分页。而交互设计师可能会认为像蘑菇街那样不断装载出足够长的页面会更亲 和……所以，我们希望是和产品经理同时开始工作，在术业有专攻的时候相互补充。 同样，开发工程师也 希望及早介入需求，在FRD并未确认的时候就了解需求，进而将商业需求和功能需求转化为开发工程师看得明白的开发需求清单（这个清单，大部分叫做UC，即 USE CASE）,当这份清单由工程师需求分析师——在过去，这个角色被叫简称为RA，但是目前已经取消此专门的职位，而是由开发工程师代表担纲此环节工作，为 了便于描述，在此文里，我仍然将做这件事情的人称为RA——交付给具体的执行工程师后，执行工程师基本上可以当作一条条的checklist开始高效工 作，而不必再思考商业逻辑和需求。同样，测试工程师也需要编写具体的文档去指导很多测试人员在开发后高效测试，这也是基于UC和FRD去撰写的。 所以，开发需求分析是个很重要的环节。那RA是如何来完成需求分析工作的呢？ 前期介入，对PD进行开发需求评估支持； 参与每次的FRD评审会； 详细审阅FRD文档并不断与PD确认。 对于做这件事情的人来说，足够详尽的FRD是非常重要的。所以一份FRD虽然是PD产出，但是很多实施细节则是由开发工程师不断沟通评估并确认下来的。而设计需求的传递，却存在很多问题。除了线框图，没有“详尽的说明性的文档”告诉他们。比如： 一方面，交互设计师对产品经理说：这块由我们来考虑，你的文档不必包含设计上的说明，这随时会调整的。 另一方面，线框图的评审有时会让RA参与，有时却没有叫他们。即使叫上了他们，他们也会发现交互设计的需求变化要比FRD变化快。另外，他们会认为UC不必写太多关于交互设计的需求。 在某个大型项目结束后，作为交互设计师，我进行了一些调研，听听这相关人员是怎么表述问题的： 开发部门的需求分析师： 每次变动都很痛苦，设计变了之后，我就要跟着改UC，改截图，有时候UED改了还忘了通知我们，导致UC有问题…… 页面交互的需求容易漏掉，因为UC里面不可能写太多交互方面的东西。 希望UED能够在提交HTML DEMO给RA时，能同时给出一份页面元素描述文档，需要介绍html demo中的文案、链接以及相关的图片尺寸或显示字符个数。现在RA在这方面花费的时间比较多，经常要和UED去确认这些内容。 产品经理： 前 期RA和PD沟通过程中，有很多交互点点不能够明确，比如“默认显示多少属性值”，“标题显示多少字符”等。在以往的需求和项目中，对待这些问题我们都是 想到一点补一点的到FRD文档或者邮件中去。既增加了沟通成本又会存在遗漏细节的风险。PD为了可控性的需求，往往会“越俎代庖”，直接在FRD注明这种 需求（对于交互设计师来讲，却又导致没有发挥余地） 走访了一些交互设计师后，他们也存在如何清晰无遗漏将交互设计需求传递下去的困惑： 交互认为很平常的设计需求，如果不表达出来，还是容易被前端和开发忽略掉。我经历的一个项目，前端从头到尾更换了三个人，每次我都要重复去讲解下设计需求，讲得口干舌燥。而且做好后，还需要去验收。 DRD做为参考手册，一定程度上避免不吻合的问题发生。 即使有问题发生，也可以作为界面验收时的Checklist。将“我对A说，我对B说，A对B说”，转变为“A和B共同参考同一份文档”，减少沟通成本及信息不对称。 全程影响用户体验（一直到测试，都需要参照设计文档）。 可是以下问题都可以通过一份DRD来解决吗？ [...]]]></description>
			<content:encoded><![CDATA[<div>
<div>
<div><img src="http://1802.img.pp.sohu.com.cn/images/blog/2012/3/11/15/26/u82672385_136c115e15cg213.png" border="0" alt="" /></div>
<div>转载请注明来处，关注我，请点击：<a href="http://weibo.com/heidixie" target="_blank">http://weibo.com/heidixie</a></div>
<div>离开交互圈已经有段时间了。但由于博客还在，还是能够偶尔收到一些邮件，上周有位同学问我：我在求职，我看到很多招聘说明上需要交互设计师编写<strong>界面交互设计文档</strong>，请问界面交互设计文档是什么文档？怎么编写呢</div>
<div>这让我想起来2009年自己在项目里也大力推行过交互说明文档（在下文中，简称为DRD），格式倒没什么限制，交互设计师自己写到界面上也行，单独文档成文也行，总之就是让交互设计师能够将界面承载不了的信息通过文档沉淀下来，降低项目里的沟通成本和风险。今天整理电脑，翻出以前的PPT，分享之。</div>
<p><span id="more-943"></span></p>
<div>这将涉及到几个问题：</div>
<div><img src="http://1871.img.pp.sohu.com.cn/images/blog/2012/3/11/15/27/u82672385_136c116bdabg213.png" border="0" alt="" /></div>
<div><strong><span style="font-size: medium;">一. 什么是交互说明文档（DRD）？</span></strong></div>
<div>
<div>所谓DRD即是用来承载交互说明，并交付给前端、测试以及开发工程师参考的文档。</div>
<div>在项目中，交互设计师的主要产出物可能依次是：site map，page flow，wireframes。有的大型项目前期，交互设计师有可能还会产出用户需求分析文档（与PD产出的市场需求文档不一样的是，URD更多侧重于对目标用户的需求分析）。</div>
<div>DRD则很少有人专门撰写。如果需要对交互设计进行说明，聪明的交互设计师往往会直接标注在线框图里，或者在项目中不断和前端工程师和开发工程师口口相传，反复验收，不断迭代修改来确保所有的交互设计意图最终得以呈现。</div>
<div><span style="font-size: medium;"><br />
</span></div>
<div>
<div><strong><span style="font-size: medium;">二. 为什么要写？</span></strong></div>
<div>DRD 非项目必需环节，一般情况下也不会为交互设计师专门留出相应的时间预估。没有这份文档，项目也会继续，但是可能项目会为此承担不必要的沟通成本和时间成 本。严重的话，项目的质量也会受到影响。所以写与不写，交互设计师需要做把握，时间被统一包含在“线框图”环节内——如果你要写，请在评估时预留1-2天 的时间。</div>
<div>那么，结合我过去的经历，谈一下此文档的必要性。</div>
<div>下图是一个产品开发项目基本的流程。</div>
<div><img src="http://1862.img.pp.sohu.com.cn/images/blog/2012/3/11/15/28/u82672385_136c117c9a6g214.png" border="0" alt="" /></div>
</div>
<div>
<div>敏 捷开发意味着很多不同角色的流程需要并行操作。如果等到产品经理的FRD已经全部敲定，交互设计师再开始去画线框图，固然会减少沟通成本和返工风险，但是 同时意味着交互设计师的很多想法不被采纳。如果产品经理再强一些，他甚至会在FRD里连原始的DEMO也一并绘制出来了，功能性的需求和界面交互的需求有 时无法区分太清楚——比如他会在FRD里直接要求每页条目40条，超过40条即分页。而交互设计师可能会认为像蘑菇街那样不断装载出足够长的页面会更亲 和……所以，我们希望是和产品经理同时开始工作，在术业有专攻的时候相互补充。</div>
<div>同样，开发工程师也 希望及早介入需求，在FRD并未确认的时候就了解需求，进而将商业需求和功能需求转化为开发工程师看得明白的开发需求清单（这个清单，大部分叫做UC，即 USE  CASE）,当这份清单由工程师需求分析师——在过去，这个角色被叫简称为RA，但是目前已经取消此专门的职位，而是由开发工程师代表担纲此环节工作，为 了便于描述，在此文里，我仍然将做这件事情的人称为RA——交付给具体的执行工程师后，执行工程师基本上可以当作一条条的checklist开始高效工 作，而不必再思考商业逻辑和需求。同样，测试工程师也需要编写具体的文档去指导很多测试人员在开发后高效测试，这也是基于UC和FRD去撰写的。</div>
</div>
<div>所以，开发需求分析是个很重要的环节。那RA是如何来完成需求分析工作的呢？</div>
<div>
<ul>
<li>前期介入，对PD进行开发需求评估支持；</li>
<li>参与每次的FRD评审会；</li>
<li>详细审阅FRD文档并不断与PD确认。</li>
</ul>
<div>
<div>对于做这件事情的人来说，足够详尽的FRD是非常重要的。所以一份FRD虽然是PD产出，但是很多实施细节则是由开发工程师不断沟通评估并确认下来的。而设计需求的传递，却存在很多问题。除了线框图，没有“详尽的说明性的文档”告诉他们。比如：</div>
<div><img src="http://1802.img.pp.sohu.com.cn/images/blog/2012/3/11/16/0/u82672385_136c119fad3g215.png" border="0" alt="" /></div>
<div>
<div>一方面，交互设计师对产品经理说：这块由我们来考虑，你的文档不必包含设计上的说明，这随时会调整的。</div>
<div>另一方面，线框图的评审有时会让RA参与，有时却没有叫他们。即使叫上了他们，他们也会发现交互设计的需求变化要比FRD变化快。另外，他们会认为UC不必写太多关于交互设计的需求。</div>
<div>在某个大型项目结束后，作为交互设计师，我进行了一些调研，听听这相关人员是怎么表述问题的：</div>
</div>
<div>
<div><strong>开发部门的需求分析师：</strong></div>
<div>
<ul>
<li>每次变动都很痛苦，设计变了之后，我就要跟着改UC，改截图，有时候UED改了还忘了通知我们，导致UC有问题……</li>
<li>页面交互的需求容易漏掉，因为UC里面不可能写太多交互方面的东西。</li>
<li>希望UED能够在提交HTML DEMO给RA时，能同时给出一份页面元素描述文档，需要介绍html demo中的文案、链接以及相关的图片尺寸或显示字符个数。现在RA在这方面花费的时间比较多，经常要和UED去确认这些内容。</li>
</ul>
</div>
<div><strong>产品经理：</strong></div>
<div>
<ul>
<li>前 期RA和PD沟通过程中，有很多交互点点不能够明确，比如“默认显示多少属性值”，“标题显示多少字符”等。在以往的需求和项目中，对待这些问题我们都是 想到一点补一点的到FRD文档或者邮件中去。既增加了沟通成本又会存在遗漏细节的风险。PD为了可控性的需求，往往会“越俎代庖”，直接在FRD注明这种 需求（对于交互设计师来讲，却又导致没有发挥余地）</li>
</ul>
</div>
<div>
<div>走访了一些交互设计师后，他们也存在如何清晰无遗漏将交互设计需求传递下去的困惑：</div>
<div>交互认为很平常的设计需求，如果不表达出来，还是容易被前端和开发忽略掉。我经历的一个项目，前端从头到尾更换了三个人，每次我都要重复去讲解下设计需求，讲得口干舌燥。而且做好后，还需要去验收。</div>
<div>
<ul>
<li>DRD做为参考手册，一定程度上避免不吻合的问题发生。</li>
<li>即使有问题发生，也可以作为界面验收时的Checklist。将“我对A说，我对B说，A对B说”，转变为“A和B共同参考同一份文档”，减少沟通成本及信息不对称。</li>
<li>全程影响用户体验（一直到测试，都需要参照设计文档）。</li>
</ul>
<div>可是以下问题都可以通过一份DRD来解决吗？</div>
<div><img src="http://1821.img.pp.sohu.com.cn/images/blog/2012/3/11/16/3/u82672385_136c11ce507g214.png" border="0" alt="" /></div>
</div>
<div><strong><span style="font-size: medium;">三. 写什么不写什么？</span></strong></div>
</div>
</div>
<div><img src="http://1821.img.pp.sohu.com.cn/images/blog/2012/3/11/16/6/u82672385_136c11f38e0g214.png" border="0" alt="" /></div>
<div>要明确文档的定位，从写什么与不写什么开始，划清DRD以及FRD的边界。</div>
<div><strong>1. 不写视觉规范规格标注</strong></div>
</div>
</div>
</div>
<blockquote>
<div>
<div>
<div>
<div>
<div>这些说明与功能实现没有太大关系，主要是为前端做HTML的时候参考的。一般视觉设计师会在PSD里标注清楚。如图：</div>
</div>
</div>
</div>
</div>
</blockquote>
<div>
<div>
<div>
<div><img src="http://1811.img.pp.sohu.com.cn/images/blog/2012/3/11/16/10/u82672385_136c1227a04g213.png" border="0" alt="" />2. 不写功能实现逻辑。</div>
</div>
</div>
</div>
<blockquote>
<div>
<div>
<div>
<div>如下图所示，作为DRD，你有必要传达清楚Browse by category区域的设计：链接的可点击性，链接的指向，字符与条目的数量限制等，但是具体二级类目排列是按产品数目排还是按字母排，还是人工运营，是FRD要解决的任务。</div>
</div>
</div>
</div>
</blockquote>
<div>
<div>
<div>
<div><img src="http://1802.img.pp.sohu.com.cn/images/blog/2012/3/11/16/10/u82672385_136c122da5dg213.png" border="0" alt="" /></div>
<div><strong>那么文档写什么呢？</strong></div>
<div><strong><br />
</strong></div>
<div><img src="http://1821.img.pp.sohu.com.cn/images/blog/2012/3/11/16/3/u82672385_136c11c5659g215.png" border="0" alt="" /></div>
</div>
<div>举例子说明下：</div>
<div><strong>1. 字符限制</strong></div>
</div>
</div>
</div>
<blockquote>
<div>
<div>提 高空间利用率，有时网页上的动态文字需要从数据库里提取部分然后截断处理。比如下图中的标题和描述。你的DRD需要传达清楚：1，是否要做限制？2，如果 做限制的话，多少字出现截断？截断后是显示为省略号还是不显示？这个汉语设计相对简单，如果英文单词的话，因为是按字符，每个字符的宽度不一致，需要预 估，另外还需要注明是整词截断还是词间截断。</div>
</div>
</blockquote>
<div>
<div>
<div><img src="http://1862.img.pp.sohu.com.cn/images/blog/2012/3/11/16/17/u82672385_136c129472dg215.png" border="0" alt="" /></div>
<div>
<div>
<div>
<div>
<div><strong>2. 链接具体化</strong></div>
</div>
</div>
</div>
</div>
</div>
</div>
<blockquote>
<div>
<div>
<div>
<div>
<div>很多网站都有对搜索结果的筛选设计（refine search），比如aliexpress搜索结果页左侧。这块区域的交互事件是非常复杂的。</div>
<div>
<ul>
<li>类目和属性的不同如何处理</li>
<li>属性以及每条属性显示的属性值的条目是否有显示上的限制？</li>
<li>选中后，被选中的属性值是停留在原地，方便用户记忆，还是放到统一的位置，方便用户统一查看？其他未被选中的属性值是否消失？</li>
</ul>
</div>
<div><img src="http://1811.img.pp.sohu.com.cn/images/blog/2012/3/11/16/25/u82672385_136c130f619g214.png" border="0" alt="" /></div>
<div>要 确保这些你设想中的复杂的交互逻辑能够被理解被呈现，除了一页页的线框图，你有必要再三让前端工程师和开发工程师了解并达成认知一致。所以你需要将页面上 的关键链接事件标识清楚。它们有的指向无需刷新页面的交互，有的指向你安排的并非PD安排的某个中间页面（page flow是交互设计师的职责）</div>
</div>
</div>
</div>
</div>
</blockquote>
<div>
<div>
<div>
<blockquote>
<div>
<div><img src="http://1871.img.pp.sohu.com.cn/images/blog/2012/3/11/16/22/u82672385_136c12d9b70g214.png" border="0" alt="" /></div>
</div>
</blockquote>
</div>
<div>
3. 交互细节说明</div>
</div>
</div>
<blockquote>
<div>相 信我，我很不愿意写这些东西。我喜欢在会议室向各位涉众演示我的线框图，我会研究用axure制作各种动态效果，达到它足够逼真呈现各种联动——比如当你 选择了下拉菜单中的某项时，页面上其他区域也发生相应的变化。可是，Axure不是全能的。即使能够表达出来，线框图交付出去，也不能确保其他人都能够一 一进行点击尝试。所以只能在会议室反复讲解，在事后再三检查并敦促修改。</p>
</div>
<div>但是当我尝试用下图对这块小小且复杂的区域进行详细说明后，事情变得简单多了。所以我用节省的时间去写了这份PPT.</div>
</blockquote>
<blockquote>
<div>
<div><img src="http://1862.img.pp.sohu.com.cn/images/blog/2012/3/11/16/5/u82672385_136c139f523g215.png" border="0" alt="" /></div>
<div>又如，你可以在这里说明任何你想要的效果。你的受众也只需要用10分钟时间阅读完毕，标注出与他工作相关的重点，存档并在遇到问题，找不到你人时随时参考。</div>
<div><img src="http://1821.img.pp.sohu.com.cn/images/blog/2012/3/11/16/10/u82672385_136c13eb0d1g215.png" border="0" alt="" /></div>
</div>
</blockquote>
<div>
<div>5. 表单的校验</div>
</div>
<blockquote>
<div>
<div>这也是一项不怎么有创意的事情，但是你若不事先想清楚，在项目过程中有点麻烦。写文档看似枯燥乏味，反过来想也是让你自己再好好思量审核设计本身的关键步骤。我曾经自以为完善的交互设计方案就是在写DRD的时候发现存在重大的纰漏，然后及时优化的。</div>
</div>
</blockquote>
<blockquote>
<div>
<div><img src="http://1802.img.pp.sohu.com.cn/images/blog/2012/3/11/16/12/u82672385_136c14091deg214.png" border="0" alt="" /></div>
</div>
</blockquote>
<div>
<div>6. 浏览器的兼容性要求</div>
</div>
</div>
<blockquote>
<div>
<div>
<div>你 们的产品兼容所有浏览器简直是梦想，但是有时出于效率的要求，我们必须战略性放弃某些浏览器，比如IE6.:D 。  这个决定谁来做？是前端工程师还是产品经理？还是你——交互设计师？我认为决定权在交互设计师这里，但是他必须和产品经理达成一致，并与前端确认。你要求 兼容的浏览器越多，标准越高，前端的工作量就会越大，测试的工作量甚至也会翻倍。</div>
</div>
</div>
</blockquote>
<div>
<div><img src="http://1862.img.pp.sohu.com.cn/images/blog/2012/3/11/16/16/u82672385_136c1441a19g213.png" border="0" alt="" /></div>
<div><span style="font-size: medium;"><strong>四. 什么时间交付呢？</strong></span></div>
<div>Heidi的建议：尽可能与你的线框图同时交付，如果你先交付出线框图，在撰写DRD的时候，极大可能会发现问题或产生优化的想法。但是往往写DRD至少需要1-2天的时间，你不可能让所有下游等着你的工作。所以：</div>
<div>
<ul>
<li>你可以交付出线框图供视觉先开始。视觉设计往往会先做风格定位设计，这和交互细节关系不大。</li>
<li>先交付出已经确定的线框图给前端，然后在1-2天DRD后，若有改动，与前端当面一一确认并一起交付。</li>
</ul>
<div><strong>五. 如何写DRD?</strong></div>
<div><span style="font-size: small;"><br />
</span></div>
<div><span style="font-size: small;"><strong>1. 选择最有效率的工具。</strong></span></div>
<div><span style="font-size: small;">我的经验是这个工具最好能够提供清晰的目录导航结构，而且易标注。word确实是个写文档的好工具，不管你信不信，反正我是信了。</span></div>
<div><img src="http://1871.img.pp.sohu.com.cn/images/blog/2012/3/11/16/27/u82672385_136c14e0981g214.png" border="0" alt="" /></div>
</div>
</div>
<div><strong>2. 建立固定的目录结构</strong></div>
<div>下图仅供参考。</div>
<div><img src="http://1881.img.pp.sohu.com.cn/images/blog/2012/3/11/17/1/u82672385_136c15105cag213.png" border="0" alt="" /></div>
<div>具体里面的细节，就不一一罗嗦了。</div>
<div><strong><span style="font-size: medium;">六. 重要的原则</span></strong></div>
<div>准 备写DRD的朋友，请认识清楚此文档真正要解决的问题是什么？如果是解决沟通偏差、需求遗漏、沟通成本高的问题，你在项目里没有出现过这种问题，各合作方 也反馈良好，那么这个文档就无需写。如果是解决对设计需求进行存档，便于后续人员改版时查看的问题，则又是另外一回事（经验证明，过去的DRD确实能够在 改版时起到一定的帮助，在我离开原项目很久后，新的设计师还找我要过相应项目的文档，了解过去的设计逻辑）。</div>
<div>
<ul>
<li><strong>不是为了写文档而写文档</strong>（而是为了解决问题）</li>
<li><strong>适合于项目、合作方</strong>（大项目有大文档，小需求有灵巧的解决方案）</li>
<li><strong>工具不是问题</strong>（易传播，易标注，成目录即可）</li>
<li><strong>模版不是问题，大家看明白就可</strong></li>
<li><strong>完美的文档无法取代面对面的沟通</strong>（评审会和讨论不会因为文档而减少）</li>
<li><strong>需要在实践中不断改进</strong></li>
</ul>
</div>
<div>
<div><span style="font-size: medium;"><strong>七. 谁来写？</strong></span></div>
</div>
<div><img src="http://1842.img.pp.sohu.com.cn/images/blog/2012/3/11/17/8/u82672385_136c15847fag214.png" border="0" alt="" /></div>
<div>我建议由交互设计师发起，但是由前端工程师进行修订，再传递给开发工程师。</div>
<div>有很多需求，交互设计师只要求实现即可，但是他可能并不在乎是前端实现还是后端实现。前端工程师对DRD进行把关和修订，能够将设计语言转化为工程师能够看懂的语言，且能够划定与开发的实现边界。</div>
<div><strong><span style="font-size: medium;"><br />
</span></strong></div>
<div><strong><span style="font-size: medium;">八. 与其他产出物的关系</span></strong></div>
<div>项目中交付物对应不同的使用角色，如下图所示：</div>
<div><img src="http://1871.img.pp.sohu.com.cn/images/blog/2012/3/11/17/11/u82672385_136c15b1454g213.png" border="0" alt="" /></div>
<div>但是有个问题是，虽然DRD的目标受众有开发和测试，但是让开发工程师同时参考那么多文档是不现实的，所以仍然是开发工程师的接口人，也就是事实上的RA需求分析作为需求整合传递的角色，将商业需求和设计需求，传达给具体的执行开发工程师与测试工程师：</div>
<div><img src="http://1881.img.pp.sohu.com.cn/images/blog/2012/3/11/17/15/u82672385_136c15e3c46g215.png" border="0" alt="" /></div>
<div><strong><span style="font-size: small;">【总结】</span></strong></div>
<div>对于坚持撰写DRD的我来说，DRD的好处自己当然是明白的。但是并非所有人都喜欢写文档，都喜欢看文档。</div>
<div>解 决问题有多种方案，DRD只是其中一个。不过，当你因为设计需求传递过程中发生了问题，或者你的需求被理解偏差，或者你的需求被遗漏，或者你接手的项目改 版，因为要梳理过去的设计逻辑焦头烂额时，你可以试试用DRD。如果使用过程中还是存在问题，那么就想想是否还存在别的解决方案吧～</div>
<div></div>
<div>转自http://heidixie.blog.sohu.com/207036464.html</div>
]]></content:encoded>
			<wfw:commentRss>http://yulans.cn/%e5%bc%80%e5%8f%91%e6%96%b9%e6%b3%95/%e5%a6%82%e4%bd%95%e5%86%99%e4%b8%80%e4%bb%bd%e4%ba%a4%e4%ba%92%e8%af%b4%e6%98%8e%e6%96%87%e6%a1%a3/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>打开zend studio 项目的方法</title>
		<link>http://yulans.cn/php/%e6%89%93%e5%bc%80zend-studio-%e9%a1%b9%e7%9b%ae%e7%9a%84%e6%96%b9%e6%b3%95</link>
		<comments>http://yulans.cn/php/%e6%89%93%e5%bc%80zend-studio-%e9%a1%b9%e7%9b%ae%e7%9a%84%e6%96%b9%e6%b3%95#comments</comments>
		<pubDate>Thu, 16 Feb 2012 12:31:02 +0000</pubDate>
		<dc:creator>cmpan</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[实践经验]]></category>

		<guid isPermaLink="false">http://yulans.cn/?p=941</guid>
		<description><![CDATA[我们用zend studio创建一个php项目的时候，把项目文件夹复制到其他电脑上以或切换工作区后，可以直接打开该项目。 打开方法： File > Import > General > Existing Projects into Workspace 然后选择项目所在文件夹即可。 Zend Studio每个工作区下创建一个.metadata和RemoteSystemsTempFiles文件夹来保存项目相关信息。 我们打开zend studio的时候，它打开了一个Workspace，这个Workspace里面可以有多个项目，项目文件可以再工作区文件夹里面，也可以在外面。 不像VS打开单个项目，而是打开一个Workspace，而项目则在Workspace中，如果想打开其他不在工作区中的项目，则把项目导入工作区即可。]]></description>
			<content:encoded><![CDATA[<p>我们用zend studio创建一个php项目的时候，把项目文件夹复制到其他电脑上以或切换工作区后，可以直接打开该项目。</p>
<p>打开方法：<br />
File > Import > General > Existing Projects into Workspace</p>
<p>然后选择项目所在文件夹即可。</p>
<p>Zend Studio每个工作区下创建一个.metadata和RemoteSystemsTempFiles文件夹来保存项目相关信息。<br />
我们打开zend studio的时候，它打开了一个Workspace，这个Workspace里面可以有多个项目，项目文件可以再工作区文件夹里面，也可以在外面。</p>
<p>不像VS打开单个项目，而是打开一个Workspace，而项目则在Workspace中，如果想打开其他不在工作区中的项目，则把项目导入工作区即可。</p>
]]></content:encoded>
			<wfw:commentRss>http://yulans.cn/php/%e6%89%93%e5%bc%80zend-studio-%e9%a1%b9%e7%9b%ae%e7%9a%84%e6%96%b9%e6%b3%95/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Zend studio 8+设置默认使用UTF-8编码</title>
		<link>http://yulans.cn/php/zend-studio-8%e8%ae%be%e7%bd%ae%e9%bb%98%e8%ae%a4%e4%bd%bf%e7%94%a8utf-8%e7%bc%96%e7%a0%81</link>
		<comments>http://yulans.cn/php/zend-studio-8%e8%ae%be%e7%bd%ae%e9%bb%98%e8%ae%a4%e4%bd%bf%e7%94%a8utf-8%e7%bc%96%e7%a0%81#comments</comments>
		<pubDate>Wed, 15 Feb 2012 04:42:21 +0000</pubDate>
		<dc:creator>cmpan</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[实践经验]]></category>

		<guid isPermaLink="false">http://yulans.cn/?p=939</guid>
		<description><![CDATA[Zend Studio 8+默认使用系统编码，也就是GBK，当我们用Zend Studio 用从SVN服务器导出项目的时候，如果项目使用UTF-8字符集，打开文件的时候中文将会出现乱码。 解决： 将如下设置为使用UTF-8编码 1、Project > Properties > Resource 2、Window > Preferences > General > Workspace 3、Window > Preferences > Web > HTML Files 4、Window > Preferences > Web > CSS Files 5、Window > Preferences > PHP > Debug]]></description>
			<content:encoded><![CDATA[<p>Zend Studio 8+默认使用系统编码，也就是GBK，当我们用Zend Studio 用从SVN服务器导出项目的时候，如果项目使用UTF-8字符集，打开文件的时候中文将会出现乱码。<br />
解决：<br />
将如下设置为使用UTF-8编码<br />
1、Project > Properties > Resource<br />
2、Window > Preferences > General > Workspace<br />
3、Window > Preferences > Web > HTML Files<br />
4、Window > Preferences > Web > CSS Files<br />
5、Window > Preferences > PHP > Debug</p>
]]></content:encoded>
			<wfw:commentRss>http://yulans.cn/php/zend-studio-8%e8%ae%be%e7%bd%ae%e9%bb%98%e8%ae%a4%e4%bd%bf%e7%94%a8utf-8%e7%bc%96%e7%a0%81/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>IE兼容性bug汇总</title>
		<link>http://yulans.cn/uncategorized/ie%e5%85%bc%e5%ae%b9%e6%80%a7bug%e6%b1%87%e6%80%bb</link>
		<comments>http://yulans.cn/uncategorized/ie%e5%85%bc%e5%ae%b9%e6%80%a7bug%e6%b1%87%e6%80%bb#comments</comments>
		<pubDate>Sun, 04 Dec 2011 11:20:08 +0000</pubDate>
		<dc:creator>cmpan</dc:creator>
				<category><![CDATA[未分类]]></category>

		<guid isPermaLink="false">http://yulans.cn/?p=922</guid>
		<description><![CDATA[项目最新版本的开发进入后期阶段，今天鼓起勇气打开IE Test，如我所料啊！页面在IE6下面目全非了，呜呜~~ 现在开始修复IE的Bug（大部分是IE6，IE7 8也有一些），顺便记录下来。 1、png图片在IE6下出现透明或背景变灰的bug; 分析： 随处可见，遇到时就想好策略。 解决方法：1、使用滤镜，语法如下 .image-style { background-image: none; filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src=&#8221;filename.png&#8221;, sizingMethod=&#8221;scale&#8221;); } 注意：使用滤镜需要性能损耗。 2、给IE6单独制作一张.gif图片，打上hack .image-style{ background:transparent url(&#8220;filename.png&#8221;) no-repeat scroll 0 0; _background-image:url(&#8220;filename.gif&#8221;); } 这种方法只需要在切图时多存储一份.gif格式的图片，一般采用这种方法。 2、position:absolute定位在IE6下存在left和bottom相对最外层视窗（body）定位的bug: 分析： 由于在IE6下，相对定位的元素没有获得IE内部的haslayout属性，因此不创建新的定位上下文，所以绝对定位的元素相对于视口进行定位。 解决方法：1、设置height：1%；//小的高度不会对实际高度找出影响 2、相对定位的祖先元素设置过高度和宽度。 3、相对定位的祖先元素设置_zoom:1,用于触发元素的layout属性。 3、IE6下border不显示的bug 分析：对一个div定义border，但是却不显示。 解决方法：定义宽度；定义高度；清除浮动。 4、在 W3C 规范中 position 是可以使用在任何元素上的，但table元素的 position:relative 却有例外： 12&#60;strong&#62;The effect of ‘position:relative’ on table-row-group, table-header-group, table-footer-group, table-row, table-column-group, table-column, table-cell, &#60;/strong&#62; [...]]]></description>
			<content:encoded><![CDATA[<div id="cnblogs_post_body">
<p>项目最新版本的开发进入后期阶段，今天鼓起勇气打开IE Test，如我所料啊！页面在IE6下面目全非了，呜呜~~</p>
<p>现在开始修复IE的Bug（大部分是IE6，IE7 8也有一些），顺便记录下来。<br />
<span id="more-922"></span><br />
<strong> 1、png图片在IE6下出现透明或背景变灰的bug;</strong></p>
<p><strong>分析：</strong> 随处可见，遇到时就想好策略。</p>
<p><strong>解决方法：</strong>1、使用滤镜，语法如下</p>
<p>.image-style { background-image: none;</p>
<p>filter:progid:<a href="http://1625.me/wiki/DXImageTransform.html">DXImageTransform</a>.Microsoft.<a href="http://1625.me/wiki/AlphaImageLoader.html">AlphaImageLoader</a>(src=&#8221;filename.png&#8221;, sizingMethod=&#8221;scale&#8221;);</p>
<p>}</p>
<p>注意：使用滤镜需要性能损耗。</p>
<p>2、给IE6单独制作一张.gif图片，打上hack</p>
<p>.image-style{ background:transparent url(&#8220;filename.png&#8221;) no-repeat scroll 0 0;</p>
<p>_background-image:url(&#8220;filename.gif&#8221;);</p>
<p>}</p>
<p>这种方法只需要在切图时多存储一份.gif格式的图片，一般采用这种方法。</p>
<p><strong> 2、position:absolute定位在IE6下存在left和bottom相对最外层视窗（body）定位的bug:</strong></p>
<p><strong>分析：</strong> 由于在IE6下，相对定位的元素没有获得IE内部的haslayout属性，因此不创建新的定位上下文，所以绝对定位的元素相对于视口进行定位。</p>
<p><strong>解决方法：</strong>1、设置height：1%；//小的高度不会对实际高度找出影响</p>
<p>2、相对定位的祖先元素设置过高度和宽度。</p>
<p>3、相对定位的祖先元素设置_zoom:1,用于触发元素的layout属性。</p>
<p><strong>3、IE6下border不显示的bug</strong></p>
<p><strong>分析：</strong>对一个div定义border，但是却不显示。</p>
<p><strong>解决方法</strong>：定义宽度；定义高度；清除浮动。</p>
<p><strong> 4、在 W3C 规范中 <a title="position" href="http://www.w3.org/TR/2006/WD-CSS21-20061106/visuren.html#propdef-position" target="_blank">position</a> 是可以使用在任何元素上的，但table元素的 position:relative 却有例外：</strong></p>
<div class="codecolorer-container html4strict mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:680px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="html4strict codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">strong</span>&gt;</span>The effect of ‘position:relative’ on table-row-group, table-header-group, table-footer-group, table-row, table-column-group, table-column, table-cell, <span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">strong</span>&gt;</span><br />
<span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">strong</span>&gt;</span>and table-caption elements is undefined. <span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">strong</span>&gt;</span></div></td></tr></tbody></table></div>
<p><strong>分析</strong>：经测试，在浏览器（IE 除外）中给 table 定义 position:relative 属性是无效的。如果的确需要在表格中使用该属性，建议在单元格中嵌套一个 div 元素，再在其上设置 position:relative 属性来模拟。</p>
<p><strong> 5、IE6下overflow:hidden失效bug</strong></p>
<p><strong>分析</strong>：在开发中经常使用overflow:hidden来清除浮动，也就是在浮动元素的父元素中使用这个属性，就相当于添加了一个clear元素。但是，这在IE6下不给力。</p>
<p><strong>解决方法</strong>：只要在父元素中给定一个宽度就可解决这个bug；</p>
<p><strong>6、IE6下的双空白边浮动bug</strong></p>
<p><strong>分析</strong>：这是一个常见的出名BUG，给IE6下的浮动元素定义margin-left或者margin-right，实际上是数值的两倍。</p>
<p><strong>解决方法：</strong>把这个浮动元素设置为display:inline;</p>
<p><strong>7、IE6下float元素如果没有定义宽度，在浮动时它会自动占满一行的bug</strong></p>
<p>分析：即使对这个浮动元素内部的元素设置了宽度，也是无效的</p>
<p>解决方法：一般用于布局的浮动元素不会要求横向可自由拓展的，那么可以通过设置宽度来解决这个bug.</p>
<p><strong>8、IE6下img元素底部留白的bug</strong></p>
<p><strong>分析</strong>：代码结构如下</p>
<p>&lt;div&gt;</p>
<p>&lt;img src=&#8221;filename&#8221; alt=&#8221;图片&#8221; /&gt;</p>
<p>&lt;/div&gt;</p>
<p><strong>解决方法</strong>：把代码结构改成如下：</p>
<p>&lt;div&gt;&lt;img src=&#8221;filename&#8221; alt=&#8221;图片&#8221; /&gt;&lt;/div&gt;</p>
<p>并设置img元素的display:block（img元素默认是一个display:inline的元素）</p>
<p><strong>9、IE6 7下的浮动元素的父元素在拖动滚动条时出现的边框缺失bug</strong></p>
<p><strong>解决方法</strong>：设置zoom，使元素获得布局。</p>
<p><strong>10、 IE（6 7 8） 的 z-index bug </strong></p>
<p><strong>分析</strong>：看代码</p>
<div>
<div>xhtml:</div>
<div>&lt;div id=&#8221;container&#8221;&gt;<br />
&lt;div id=&#8221;box1&#8243;&gt;这个box应该在上面&lt;/div&gt;<br />
&lt;/div&gt;<br />
&lt;div id=&#8221;box2&#8243;&gt;<br />
这个box应该在下面，IE浏览器会对定位元素产生一个新的stacking<br />
context ，甚至当元素 z-index的为“auto”。<br />
&lt;/div&gt;</div>
<div>css:</div>
<div>container { position: relative; }<br />
#box1 { position: absolute; top: 100px; left: 210px; width: 200px; height: 200px; background-color: yellow; z-index: 20; }<br />
#box2 { position: absolute; top: 50px; left: 160px; width: 200px; height: 200px; background-color: green; z-index: 10; }</div>
</div>
<p><strong>结果：</strong>ff/chrome显示为box1在box2上，而IE确实box2显示在了box1上</p>
<p><strong>原因：</strong>IE浏览器会对定位的元素产生一个新的stacking context，并且从z-index:0开始</p>
<p><strong>11、关于IE8的一些资料</strong></p>
<ul>
<li><a title="IE8" href="http://css-discuss.incutio.com/?page=IE8" target="_blank">IE8 – css-discuss</a></li>
<li><a title="IE8 beta 1 - first tests" href="http://www.quirksmode.org/blog/archives/2008/03/ie8_beta_1_firs.html" target="_blank">IE8 beta 1 – first tests</a></li>
<li><a title="Internet Explorer 8" href="http://www.howtocreate.co.uk/ie8.html" target="_blank">Internet Explorer 8</a></li>
</ul>
<p><strong>12、IE6下的重复字符bug</strong></p>
<p><strong>分析：</strong>如下图所示</p>
<p><img src="http://pic002.cnblogs.com/images/2011/260614/2011021511545566.gif" alt="" /></p>
<p>出现这个BUG的“机遇“其实并不大，要满足以下一个或者多个条件：</p>
<blockquote><p>1.父元素的内部有多个<strong>浮动</strong>元素。</p>
<p>2. 最后一个浮动元素前有隐藏元素：包括html<strong>注释</strong>和display：<strong>none</strong>的元素</p>
<p>3.子元素的宽度和父元素相同，父元素的宽度减去子元素宽度<strong>小于3px</strong></p></blockquote>
<p>最终得出的结论是：<strong>溢出文字的字数=注释的条数 *2-1</strong></p>
<p>这个变态BUG的最早文献是出现在2004年，<a href="http://www.positioniseverything.net/explorer/dup-characters.html" target="_blank">这里可见</a>。</p>
<h3><strong>解决方法：</strong></h3>
<blockquote><p>1.把浮动的子元素加上display:inline;属性（推荐）</p>
<p>2.去掉注释和所有隐藏元素（缺点：特殊情况下不一定可以删除）</p>
<p>3. 把浮动的子元素加上margin-right:-3px;属性（缺点：要加IE6的hack，也算是好方法）</p>
<p>4.在隐藏的DIV外嵌套一个DIV（缺点：增加的结果复杂性）</p></blockquote>
<p>详解：<a href="http://www.cnblogs.com/javashi/archive/2010/05/08/1730504.html">http://www.cnblogs.com/javashi/archive/2010/05/08/1730504.html</a></p>
<p><strong>13、IE6下的空div bug</strong></p>
<p><strong>分析：</strong>通常在实现可拓展的圆角框时，习惯在头尾使用两个空元素来放置背景图片如&lt;div&gt;&lt;/div&gt;，但是这个空元素在IE6下会莫名的产生一个高度，为原先定义的2倍。代码如下：</p>
<div>
<div>
<div id="highlighter_35983">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>1</div>
</td>
<td>
<div>
<div>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:680px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&amp;lt;</div></td></tr></tbody></table></div>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:680px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">div</div></td></tr></tbody></table></div>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:680px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">class</div></td></tr></tbody></table></div>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:680px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">=</div></td></tr></tbody></table></div>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:680px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&quot;hd&quot;</div></td></tr></tbody></table></div>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:680px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&amp;gt;&amp;lt;/</div></td></tr></tbody></table></div>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:680px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">div</div></td></tr></tbody></table></div>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:680px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&amp;gt;&amp;lt;</div></td></tr></tbody></table></div>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:680px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">br</div></td></tr></tbody></table></div>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:680px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&amp;gt;&amp;lt;</div></td></tr></tbody></table></div>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:680px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">div</div></td></tr></tbody></table></div>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:680px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">class</div></td></tr></tbody></table></div>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:680px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">=</div></td></tr></tbody></table></div>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:680px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&quot;bd&quot;</div></td></tr></tbody></table></div>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:680px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&amp;gt;content&amp;lt;/</div></td></tr></tbody></table></div>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:680px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">div</div></td></tr></tbody></table></div>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:680px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&amp;gt;&amp;lt;</div></td></tr></tbody></table></div>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:680px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">br</div></td></tr></tbody></table></div>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:680px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&amp;gt;&amp;lt;</div></td></tr></tbody></table></div>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:680px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">div</div></td></tr></tbody></table></div>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:680px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">class</div></td></tr></tbody></table></div>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:680px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">=</div></td></tr></tbody></table></div>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:680px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&quot;ft&quot;</div></td></tr></tbody></table></div>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:680px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&amp;gt;&amp;lt;/</div></td></tr></tbody></table></div>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:680px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">div</div></td></tr></tbody></table></div>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:680px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&amp;gt;&amp;lt;</div></td></tr></tbody></table></div>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:680px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">br</div></td></tr></tbody></table></div>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:680px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&amp;gt;样式：&amp;lt;</div></td></tr></tbody></table></div>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:680px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">br</div></td></tr></tbody></table></div>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:680px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&amp;gt; .hd{background:url(&quot;filename&quot;) transparent ...; height:5px;}</div></td></tr></tbody></table></div>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<p><strong>解决方法：</strong>为这个空元素设定line-height:0;font-size:1px;即可解决这个bug。</p>
<p><strong>14、IE6下对position：fixed不支持的bug</strong></p>
<p>分析：问题的原因是IE6下的fixed元素绝对定位位置是针对html元素的，而滚动条则是针对body元素的</p>
<p>解决方法：</p>
<div>
<div>body{background-image:(&#8220;xx.gif&#8221;);}/*一张不存在的图片*/<br />
.fixed{_postion:absolute;expression(eval(document.documentElement.scrollTop+document.documentElement.clientHeight- this.offsetHeight))}//底部&nbsp;</p>
<pre>expression(eval(document.documentElement.scrollTop));/*头部*/
expression(eval(document.documentElement.clientHeight/2+document.documentElement.scrollTop-80));/*右侧*/</pre>
</div>
</div>
<p><strong>15、IE6下input/textarea/select元素继承父元素的水平margin的bug</strong></p>
<p>分析：<a href="http://haslayout.net/css/Form-Control-Double-Margin-Bug">http://haslayout.net/css/Form-Control-Double-Margin-Bug</a></p>
<p>解决方法：这个bug在开发中是经常遇到的，解决方法就是在input或者textarea元素上使用负的margin，使元素重新布局。</p>
<p><strong>16、IE6不支持min-height/max-height/min-width/max-width的bug</strong></p>
<p>分析：无</p>
<p>解决方法：根据IE6的特性模拟出来</p>
<div>
<div>height:auto!important;<br />
min-height: x px;/*需要的最小高度*/<br />
height: x px</div>
</div>
<p><strong>17、IE6下position：relative的bug</strong></p>
<p>分析：在IE6下父层（或者上层）设置为position:relative且没有写宽度,而这个元素又被一个带有滚动条overflow：auto/scroll属性包含的时候，它将会表现出absulute，并且在鼠标移上去时，会在整个屏幕上滑动；</p>
<p>解决方法：找到了bug的原因后，事情就变得简单了，方法有两种</p>
<p>1、把父层（或者更上层）的relative删掉，但有时候这个relative是必须的，那就只能用第二种方法了；</p>
<p>2、不删除relative，但给这个元素设定一个宽度，可以是100%。</p>
<p><strong>18、IE6下样式不表现</strong></p>
<p>分析：通常一个页面需要载入2-3个样式文件，但其中有某个样式文件里的样式完全不表现，你或许怀疑这个文件没有被加载，这个时候打开IE Debug看文件的传输情况，很清晰的看到文件正常加载了，纳闷吧！后来找到了</p>
<p>原因，主要有两个：</p>
<p>1、这个文件的编码和页面定义的character编码不一致；</p>
<p>2、不正确的注释（这个也有可能是页面编码不正确，而注释是中文导致的）</p>
<p>解决方法：检查页面编码；去掉注释；</p>
<p><strong>19、IE6浮动元素与非浮动元素间隔的3pxbug</strong></p>
<p>分析：在IE下，一个浮动元素与一个非浮动元素相邻时候，中间会莫名的出现一个3px的间隔。</p>
<p>解决方法：1、由于明确知道是3px，所以可以使用负的margin，但是由于IE下不同的盒模式，又会导致宽度上的不同，浮动元素的宽度如果超出了父元素的宽度在IE下是会有bug的，所以不建议使用这种方法。</p>
<p>2、不让浮动元素和非浮动元素相邻，或者把非浮动元素也设置为浮动元素。</p>
<p><strong>20、IE6下img元素底部出现5px的空白间隙bug</strong></p>
<p>分析：</p>
<p>代码结构如下：</p>
<div>
<div>&lt;li&gt;&lt;a href=&#8221;#&#8221;&gt;&lt;img src=&#8221;xxx.png&#8221; alt=&#8221;xxx&#8221;/&gt;&lt;/a&gt;&lt;/li&gt;</div>
</div>
<p>在ie6下会出现5px的空白。</p>
<p>解决方法：</p>
<div>
<div>1、<br />
ul li{display:block;font-size:0;}<br />
img{vertical-align:bottom;}<br />
2、img{_margin-bottom:-5px;}</div>
</div>
<p><strong>21、IE下透明度无效bug</strong></p>
<p>分析：在IE的所有版本中，父元素设置了透明度，而子元素如果有position：relative/absolute时，子元素无法继承父元素的透明度。</p>
<p>找了一些资料：http://younglab.blog.51cto.com/416652/431363</p>
<p>解决方法：除了资料中使用的方法（触发layout），还有个土方法，那就是在透明度比较小的区域中，使用具有透明度的图片和字体模糊。</p>
<p><strong>22、IE下overflow：auto/scroll不起作用的bug</strong></p>
<p>分析：在IE下overflow不起作用，但是在火狐下是正常显示的，IE下overflow如同虚设，内容是表现出来了，但是确实在区域的下面显示，滚动条也无法使用。</p>
<p>解决方法：对设置了overflow的元素设定高度和宽度（一般使用滚动条的都会设置这些属性），然后给这个元素进行position定位，relative或者absolute都是可以的</p>
<p><strong>23、IE6 7 8下元素重叠bug<br />
</strong></p>
<p>分析：页面操作过程中对一组li元素做remove（）或者hide（）时，被移除的li的下一个兄弟元素会往下偏移，从而发生元素重叠。</p>
<p>解决方法：js中对这个li元素的父元素加上toggleClass(&#8220;ie-hack&#8221;);//ie-hack为不存在的class。<br />
原因不明。</p>
<p><strong>24、IE6/7/8下直接对input输入框使用背景图片，当输入值冲出背景图片宽度时，背景图片会随字符滑动</strong></p>
<p>解决方法：在input框外加一个div，在这个div上设置背景</p>
<p><strong>25、去掉a链接难看外框的方法：</strong></p>
<p>IE下设置hideFocus=true,其他浏览器设置：outline:none</p>
<p>即</p>
<div>
<div>&lt;a href=&#8221;&#8230;&#8221; hideFocus=&#8221;true&#8221; title=&#8221;..&#8221;/&gt;<br />
a{<br />
outline:none<br />
}<br />
还有一种是用expression的方法，但是耗性能，原因是每秒钟执行了很多次。不推荐是用<br />
a{<br />
blr:expression(this.onFocus=this.blue())<br />
}</div>
</div>
<p>未完待续。。</p>
<p>转自：http://www.cnblogs.com/svage/archive/2011/02/12/1952704.html</p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://yulans.cn/uncategorized/ie%e5%85%bc%e5%ae%b9%e6%80%a7bug%e6%b1%87%e6%80%bb/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>使用HTML5开发安卓应用入门</title>
		<link>http://yulans.cn/uncategorized/%e4%bd%bf%e7%94%a8html5%e5%bc%80%e5%8f%91%e5%ae%89%e5%8d%93%e5%ba%94%e7%94%a8%e5%85%a5%e9%97%a8</link>
		<comments>http://yulans.cn/uncategorized/%e4%bd%bf%e7%94%a8html5%e5%bc%80%e5%8f%91%e5%ae%89%e5%8d%93%e5%ba%94%e7%94%a8%e5%85%a5%e9%97%a8#comments</comments>
		<pubDate>Mon, 28 Nov 2011 16:55:03 +0000</pubDate>
		<dc:creator>cmpan</dc:creator>
				<category><![CDATA[未分类]]></category>

		<guid isPermaLink="false">http://yulans.cn/?p=913</guid>
		<description><![CDATA[开发工具安装包下载及安装 1、Eclipse Classic 下载Eclipse Classic版解压 http://www.eclipse.org/downloads/ 下载并安装Android SDK http://developer.android.com/sdk/index.html 下载Android Eclipse插件 ADT http://developer.android.com/sdk/eclipse-adt.html 下载HTML5转手机应用构建工具phonegap http://phonegap.com/download-thankyou 安装eclipse插件小插曲： 拜于天朝的超级局域网，我2M宽带安装插件时速度一直不能达到10K，让我得以挑战人类忍耐的极限。还好用vps做代理后有50K左右的速度，人品好的时候还能到200K+，终于安装上了adt插件。 phonegap官方入门教程：http://phonegap.com/start#android 问题及解决： 解决&#8211;emulator.exe 遇到问题需要关闭。把C:\Documents and Settings\Administrator\.android\目录删除，重新创建svd。 phonegap 的ajax不存在跨域问题。]]></description>
			<content:encoded><![CDATA[<p>开发工具安装包下载及安装<br />
1、Eclipse Classic<br />
下载Eclipse Classic版解压 http://www.eclipse.org/downloads/<br />
下载并安装Android SDK   http://developer.android.com/sdk/index.html<br />
下载Android Eclipse插件 ADT   http://developer.android.com/sdk/eclipse-adt.html<br />
下载HTML5转手机应用构建工具phonegap http://phonegap.com/download-thankyou</p>
<p>安装eclipse插件小插曲：<br />
拜于天朝的超级局域网，我2M宽带安装插件时速度一直不能达到10K，让我得以挑战人类忍耐的极限。还好用vps做代理后有50K左右的速度，人品好的时候还能到200K+，终于安装上了adt插件。</p>
<p>phonegap官方入门教程：http://phonegap.com/start#android</p>
<p>问题及解决：<br />
解决&#8211;emulator.exe 遇到问题需要关闭。把C:\Documents and Settings\Administrator\.android\目录删除，重新创建svd。<br />
phonegap 的ajax不存在跨域问题。</p>
]]></content:encoded>
			<wfw:commentRss>http://yulans.cn/uncategorized/%e4%bd%bf%e7%94%a8html5%e5%bc%80%e5%8f%91%e5%ae%89%e5%8d%93%e5%ba%94%e7%94%a8%e5%85%a5%e9%97%a8/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>IE下类似firebug的调试工具DebugBar</title>
		<link>http://yulans.cn/uncategorized/ie%e4%b8%8b%e7%b1%bb%e4%bc%bcfirebug%e7%9a%84%e8%b0%83%e8%af%95%e5%b7%a5%e5%85%b7debugbar</link>
		<comments>http://yulans.cn/uncategorized/ie%e4%b8%8b%e7%b1%bb%e4%bc%bcfirebug%e7%9a%84%e8%b0%83%e8%af%95%e5%b7%a5%e5%85%b7debugbar#comments</comments>
		<pubDate>Sat, 26 Nov 2011 20:59:34 +0000</pubDate>
		<dc:creator>cmpan</dc:creator>
				<category><![CDATA[未分类]]></category>

		<guid isPermaLink="false">http://yulans.cn/?p=910</guid>
		<description><![CDATA[下载地址：http://www.debugbar.com/download.php 该网站还提供ie下使用的仿firebug-lite的CompanionJS：http://www.my-debugbar.com/wiki/CompanionJS/HomePage 比较有名的IETester也是该站出品：http://www.my-debugbar.com/wiki/IETester/HomePage]]></description>
			<content:encoded><![CDATA[<p>下载地址：http://www.debugbar.com/download.php<br />
该网站还提供ie下使用的仿firebug-lite的CompanionJS：http://www.my-debugbar.com/wiki/CompanionJS/HomePage<br />
比较有名的IETester也是该站出品：http://www.my-debugbar.com/wiki/IETester/HomePage</p>
]]></content:encoded>
			<wfw:commentRss>http://yulans.cn/uncategorized/ie%e4%b8%8b%e7%b1%bb%e4%bc%bcfirebug%e7%9a%84%e8%b0%83%e8%af%95%e5%b7%a5%e5%85%b7debugbar/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>解决ie下JS提示“尚未实现 ”错误</title>
		<link>http://yulans.cn/css/%e8%a7%a3%e5%86%b3ie%e4%b8%8bjs%e6%8f%90%e7%a4%ba%e2%80%9c%e5%b0%9a%e6%9c%aa%e5%ae%9e%e7%8e%b0-%e2%80%9d%e9%94%99%e8%af%af</link>
		<comments>http://yulans.cn/css/%e8%a7%a3%e5%86%b3ie%e4%b8%8bjs%e6%8f%90%e7%a4%ba%e2%80%9c%e5%b0%9a%e6%9c%aa%e5%ae%9e%e7%8e%b0-%e2%80%9d%e9%94%99%e8%af%af#comments</comments>
		<pubDate>Sat, 26 Nov 2011 20:43:48 +0000</pubDate>
		<dc:creator>cmpan</dc:creator>
				<category><![CDATA[CSS]]></category>
		<category><![CDATA[JS]]></category>

		<guid isPermaLink="false">http://yulans.cn/?p=908</guid>
		<description><![CDATA[鼠标经过链接的时候，总提示JS 1行、第xx个字符，错误信息是：“尚未实现 ”。从js入手调试了半天，解决不了问题。想到是不是css里的expression引起的，把链入的样式去掉，果真是。css的expression调用了ie不支持的方法引起js错误提示。IE这SB只知道第1行、第xx个字符出错了，不可信。]]></description>
			<content:encoded><![CDATA[<p>鼠标经过链接的时候，总提示JS 1行、第xx个字符，错误信息是：“尚未实现 ”。从js入手调试了半天，解决不了问题。想到是不是css里的expression引起的，把链入的样式去掉，果真是。css的expression调用了ie不支持的方法引起js错误提示。IE这SB只知道第1行、第xx个字符出错了，不可信。</p>
]]></content:encoded>
			<wfw:commentRss>http://yulans.cn/css/%e8%a7%a3%e5%86%b3ie%e4%b8%8bjs%e6%8f%90%e7%a4%ba%e2%80%9c%e5%b0%9a%e6%9c%aa%e5%ae%9e%e7%8e%b0-%e2%80%9d%e9%94%99%e8%af%af/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>敏捷测试的方法和实践</title>
		<link>http://yulans.cn/%e5%bc%80%e5%8f%91%e6%96%b9%e6%b3%95/%e6%95%8f%e6%8d%b7%e6%b5%8b%e8%af%95%e7%9a%84%e6%96%b9%e6%b3%95%e5%92%8c%e5%ae%9e%e8%b7%b5</link>
		<comments>http://yulans.cn/%e5%bc%80%e5%8f%91%e6%96%b9%e6%b3%95/%e6%95%8f%e6%8d%b7%e6%b5%8b%e8%af%95%e7%9a%84%e6%96%b9%e6%b3%95%e5%92%8c%e5%ae%9e%e8%b7%b5#comments</comments>
		<pubDate>Tue, 15 Nov 2011 18:50:37 +0000</pubDate>
		<dc:creator>cmpan</dc:creator>
				<category><![CDATA[开发方法]]></category>
		<category><![CDATA[敏捷]]></category>

		<guid isPermaLink="false">http://yulans.cn/?p=901</guid>
		<description><![CDATA[有一次，当开发人员完成当前Sprint 任务的代码之后，测试人员与开发人员、产品经理一起来浏览产品、从头到尾走一边，产品经理发现了问题，认为需要对功能进行比较大的修改。这时开发人员估计 需要两天时间才能完成代码，但测试人员反对这样做，我们本来只有5天测试时间，加上这次新做的功能比较多、开发代码质量不高，验收测试已经很紧张。如果再 延迟两天，测试没法完成。产品经理说，你们不是在用敏捷测试方法，应该测得很快，三天应该能完成测试工作啊！ 什么是敏捷测试呢？敏捷测试当然不能简单地理解测得更快，绝对不是比以前用更少时间进行测试，也不是将测试的范围缩小了或将质量降低来减少测试任务。也有人说，只有敏捷开发，没有敏捷测试。 1.下面我们就要讨论一下 1.1究竟什么是敏捷测试？ 2.2敏捷测试有哪些流程改进？ 3.3测试人员如何面对敏捷测试的挑战？ 4.4在敏捷测试中如何制定相应的自动化测试策略？ 等等各种问题。 1. 什么是敏捷测试 假如将过去传统的测试流程和方法硬塞入敏捷开发流程中，测试工作可能会事倍功半，测试人员可能会天天加班，而不能发挥应用的作用。敏捷测试应该是适应敏捷 方法而采用的新的测试流程、方法和实践，对传统的测试流程有所剪裁，有所不同的侧重，例如减少测试计划、测试用例设计等工作的比重，增加与产品设计人员、 开发人员的交流和协作。在敏捷测试流程中，参与单元测试，关注持续迭代的新功能，针对这些新功能进行足够的验收测试，而对原有功能的回归测试则依赖于自动 化测试。由于敏捷方法中迭代周期短，测试人员尽早开始测试，包括及时对需求、开发设计的评审，更重要的是能够及时、持续的对软件产品质量进行反馈。简单地 说，敏捷测试就是持续地对软件质量问题进行及时地反馈，如图1所示。 2. 敏捷测试流程的优化 在敏捷方法中，需求变化比较快、产品开发周期很短，我们目前采用四周时间，也就是每个月发布一个新版本。开发周期短，功能不断累加，给软件测试带来很大 的挑战，软件测试流程要做相应的调整。例如，我们原有的测试规范明确规定，首先要建立项目的主测试计划书，然后再建立每个功能任务的测试计划书，测试计划 书有严格的模板，而且需要和产品经理、开发人员讨论，并和测试团队其他人员（包括测试经理）讨论，最终得到大家的认可和签字才能通过，仅测试计划经过“起 草、评审和签发”一个完整的周期就需要一个月。在敏捷方法中，不再要求写几十页的测试计划书，而是在每个迭代周期，写出一页纸的测试计划，将测试要点（包 括策略、特定方法、重点范围等）列出来就可以了。 在原有测试规范中，要求先用Excel写出测试用例，然后进行讨论、评审，评审通过以 后再导入测试用例库（在线管理系统）中。在敏捷测试中，可能不需要测试用例，而是针对use case 或user story直接进行验证，并进行探索性测试。而节约出来的时间，用于开发原有功能的自动化测试脚本，为回归测试服务。自动化测试脚本将代替测试用例，成为 软件组织的财富。原有测试规范还要求进行两轮回归测试，在敏捷测试中，只能进行一轮回归测试。综合这些考虑，敏捷测试的流程简单有效，如下图2所示。 在敏捷测试流程中，如前所述，测试是一个持续的质量反馈过程，测试中发现的问题及时反馈给产品经理和开发人员，而且某些关键方面也要得到我们足够的关注，主要有： l 测试人员不仅要全程参与需求、产品功能设计等讨论，而且要面对面地、充分地讨论（包括带语言、视频的即时通讯），仅仅通过邮件是不够的。 参与代码复审（code review），并适当辅助开发人员进行单元测试。 在流程中增加一个环节“产品走查（Product work-through）”——测试人员和产品经理、开发人员等在一起，从头到尾将新功能看一遍，可直观、快速地发现问题。 3. 新功能的测试和回归测试策略 测试任务简单地可分为新功能测试和回归测试。在敏捷方法中，针对这两部分的测试建立相应的策略，以提高测试的效率，最大限度地降低质量风险。新功能测试的策略主要有： 不需要测试用例，直接基于用例、基于对需求的理解来完成新功能的验证。即使要写测试用例，只要保证各个功能点被覆盖，不要过于详细（大颗粒度）。持续地进 行验证，一旦某块新代码完成（code drop）, 就开始验证，而不是等到所有代码完成后才开始测试。这也包括参与到单元测试和集成测试中。实施端到端（end-to-end）的测试，确保完整的业务流程 的实现，同时，也容易发现业务逻辑不够清晰、不够合理等各方面的问题。阅读代码来发现问题，可以和开发人员工作保持同步，消除测试周期的压力。基于经验， 可以实施更多的探索性测试、组合交互性（interoperation）测试和用户场景(user scenario)测试，更有效地发现埋藏较深的缺陷。 回归测试是敏捷测试中需要面对的难点。每次迭代都会增加新的功能，一个产品可能会经过十几次、甚至几十次迭代，回归测试范围在不断增大，而每次迭代周期没 变，可能还是一个月。这样验收测试的时间非常有限，所以回归测试很大程度上依赖于自动化测试，因为很难将回归测试控制在非常有限的范围内。当然，还是有些 办法可以帮助我们减少回归测试的范围，例如： 通过执行code diff来了解代码变动的所有地方，再做代码关联分析，就可以明确知道要进行哪些地方的回归测试，回归测试范围会大大缩小。基于风险和操作面分析来减少回 归测试的范围，例如回归测试只是保证主要功能点没有问题，而忽视一些细节的问题。持续测试的过程，只要有时间，就进行测试，包括开发人员、产品设计人员都 参与到日常的试用和测试中来 [...]]]></description>
			<content:encoded><![CDATA[<p>有一次，当开发人员完成当前Sprint  任务的代码之后，测试人员与开发人员、产品经理一起来浏览产品、从头到尾走一边，产品经理发现了问题，认为需要对功能进行比较大的修改。这时开发人员估计 需要两天时间才能完成代码，但测试人员反对这样做，我们本来只有5天测试时间，加上这次新做的功能比较多、开发代码质量不高，验收测试已经很紧张。如果再 延迟两天，测试没法完成。产品经理说，你们不是在用敏捷测试方法，应该测得很快，三天应该能完成测试工作啊！</p>
<p>什么是敏捷测试呢？敏捷测试当然不能简单地理解测得更快，绝对不是比以前用更少时间进行测试，也不是将测试的范围缩小了或将质量降低来减少测试任务。也有人说，只有敏捷开发，没有敏捷测试。<br />
<span id="more-901"></span><br />
<strong>1.下面我们就要讨论一下</strong></p>
<div>
<p>1.1究竟什么是敏捷测试？</p>
<p>2.2敏捷测试有哪些流程改进？</p>
<p>3.3测试人员如何面对敏捷测试的挑战？</p>
<p>4.4在敏捷测试中如何制定相应的自动化测试策略？</p>
</div>
<p>等等各种问题。 1. 什么是敏捷测试  假如将过去传统的测试流程和方法硬塞入敏捷开发流程中，测试工作可能会事倍功半，测试人员可能会天天加班，而不能发挥应用的作用。敏捷测试应该是适应敏捷 方法而采用的新的测试流程、方法和实践，对传统的测试流程有所剪裁，有所不同的侧重，例如减少测试计划、测试用例设计等工作的比重，增加与产品设计人员、 开发人员的交流和协作。在敏捷测试流程中，参与单元测试，关注持续迭代的新功能，针对这些新功能进行足够的验收测试，而对原有功能的回归测试则依赖于自动 化测试。由于敏捷方法中迭代周期短，测试人员尽早开始测试，包括及时对需求、开发设计的评审，更重要的是能够及时、持续的对软件产品质量进行反馈。简单地 说，敏捷测试就是持续地对软件质量问题进行及时地反馈，如图1所示。</p>
<p><img src="http://www.uml.org.cn/SoftWareProcess/images/0_1292419807EF9C.gif" alt="" width="666" height="225" /><br />
<strong>2. 敏捷测试流程的优化</strong><br />
在敏捷方法中，需求变化比较快、产品开发周期很短，我们目前采用四周时间，也就是每个月发布一个新版本。开发周期短，功能不断累加，给软件测试带来很大 的挑战，软件测试流程要做相应的调整。例如，我们原有的测试规范明确规定，首先要建立项目的主测试计划书，然后再建立每个功能任务的测试计划书，测试计划 书有严格的模板，而且需要和产品经理、开发人员讨论，并和测试团队其他人员（包括测试经理）讨论，最终得到大家的认可和签字才能通过，仅测试计划经过“起 草、评审和签发”一个完整的周期就需要一个月。在敏捷方法中，不再要求写几十页的测试计划书，而是在每个迭代周期，写出一页纸的测试计划，将测试要点（包 括策略、特定方法、重点范围等）列出来就可以了。</p>
<p>在原有测试规范中，要求先用Excel写出测试用例，然后进行讨论、评审，评审通过以 后再导入测试用例库（在线管理系统）中。在敏捷测试中，可能不需要测试用例，而是针对use                            case 或user  story直接进行验证，并进行探索性测试。而节约出来的时间，用于开发原有功能的自动化测试脚本，为回归测试服务。自动化测试脚本将代替测试用例，成为 软件组织的财富。原有测试规范还要求进行两轮回归测试，在敏捷测试中，只能进行一轮回归测试。综合这些考虑，敏捷测试的流程简单有效，如下图2所示。</p>
<p><img src="http://www.uml.org.cn/SoftWareProcess/images/214vadt45.jpg" alt="" width="662" height="303" /></p>
<p>在敏捷测试流程中，如前所述，测试是一个持续的质量反馈过程，测试中发现的问题及时反馈给产品经理和开发人员，而且某些关键方面也要得到我们足够的关注，主要有：<br />
l 测试人员不仅要全程参与需求、产品功能设计等讨论，而且要面对面地、充分地讨论（包括带语言、视频的即时通讯），仅仅通过邮件是不够的。</p>
<p>参与代码复审（code review），并适当辅助开发人员进行单元测试。</p>
<p>在流程中增加一个环节“产品走查（Product work-through）”——测试人员和产品经理、开发人员等在一起，从头到尾将新功能看一遍，可直观、快速地发现问题。</p>
<p><strong>3. 新功能的测试和回归测试策略</strong><br />
测试任务简单地可分为新功能测试和回归测试。在敏捷方法中，针对这两部分的测试建立相应的策略，以提高测试的效率，最大限度地降低质量风险。新功能测试的策略主要有：</p>
<p>不需要测试用例，直接基于用例、基于对需求的理解来完成新功能的验证。即使要写测试用例，只要保证各个功能点被覆盖，不要过于详细（大颗粒度）。持续地进 行验证，一旦某块新代码完成（code                            drop）,  就开始验证，而不是等到所有代码完成后才开始测试。这也包括参与到单元测试和集成测试中。实施端到端（end-to-end）的测试，确保完整的业务流程 的实现，同时，也容易发现业务逻辑不够清晰、不够合理等各方面的问题。阅读代码来发现问题，可以和开发人员工作保持同步，消除测试周期的压力。基于经验， 可以实施更多的探索性测试、组合交互性（interoperation）测试和用户场景(user                            scenario)测试，更有效地发现埋藏较深的缺陷。</p>
<p>回归测试是敏捷测试中需要面对的难点。每次迭代都会增加新的功能，一个产品可能会经过十几次、甚至几十次迭代，回归测试范围在不断增大，而每次迭代周期没 变，可能还是一个月。这样验收测试的时间非常有限，所以回归测试很大程度上依赖于自动化测试，因为很难将回归测试控制在非常有限的范围内。当然，还是有些 办法可以帮助我们减少回归测试的范围，例如：</p>
<p>通过执行code  diff来了解代码变动的所有地方，再做代码关联分析，就可以明确知道要进行哪些地方的回归测试，回归测试范围会大大缩小。基于风险和操作面分析来减少回 归测试的范围，例如回归测试只是保证主要功能点没有问题，而忽视一些细节的问题。持续测试的过程，只要有时间，就进行测试，包括开发人员、产品设计人员都 参与到日常的试用和测试中来</p>
<p><strong>4 自动化测试策略</strong><br />
由于开发周期短，需求、设计等方面沟通也需要花费很多时间，没有足够时间开发自动化测试脚本，至少对新功能的测试很难实现自动化测试。这时候，就需要正确 的策略来提高自动化测试的效益，如图30-3所示，并说明如下</p>
<p><img src="http://www.uml.org.cn/SoftWareProcess/images/0_1293337333ZyNn.gif" alt="" width="629" height="251" /></p>
<p><strong>图3 自动化测试的策略</strong></p>
<p>4.1构建一个灵活的、开放的自动化测试框架，如基于关键字驱动的自动化框架，使测试脚本的开发简单易行，脚本维护也方便。</p>
<p>4.2针对稳定的产品特性开发自动化测试脚本，也就是针对前期完成的已有功能开发自动化测试的脚本，而大部分新功能测试采用手工测试。</p>
<p>4.3集中精力在单元层次上实现自动化测试，主要由开发人员实施，测试人员提供单元测试框架，并辅助完成一些所需的基础工作。</p>
<p>4.4在产品设计、编程时就很好地考虑了自动化测试的需求，使全面的、自动化的底层测试、接口测试成为可能，尽量避免用户界面（UI）的自动化测试。</p>
<p>4.5良好的IT基础设施，包括自动化构建软件包、自动化版本验证（BVT）、自动化部署、覆盖率自动产生等。</p>
<p><strong>5 敏捷测试工具</strong><br />
自动化测试依赖于测试工具，所幸的是，目前已有很多敏捷测试工具。由于篇幅所限，这里只是简单地列出一些常用的敏捷测试工具，不再深入讨论了。</p>
<p>5.1 单元测试工具：TestNG、xUnit家族（如JUnit 、NUnit）、JMock、BizMock等。</p>
<p>5.2 功能测试自动化：ThoughtWorks Twist。</p>
<p>5.3 Web功能测试（frontend）：Selenium IDE/RC、WatiR、WatiN。</p>
<p>5.4 Web service测试工具（backend）：soapUI 。</p>
<p>5.5 性能测试：JMeter+BadBoy。</p>
<p>5.6 验收测试框架：Fitnesse、Tellurium。</p>
<p>5.7 敏捷测试过程管理工具：微软的Visual Studio 2010，包括TFS 2010、Scrum模板（MS                            VS Scrum 1.0）、Test Manager 2010、Coded UI Test等。</p>
<p>5.8 业务智能（BI）应用的测试框架：Oraylis BI.Quality (+ NUnit)。</p>
<p>5.9 其它一些协作工具等，如TestLink、BugZilla、BugFree、wiki、ant等。</p>
<p><strong>6 测试人员在敏捷方法中的价值</strong><br />
在敏捷方法中，开发人员的主导作用更明显，系统设计、编程实现、单元测试、重构等看似关键的一些任务都落在开发人员身上，测试人员容易被边缘化。那么，在敏捷方法中，测试人员的价值又如何体现呢？</p>
<p>6.1 在需求和功能设计讨论上，测试人员可以站在客户角度上来阐述自己的观点，扮演“用户代表”角色，强调用户体验，真正体现测试人员和开发人员的互补作用。</p>
<p>6.2测试人员不仅扮演“用户代表”角色，而且通过需求讨论、代码复审等各种活动及时地提供质量反馈，包括代码质量、接口一致性等，保证在产品构造的整个过程中质量受到足够的关注，以提高质量改进的持续性和可视性。</p>
<p>6.3 测试人员应积极参与单元测试，即使不参加单元测试，也应督促开发人员进行单元测，确保单元测试达到80%以上覆盖率，确保开发出具有良好可测试性的代码。</p>
<p>6.4在敏捷方法中，往往将一个大的系统开发分解成多个小的子系统（模块或组件），集成测试和端到端（end-to-end）测试显得更为重要，测试人员在这些测试上能发挥更大的作用。</p>
<p>6.5 产品发布前，验收测试和回归测试依然不可缺少，这更是测试人员的用武之地。</p>
<p>6.6 一个迭代周期结束后，对缺陷根本原因进行分析、总结规律，帮助开发人员建立良好的习惯，预防缺陷，从根本上提高产品质量。</p>
<p>理想情况下，测试人员掌握设计模式、具有很好的编程能力，可以和开发人员进行角色互换，如在当前版本开发（/迭代周期）中担任测试人员角色，在下一个版本 开发（/迭代周期）中则担任开发人员角色。这样双方对不同角色的工作有着更深刻的认识，消除沟通的障碍，开发的效率和质量会有进一步的提高。</p>
<p><strong>小结</strong><br />
根据上面的讨论和我们的实践，最后针对敏捷测试进行一个简单的总结，就是：<br />
l 敏捷测试就是持续测试、持续反馈，扮演“用户代表”角色，确保产品满足客户的需求。</p>
<p>敏捷功能测试 = 新特性的手工测试 (use case验证和探索性测试) + 原有功能的自动化测试                            (回归测试)</p>
<p>敏捷测试人员和开发人员的区别越来越小，理想情况下，敏捷方法中，测试人员和开发人员在不同的迭代周期可以互换。</p>
<p>敏捷测试流程依据不同的团队特点、不同产品的特点而不同，因地制宜，适合才是最好。</p>
]]></content:encoded>
			<wfw:commentRss>http://yulans.cn/%e5%bc%80%e5%8f%91%e6%96%b9%e6%b3%95/%e6%95%8f%e6%8d%b7%e6%b5%8b%e8%af%95%e7%9a%84%e6%96%b9%e6%b3%95%e5%92%8c%e5%ae%9e%e8%b7%b5/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>软件架构模式的种类</title>
		<link>http://yulans.cn/%e6%9e%b6%e6%9e%84/%e8%bd%af%e4%bb%b6%e6%9e%b6%e6%9e%84%e6%a8%a1%e5%bc%8f%e7%9a%84%e7%a7%8d%e7%b1%bb</link>
		<comments>http://yulans.cn/%e6%9e%b6%e6%9e%84/%e8%bd%af%e4%bb%b6%e6%9e%b6%e6%9e%84%e6%a8%a1%e5%bc%8f%e7%9a%84%e7%a7%8d%e7%b1%bb#comments</comments>
		<pubDate>Sat, 12 Nov 2011 01:40:07 +0000</pubDate>
		<dc:creator>cmpan</dc:creator>
				<category><![CDATA[架构]]></category>

		<guid isPermaLink="false">http://yulans.cn/?p=897</guid>
		<description><![CDATA[在做软件架构设计时,根据不同的抽象层次可分为三种不同层次的模式：架构模式(Architectural Pattern)、设计模式(Design Pattern)、代码模式(Coding Pattern)。 架构模式是一个系统的高层次策略，涉及到大尺度的组件以及整体性质和力学。架构模式的好坏可以影响到总体布局和框架性结构。 设计模式是中等尺度的结构策略。这些中等尺度的结构实现了一些大尺度组件的行为和它们之间的关系。模式的好坏不会影响到系统的总体布局和总体框架。设计模式定义出子系统或组件的微观结构。 代码模式（或成例）是特定的范例和与特定语言有关的编程技巧。代码模式的好坏会影响到一个中等尺度组件的内部、外部的结构或行为的底层细节，但不会影响到一个部件或子系统的中等尺度的结构，更不会影响到系统的总体布局和大尺度框架。 架构模式(Architectural Pattern) 一个架构模式描述软件系统里的基本的结构组织或纲要。架构模式提供一些事先定义好的子系统，指定它们的责任，并给出把它们组织在一起的法则和指南。称之为系统模式。 •MVC模式,一个架构模式常常可以分解成很多个设计模式的联合使用。MVC模式常常包括调停者（Mediator）模式、策略（Strategy）模式、合成（Composite）模式、观察者（Observer）模式等。 •Layers（分层）模式，有时也称Tiers模式 •Blackboard（黑板）模式 •Broker（中介）模式 •Distributed Process（分散过程）模式 •Microkernel（微核）模式 架构模式常常划分成如下的几种： 一、 模块结构(From Mud to Structure)型。帮助架构师将系统合理划分，避免形成一个对象的海洋。包括Layers（分层）模式、Blackboard（黑板）模式、Pipes/Filters（管道/过滤器）模式等。 二、分散系统（Distributed Systems）型。为分散式系统提供完整的架构设计，包括像Broker（中介）模式等。 三、人机互动（Interactive Systems）型，支持包含有人机互动介面的系统的架构设计，例子包括MVC（Model-View-Controller）模式、PAC（Presentation-Abstraction-Control）模式等。 四、Adaptable Systems型，支持应用系统适应技术的变化、软件功能需求的变化。如Reflection（反射）模式、Microkernel（微核）模式等。 设计模式(Design Pattern) 一个设计模式提供一种提炼子系统或软件系统中的组件的，或者它们之间的关系的纲要设计。设计模式描述普遍存在的在相互通讯的组件中重复出现的结构，这种结构解决在一定的背景中的具有一般性的设计问题。 设计模式常常划分成不同的种类，常见的种类有： 创建型设计模式，如工厂方法（Factory Method）模式、抽象工厂（Abstract Factory）模式、原型（Prototype）模式、单例（Singleton）模式，建造（Builder）模式等 结构型设计模式，如合成（Composite）模式、装饰（Decorator）模式、代理（Proxy）模式、享元（Flyweight）模式、门面（Facade）模式、桥梁（Bridge）模式等 行为型模式，如模版方法（Template Method）模式、观察者（Observer）模式、迭代子（Iterator）模式、责任链（Chain of Responsibility）模式、备忘录（Memento）模式、命令（Command）模式、状态（State）模式、访问者（Visitor）模式等等。 以上是三种经典类型，实际上还有很多其他的类型，比如Fundamental型、Partition型，Relation型等等。设计模式在特定的编程语言中实现的时候，常常会用到代码模式。比如单例（Singleton）模式的实现常常涉及到双检锁（Double-Check Locking）模式等。 代码模式(Coding Pattern) 代码模式（或成例）是较低层次的模式，并与编程语言密切相关。代码模式描述怎样利用一个特定的编程语言的特点来实现一个组件的某些特定的方面或关系。 较为著名的代码模式的例子包括双检锁（Double-Check Locking）模式等]]></description>
			<content:encoded><![CDATA[<p>      在做软件架构设计时,根据不同的抽象层次可分为三种不同层次的模式：<strong>架构模式</strong>(Architectural Pattern)、<strong>设计模式</strong>(Design Pattern)、<strong>代码模式</strong>(Coding Pattern)。</p>
<p>       <strong> 架构模式</strong>是一个系统的高层次策略，涉及到大尺度的组件以及整体性质和力学。架构模式的好坏可以影响到总体布局和框架性结构。</p>
<p>        <strong>设计模式</strong>是中等尺度的结构策略。这些中等尺度的结构实现了一些大尺度组件的行为和它们之间的关系。模式的好坏不会影响到系统的总体布局和总体框架。设计模式定义出子系统或组件的微观结构。</p>
<p>        <strong>代码模式</strong>（或成例）是特定的范例和与特定语言有关的编程技巧。代码模式的好坏会影响到一个中等尺度组件的内部、外部的结构或行为的底层细节，但不会影响到一个部件或子系统的中等尺度的结构，更不会影响到系统的总体布局和大尺度框架。<br />
<span id="more-897"></span><br />
<strong>架构模式(Architectural Pattern)</strong></p>
<p>一个架构模式描述软件系统里的基本的结构组织或纲要。架构模式提供一些事先定义好的子系统，指定它们的责任，并给出把它们组织在一起的法则和指南。称之为系统模式。</p>
<p>•MVC模式,一个架构模式常常可以分解成很多个设计模式的联合使用。MVC模式常常包括调停者（Mediator）模式、策略（Strategy）模式、合成（Composite）模式、观察者（Observer）模式等。</p>
<p>•Layers（分层）模式，有时也称Tiers模式</p>
<p>•Blackboard（黑板）模式</p>
<p>•Broker（中介）模式</p>
<p>•Distributed Process（分散过程）模式</p>
<p>•Microkernel（微核）模式</p>
<p>架构模式常常划分成如下的几种：</p>
<p>一、 模块结构(From Mud to Structure)型。帮助架构师将系统合理划分，避免形成一个对象的海洋。包括Layers（分层）模式、Blackboard（黑板）模式、Pipes/Filters（管道/过滤器）模式等。</p>
<p>二、分散系统（Distributed Systems）型。为分散式系统提供完整的架构设计，包括像Broker（中介）模式等。</p>
<p>三、人机互动（Interactive Systems）型，支持包含有人机互动介面的系统的架构设计，例子包括MVC（Model-View-Controller）模式、PAC（Presentation-Abstraction-Control）模式等。</p>
<p>四、Adaptable Systems型，支持应用系统适应技术的变化、软件功能需求的变化。如Reflection（反射）模式、Microkernel（微核）模式等。</p>
<p><strong>设计模式(Design Pattern)</strong></p>
<p>一个设计模式提供一种提炼子系统或软件系统中的组件的，或者它们之间的关系的纲要设计。设计模式描述普遍存在的在相互通讯的组件中重复出现的结构，这种结构解决在一定的背景中的具有一般性的设计问题。</p>
<p>设计模式常常划分成不同的种类，常见的种类有：</p>
<p>创建型设计模式，如工厂方法（Factory Method）模式、抽象工厂（Abstract Factory）模式、原型（Prototype）模式、单例（Singleton）模式，建造（Builder）模式等</p>
<p>结构型设计模式，如合成（Composite）模式、装饰（Decorator）模式、代理（Proxy）模式、享元（Flyweight）模式、门面（Facade）模式、桥梁（Bridge）模式等</p>
<p>行为型模式，如模版方法（Template Method）模式、观察者（Observer）模式、迭代子（Iterator）模式、责任链（Chain of Responsibility）模式、备忘录（Memento）模式、命令（Command）模式、状态（State）模式、访问者（Visitor）模式等等。</p>
<p>以上是三种经典类型，实际上还有很多其他的类型，比如Fundamental型、Partition型，Relation型等等。设计模式在特定的编程语言中实现的时候，常常会用到代码模式。比如单例（Singleton）模式的实现常常涉及到双检锁（Double-Check Locking）模式等。</p>
<p><strong>代码模式(Coding Pattern)</strong></p>
<p>代码模式（或成例）是较低层次的模式，并与编程语言密切相关。代码模式描述怎样利用一个特定的编程语言的特点来实现一个组件的某些特定的方面或关系。</p>
<p>较为著名的代码模式的例子包括双检锁（Double-Check Locking）模式等</p>
]]></content:encoded>
			<wfw:commentRss>http://yulans.cn/%e6%9e%b6%e6%9e%84/%e8%bd%af%e4%bb%b6%e6%9e%b6%e6%9e%84%e6%a8%a1%e5%bc%8f%e7%9a%84%e7%a7%8d%e7%b1%bb/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>用Flash理跨域上传或异步请求不能传Cookie的解决方案</title>
		<link>http://yulans.cn/php/%e7%94%a8flash%e7%90%86%e8%b7%a8%e5%9f%9f%e4%b8%8a%e4%bc%a0%e6%88%96%e5%bc%82%e6%ad%a5%e8%af%b7%e6%b1%82%e4%b8%8d%e8%83%bd%e4%bc%a0cookie%e7%9a%84%e8%a7%a3%e5%86%b3%e6%96%b9%e6%a1%88</link>
		<comments>http://yulans.cn/php/%e7%94%a8flash%e7%90%86%e8%b7%a8%e5%9f%9f%e4%b8%8a%e4%bc%a0%e6%88%96%e5%bc%82%e6%ad%a5%e8%af%b7%e6%b1%82%e4%b8%8d%e8%83%bd%e4%bc%a0cookie%e7%9a%84%e8%a7%a3%e5%86%b3%e6%96%b9%e6%a1%88#comments</comments>
		<pubDate>Thu, 10 Nov 2011 19:09:00 +0000</pubDate>
		<dc:creator>cmpan</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[实践经验]]></category>

		<guid isPermaLink="false">http://yulans.cn/?p=894</guid>
		<description><![CDATA[用flash上传或flash做代理异步请求的时候，因为flash不能直接传递浏览器中的cookie到服务器，引起SESSION无法识别身份。想当年刚碰到这个问题的时候会非常头痛。其实在PHP里面，解决时很容易的。 客户端：把cookie变量添加到URL 1234567&#160; &#160; var url = 'http://www.vkas.net/index.php?user/account/getInfo'; &#160; &#160; if&#40;document.cookie.length &#62; 0&#41; &#123; &#160; &#160; &#160; &#160; var cookies = document.cookie.split&#40;';'&#41;; &#160; &#160; &#160; &#160; for&#40;var i in cookies&#41; &#123; &#160; &#160; &#160; &#160; &#160; &#160; url += '&#38;' + encodeURI&#40;cookies&#91;i&#93;.replace&#40;/^\s+&#124;\s+$/g, ''&#41;&#41;; &#160; &#160; &#160; &#160; &#125; &#160; &#160; &#125; 服务器端：把URL中传的cookie.session.name设置到session id。这个过程必须在session_start()之前做。 1234&#160; &#160; [...]]]></description>
			<content:encoded><![CDATA[<p>用flash上传或flash做代理异步请求的时候，因为flash不能直接传递浏览器中的cookie到服务器，引起SESSION无法识别身份。想当年刚碰到这个问题的时候会非常头痛。其实在PHP里面，解决时很容易的。</p>
<p>客户端：把cookie变量添加到URL</p>
<div class="codecolorer-container javascript mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:680px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br /></div></td><td><div class="javascript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; <span style="color: #003366; font-weight: bold;">var</span> url <span style="color: #339933;">=</span> <span style="color: #3366CC;">'http://www.vkas.net/index.php?user/account/getInfo'</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span>document.<span style="color: #660066;">cookie</span>.<span style="color: #660066;">length</span> <span style="color: #339933;">&gt;</span> <span style="color: #CC0000;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #003366; font-weight: bold;">var</span> cookies <span style="color: #339933;">=</span> document.<span style="color: #660066;">cookie</span>.<span style="color: #660066;">split</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">';'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">for</span><span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">var</span> i <span style="color: #000066; font-weight: bold;">in</span> cookies<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; url <span style="color: #339933;">+=</span> <span style="color: #3366CC;">'&amp;'</span> <span style="color: #339933;">+</span> <span style="color: #000066; font-weight: bold;">encodeURI</span><span style="color: #009900;">&#40;</span>cookies<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span>.<span style="color: #660066;">replace</span><span style="color: #009900;">&#40;</span><span style="color: #009966; font-style: italic;">/^\s+|\s+$/g</span><span style="color: #339933;">,</span> <span style="color: #3366CC;">''</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span></div></td></tr></tbody></table></div>
<p>服务器端：把URL中传的cookie.session.name设置到session id。这个过程必须在session_start()之前做。</p>
<div class="codecolorer-container php mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:680px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="php codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; <span style="color: #666666; font-style: italic;">// 解决Flash不能传Cookie</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span><span style="color: #990000;">empty</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$_GET</span><span style="color: #009900;">&#91;</span><span style="color: #990000;">session_name</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #990000;">session_id</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$_GET</span><span style="color: #009900;">&#91;</span><span style="color: #990000;">session_name</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span></div></td></tr></tbody></table></div>
]]></content:encoded>
			<wfw:commentRss>http://yulans.cn/php/%e7%94%a8flash%e7%90%86%e8%b7%a8%e5%9f%9f%e4%b8%8a%e4%bc%a0%e6%88%96%e5%bc%82%e6%ad%a5%e8%af%b7%e6%b1%82%e4%b8%8d%e8%83%bd%e4%bc%a0cookie%e7%9a%84%e8%a7%a3%e5%86%b3%e6%96%b9%e6%a1%88/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>一种以ID特征为依据的数据分片（Sharding）策略</title>
		<link>http://yulans.cn/uncategorized/%e4%b8%80%e7%a7%8d%e4%bb%a5id%e7%89%b9%e5%be%81%e4%b8%ba%e4%be%9d%e6%8d%ae%e7%9a%84%e6%95%b0%e6%8d%ae%e5%88%86%e7%89%87%ef%bc%88sharding%ef%bc%89%e7%ad%96%e7%95%a5</link>
		<comments>http://yulans.cn/uncategorized/%e4%b8%80%e7%a7%8d%e4%bb%a5id%e7%89%b9%e5%be%81%e4%b8%ba%e4%be%9d%e6%8d%ae%e7%9a%84%e6%95%b0%e6%8d%ae%e5%88%86%e7%89%87%ef%bc%88sharding%ef%bc%89%e7%ad%96%e7%95%a5#comments</comments>
		<pubDate>Mon, 31 Oct 2011 16:21:23 +0000</pubDate>
		<dc:creator>cmpan</dc:creator>
				<category><![CDATA[未分类]]></category>

		<guid isPermaLink="false">http://yulans.cn/?p=889</guid>
		<description><![CDATA[假如您有一个应用程序，随着业务越来越有起色，系统所牵涉到的数据量也就越来越大，此时您要涉及到对系统进行伸缩（Scale）的 问题了。一种典型的扩展方法叫做“向上伸缩（Scale Up）”，它的意思是通过使用更好的硬件来提高系统的性能参数。而另一种方法则叫做“向外伸缩（Scale Out）”，它是指通过增加额外的硬件（如服务器）来达到相同的效果。从“硬件成本”还是“系统极限”的角度来说，“向外伸缩”一般都会优于“向上伸 缩”，因此大部分上规模的系统都会在一定程度上考虑“向外”的方式。由于许多系统的瓶颈都处在数据存储上，因此一种叫做“数据分片（Database Sharding）”的数据架构方式应运而生，本文便会讨论这种数据架构方式的一种比较典型的实现方式。 简介 数据分片，自然便是将整体数据分摊在多个存储设备（下文统称为“数据分区”或“分区”）上，这样每个存储设备的数据量相对就会小很多，以此满足系统的性能需求。值得注意的是，系统分片的策略有很多，例如常见的有以下几种： 根据ID特征：例如对记录的ID取模，得到的结果是几，那么这条记录就放在编号为几的数据分区上。 根据时间范围：例如前100万个用户数据在第1个分区中，第二个100万用户数据放在第2个分区中。 基于检索表：根据ID先去一个表内找到它所在的分区，然后再去目标分区进行查找。 …… 在这些数据分片策略之中没有哪个有绝对的优势，选择哪种策略完全是根据系统的业务或是数据特征来确定的。值得强调的是：数据分片不是银弹，它对系统的性能和伸缩性（Scalability）带 来一定好处的同时，也会对系统开发带来许多复杂度。例如，有两条记录分别处在不同的服务器上，那么如果有一个业务是为它们建立一个“关联”，那么很可能表 示“关联”的记录就必须在两个分区内各放一条。另外，如果您重视数据的完整性，那么跨数据分区的事务又立即变成了性能杀手。最后，如果有一些需要进行全局 查找的业务，光有数据分片策略也很难对系统性能带来什么优势。 数据分片虽然重要，但在使用之前一定要三思而后行。一旦踏上这艘贼船，往往不成功便成仁，很难回头。在我的经验里，一个滥用数据分片策略而事倍功半的项目给我留下了非常深刻的印象（当然也有成功的啦），因此目前我对待数据分片策略变得愈发谨慎。 那么现在，我们便来讨论一种比较常见的数据分片策略。 策略描述 这里我先描述一个极其简单的业务： 系统中有用户，用户可以发表文章，文章会有评论 可以根据用户查找文章 可以根据文章查找评论 那么，如果我要对这样一个系统进行数据分片又该怎么做呢？这里我们可以使用上面提到的第一种方式，即对记录的ID取模，并根据结果选择数据所在的分区。根据后两条业务中描述的查询要求，我们会为分区策略补充这样的规则： 某个用户的所有文章，与这个用户处在同一数据分区内。 某篇文章的所有评论，与这篇文章处在用一数据分区内。 您可能会说，似乎只要保证“相同用户文章在同一个数据分区内”就行了，不是吗？没错，不过我这里让文章和用户在同一个分区内，也是为了方便许多额外的操作（例如在关系数据库中进行连接）。那么假设我们有4个数据分区，那么它们内部的条目可能便是： 分区0 分区1 User 4 Article 8 Article 12 Comment 4 Comment 16 User 12 Article 4 User 1 Article 5 Article 9 Comment 13 Comment 17 User 5 [...]]]></description>
			<content:encoded><![CDATA[<div>
<p>假如您有一个应用程序，随着业务越来越有起色，系统所牵涉到的数据量也就越来越大，此时您要涉及到对系统进行伸缩（Scale）的 问题了。一种典型的扩展方法叫做“向上伸缩（Scale  Up）”，它的意思是通过使用更好的硬件来提高系统的性能参数。而另一种方法则叫做“向外伸缩（Scale  Out）”，它是指通过增加额外的硬件（如服务器）来达到相同的效果。从“硬件成本”还是“系统极限”的角度来说，“向外伸缩”一般都会优于“向上伸 缩”，因此大部分上规模的系统都会在一定程度上考虑“向外”的方式。由于许多系统的瓶颈都处在数据存储上，因此一种叫做“<a href="http://en.wikipedia.org/wiki/Shard_%28database_architecture%29">数据分片（Database Sharding）</a>”的数据架构方式应运而生，本文便会讨论这种数据架构方式的一种比较典型的实现方式。<br />
<span id="more-889"></span></p>
<h1>简介</h1>
<p>数据分片，自然便是将整体数据分摊在多个存储设备（下文统称为“数据分区”或“分区”）上，这样每个存储设备的数据量相对就会小很多，以此满足系统的性能需求。值得注意的是，系统分片的策略有很多，例如常见的有以下几种：</p>
<ul>
<li>根据ID特征：例如对记录的ID取模，得到的结果是几，那么这条记录就放在编号为几的数据分区上。</li>
<li>根据时间范围：例如前100万个用户数据在第1个分区中，第二个100万用户数据放在第2个分区中。</li>
<li>基于检索表：根据ID先去一个表内找到它所在的分区，然后再去目标分区进行查找。</li>
<li>……</li>
</ul>
<p>在这些数据分片策略之中没有哪个有绝对的优势，选择哪种策略完全是根据系统的业务或是数据特征来确定的。值得强调的是：数据分片不是银弹，它对系统的性能和<a href="http://en.wikipedia.org/wiki/Scalability">伸缩性（Scalability）</a>带 来一定好处的同时，也会对系统开发带来许多复杂度。例如，有两条记录分别处在不同的服务器上，那么如果有一个业务是为它们建立一个“关联”，那么很可能表 示“关联”的记录就必须在两个分区内各放一条。另外，如果您重视数据的完整性，那么跨数据分区的事务又立即变成了性能杀手。最后，如果有一些需要进行全局 查找的业务，光有数据分片策略也很难对系统性能带来什么优势。</p>
<p>数据分片虽然重要，但在使用之前一定要三思而后行。一旦踏上这艘贼船，往往不成功便成仁，很难回头。在我的经验里，一个滥用数据分片策略而事倍功半的项目给我留下了非常深刻的印象（当然也有成功的啦），因此目前我对待数据分片策略变得愈发谨慎。</p>
<p>那么现在，我们便来讨论一种比较常见的数据分片策略。</p>
<h1>策略描述</h1>
<p>这里我先描述一个极其简单的业务：</p>
<ol>
<li>系统中有用户，用户可以发表文章，文章会有评论</li>
<li>可以根据用户查找文章</li>
<li>可以根据文章查找评论</li>
</ol>
<p>那么，如果我要对这样一个系统进行数据分片又该怎么做呢？这里我们可以使用上面提到的第一种方式，即对记录的ID取模，并根据结果选择数据所在的分区。根据后两条业务中描述的查询要求，我们会为分区策略补充这样的规则：</p>
<ul>
<li>某个用户的所有文章，与这个用户处在同一数据分区内。</li>
<li>某篇文章的所有评论，与这篇文章处在用一数据分区内。</li>
</ul>
<p>您可能会说，似乎只要保证“相同用户文章在同一个数据分区内”就行了，不是吗？没错，不过我这里让文章和用户在同一个分区内，也是为了方便许多额外的操作（例如在关系数据库中进行连接）。那么假设我们有4个数据分区，那么它们内部的条目可能便是：</p>
<table border="1" cellspacing="0" cellpadding="5">
<tbody>
<tr>
<td>分区0</td>
<td>分区1</td>
</tr>
<tr>
<td>
<ul>
<li>User 4
<ul>
<li>Article 8</li>
<li>Article 12
<ul>
<li>Comment 4</li>
<li>Comment 16</li>
</ul>
</li>
</ul>
</li>
<li>User 12
<ul>
<li>Article 4</li>
</ul>
</li>
</ul>
</td>
<td>
<ul>
<li>User 1
<ul>
<li>Article 5</li>
<li>Article 9
<ul>
<li>Comment 13</li>
<li>Comment 17</li>
</ul>
</li>
</ul>
</li>
<li>User 5
<ul>
<li>Article 13</li>
</ul>
</li>
</ul>
</td>
</tr>
<tr>
<td>分区2</td>
<td>分区3</td>
</tr>
<tr>
<td>
<ul>
<li>User 2
<ul>
<li>Article 10</li>
<li>Article 14
<ul>
<li>Comment 6</li>
<li>Comment 10</li>
</ul>
</li>
</ul>
</li>
<li>User 10
<ul>
<li>Article 4</li>
</ul>
</li>
</ul>
</td>
<td>
<ul>
<li>User 7
<ul>
<li>Article 7</li>
<li>Article 11
<ul>
<li>Comment 3</li>
<li>Comment 15</li>
</ul>
</li>
</ul>
</li>
<li>User 11
<ul>
<li>Article 4</li>
</ul>
</li>
</ul>
</td>
</tr>
</tbody>
</table>
<p>在 ID为0的分区中，所有对象的ID模4均为0，其他分区里的对象也有这样的规律。那么好，在实际应用中，如果我们需要查找“ID为2的用户”，便去第2分 区搜索便是；如果要查找“ID为8的文章的所有评论”那么也只要去第0分区进行一次查询即可。既然查询不成问题，那么我们该如何添加新记录呢？其实这也不 难，只要：</p>
<ul>
<li>添加新用户时，随机选择一个数据分区</li>
<li>添加新文章时，选择文章作者所在分区（可根据Article的UserID求模得到）</li>
<li>添加新评论时，选择文章所在分区（可根据Comment的ArticleID求模得到）</li>
</ul>
<p>但 是，我们又如何保证新纪录的ID正好满足我们的分区规律？例如我们向第3分区添加的新数据，则它的ID必须是3、7、11等等。以前，我们可能会使用数据 库的自增列作为ID的值，但这似乎不能满足我们“取模”的要求。以前我们可能还会使用GUID，但是我们如何生成一个“被4模于3”的GUID呢？其实我 们还是可以使用自增ID来解决这个问题，只不过需要进行一些简单的设置。例如在SQL Server中，默认的自增ID属性为IDENTITY(1,  1)，表示ID从1开始，以1为间距自动增长。于是我们在创建数据分区的时候，每个自增列的属性则可以设置为：</p>
<ul>
<li>分区0：IDENTITY(4, 4)</li>
<li>分区1：IDENTITY(1, 4)</li>
<li>分区2：IDENTITY(2, 4)</li>
<li>分区3：IDENTITY(3, 4)</li>
</ul>
<p>这样，ID方面的问题便交由数据库来关心吧，我们的使用方式和以前并没有什么区别。</p>
<h1>缺陷</h1>
<p>那么这个数据分片策略有什么缺陷呢？当然缺陷还是有很多啦，只是大多数问题可能还是要和业务放在一起考虑时才会凸显出来。不过有一个问题倒和业务关系不大：如果数据继续增长，单个数据分区的数据量也超标了，怎么办？</p>
<p>自 然，继续拆分咯。那么我们使用什么分区规则呢？和原先一致吗？我们举个例子便知。假设我们原有4个分区，有一个ID为1的用户落在第1分区里，他的文章也 都在这个分区里，ID分别是1、5、9、13、17等等。于是在某一天，我们需要将分区数量提高到5个（财力有限，一台一台来吧），在重新计算每篇文章所 在的分区之后，我们忽然发现：</p>
<ul>
<li>ID为1的文章，模5余1，处在分区1。</li>
<li>ID为5的文章，模5余0，处在分区0。</li>
<li>ID为9的文章，模5余4，处在分区4。</li>
<li>ID为13的文章，模5余3，处在分区3。</li>
<li>ID为17的文章，模5余2，处在分区2。</li>
</ul>
<p>呼，5 个分区都齐了！这说明，如果我们保持记录原来的ID不变，是没有办法直接使用之前的分区规则——无论您扩展成几个分区，（即便是从4个到8个）也只能“缓 解”也不能“解决”这个情况。那么这时候该如何是好呢？例如，我们可以重新分配记录，改变原有ID，只是这么做会产生一个问题，便是外部URL可能也会随 着ID一起改变，这样对SEO的折损很大。为此，我们可以制作一个查询表：例如在查询小于1234567的ID时（这是“老系统”的最大ID），假设是 100，则根据查询表得知这条记录的新ID为7654321，再以此去数据源进行查找。解决这类问题的方法还有几种，但无论怎么做都会对新系统带来额外的 复杂度。而且，一次扩展也罢，如果以后还要有所扩展呢？</p>
<p><a href="http://img.zhaojie.me/blog/consistent-hashing.png" target="_blank"><img src="http://img.zhaojie.me/blog/consistent-hashing.png" alt="consistent-hahsing" width="250" /></a>有朋友可能会说，取模自然会带来这样的问题，那么为什么不用<a href="http://en.wikipedia.org/wiki/Consistent_hashing">一致性哈希（Consistent Hash）</a>呢？ 现在一致性哈希是个很流行的东西，和Memcached一样，如果不用上就会被一些高级架构师所鄙视。不过在这里一致性哈希也不能解决问题。一致性哈希的 目的，是希望“在增加服务器的时候降低数据移动规模，让尽可能多的数据保留在原有的服务器”上。而我们现在的问题却是“在增加服务器的时候，让特征相同的 数据同样放在一起”。两个目标不同，这并不是一致性哈希的应用场景。</p>
<p>我在以前的一个项目中曾经用过这样的方法：根据对访问量与数据量的 预估，我们认为使用最多24个分区便一定可以满足性能要求（为什么是24个？因为它能被许多数字整除）。于是，从项目第一次在生产环境中部署时便创建了 24个数据分区，只不过一开始只用了2台服务器，每台服务器放置12个数据分区。待以后需要扩展时，则将数据分区均匀地迁移到新的服务器上即可。我们团队 当时便是用这种方法避免尴尬的数据分配问题。</p>
<p>没错，数据分区的数目是个限制，但您真认为，24个数据分区还是无法满足您的项目需求吗？ 要知道，需要用上24个数据分区的项目，一般来说本身已经有充分的时间和经济实力进行架构上的重大调整（也该调整了，几乎没有什么架构可以满足各种数据规 模的需求）。此外，无论是系统优化还是数据分片都可以同时运用其他手段。</p>
<p>不过，我们目前还是想办法解决这个问题吧。</p>
<h1>策略改进</h1>
<p>我们之所以会遇到上面这个问题，在于我们没有选择好合适的策略，这个策略把一些重要的“要求”给“具体化”了，导致“具体化”后的结果在外部条件改变的时候，却无法重新满足原有的“要求”。还是以前面的案例来说明问题，其实我们“要求”其实是：</p>
<ul>
<li>某个用户的所有文章，与这个用户处在同一数据分区内。</li>
<li>某篇文章的所有评论，与这篇文章处在用一数据分区内。</li>
</ul>
<p>而我们“具体化”以后的结果却是：</p>
<ul>
<li>某个用户的所有文章ID，与这个用户的ID模4后的余数相同。</li>
<li>某篇文章的所有评论ID，与这篇文章的ID模4后的余数相同。</li>
</ul>
<p>之 所以能如此“具体化”，这是因为有“4个分区”这样的前提条件在，一旦这个前提条件发生了改变，则杯具无法避免。因此，我们在制定规则的时候，其实不应该 把前提条件给过分的“具体化”——具体化可以，但不能过度，得留有一定空间（这个稍后再谈）。打个比方，还是前面的条件（XX和XX处在同一数据分区 内），但我们换一种具体化的方式：</p>
<ul>
<li>某个用户的所有文章ID的前缀，便是这个用户的ID。例如，ID为1的用户的所有文章，其ID便可能是1-A1、1-A2、1-A3……</li>
<li>某篇文章的所有评论ID，与这个文章的ID使用相同前缀。例如，ID为3-A1的文章的所有评论，其ID便可能是3-C1、3-C2、3-C3……</li>
</ul>
<p>使 用这个策略，我们便可以保证与某个用户相关的“所有数据”都共享相同的“特征”（ID的前缀都相同），然后我们便可以根据这个特征来选择分区——例如，还 是以“取模”的方式。此时，我们已经确保了“相同分区内的所有数据都具备相同的特征”，即便分区数量有所调整，我们也只需要根据特征重新计算分区即可，影 响不大。而以前为什么不行？因为“模4的余数”只是“结果”而不是“特征”，这里的“特征”应该是“追本溯源后的用户ID相同”，而这一点已经体现在新的 策略中了。</p>
<p>还是通过图示来说明问题吧。假设原有4个分区，使用“取模”的策略：</p>
<table border="1" cellspacing="0" cellpadding="5">
<tbody>
<tr>
<td>分区0</td>
<td>分区1</td>
</tr>
<tr>
<td>
<ul>
<li>User 4
<ul>
<li>Article 4-A1</li>
<li>Article 4-A2
<ul>
<li>Comment 4-C1</li>
<li>Comment 4-C2</li>
</ul>
</li>
</ul>
</li>
<li>User 12
<ul>
<li>Article 12-A3</li>
</ul>
</li>
</ul>
</td>
<td>
<ul>
<li>User 1
<ul>
<li>Article 1-A4</li>
<li>Article 1-A5
<ul>
<li>Comment 1-C3</li>
<li>Comment 1-C4</li>
</ul>
</li>
</ul>
</li>
<li>User 5
<ul>
<li>Article 5-A6</li>
</ul>
</li>
</ul>
</td>
</tr>
<tr>
<td>分区2</td>
<td>分区3</td>
</tr>
<tr>
<td>
<ul>
<li>User 2
<ul>
<li>Article 2-A7</li>
<li>Article 2-A8
<ul>
<li>Comment 2-C5</li>
<li>Comment 2-C6</li>
</ul>
</li>
</ul>
</li>
<li>User 10
<ul>
<li>Article 10-A9</li>
</ul>
</li>
</ul>
</td>
<td>
<ul>
<li>User 7
<ul>
<li>Article 7-A10</li>
<li>Article 7-A11
<ul>
<li>Comment 7-C7</li>
<li>Comment 7-C8</li>
</ul>
</li>
</ul>
</li>
<li>User 11
<ul>
<li>Article 11-A12</li>
</ul>
</li>
</ul>
</td>
</tr>
</tbody>
</table>
<p>当分区数量调整为5个之后（为了避免分区3空缺，我又补充了一些对象）：</p>
<table border="1" cellspacing="0" cellpadding="5">
<tbody>
<tr>
<td>分区0</td>
<td>分区1</td>
</tr>
<tr>
<td>
<ul>
<li>User 10
<ul>
<li>Article 10-A9</li>
</ul>
</li>
<li>User 5
<ul>
<li>Article 5-A6</li>
</ul>
</li>
</ul>
</td>
<td>
<ul>
<li>User 1
<ul>
<li>Article 1-A4</li>
<li>Article 1-A5
<ul>
<li>Comment 1-C3</li>
<li>Comment 1-C4</li>
</ul>
</li>
</ul>
</li>
<li>User 11
<ul>
<li>Article 11-A12</li>
</ul>
</li>
</ul>
</td>
</tr>
<tr>
<td>分区2</td>
<td>分区3</td>
</tr>
<tr>
<td>
<ul>
<li>User 2
<ul>
<li>Article 2-A7</li>
<li>Article 2-A8
<ul>
<li>Comment 2-C5</li>
<li>Comment 2-C6</li>
</ul>
</li>
</ul>
</li>
<li>User 12
<ul>
<li>Article 12-A3</li>
</ul>
</li>
<li>User 7
<ul>
<li>Article 7-A10</li>
<li>Article 7-A11
<ul>
<li>Comment 7-C7</li>
<li>Comment 7-C8</li>
</ul>
</li>
</ul>
</li>
</ul>
</td>
<td>
<ul>
<li>User 8
<ul>
<li>Article 8-A12</li>
<li>Article 8-A13
<ul>
<li>Comment 8-C9</li>
<li>Comment 7-C10</li>
</ul>
</li>
</ul>
</li>
</ul>
</td>
</tr>
<tr>
<td>分区4</td>
<td></td>
</tr>
<tr>
<td>
<ul>
<li>User 4
<ul>
<li>Article 4-A1</li>
<li>Article 4-A2
<ul>
<li>Comment 4-C1</li>
<li>Comment 4-C2</li>
</ul>
</li>
</ul>
</li>
</ul>
</td>
<td></td>
</tr>
</tbody>
</table>
<p>是不是很合理？</p>
<p>值 得一提的是，只要满足了“特征”这个要求，其实选择分区的方式并没有什么限制。例如，我们可以不用“取模”的方式，而是使用“一致性哈希”——没错，这里 就是一致性哈希的使用场景了。在利用“一致性哈希”来选择分区之后，在添加服务器的情况下便可以相对减少数据的迁移数量了。</p>
<p>当然，在实 现时还可以运用一些技巧。例如，我们的特征并非一定要“把用户ID作为前缀”——毕竟用户ID可能比较长，作为ID前缀还真有些难看（请想象把GUID作 为ID前缀，再加上另一个GUID作为ID主体的情景）。此时，我们可以把前提条件先进行一定程度的“具体化”（但就像之前提到的，不能过度），例如我们 可以把用户ID先进行取模，可能是1000万，便可以得到一个落在较大区间范围内的数字。然后，再把这个数字作BASE64编码，一下子前缀就缩小为4个 字符以内了。而且，1000万这个区间范围，无论是使用取模还是一致性哈希的方式来选择分区都非常可行，一般不会造成什么问题。</p>
<h1>总结</h1>
<p>数 据分片是系统优化的常用设计方式之一。正如前文所说的那样，数据分片的做法很多，本文提到的方式只是其中一种方式。这种根据ID特征的分片方式比较容易遇 到的问题之一，便是在数据分区数量改变时造成的规则冲突，这也正是我这篇文章所讨论的主要内容。从这个角度看来，其他一些分片方式，如创建时间也好，查找 表也罢，这样的问题反而不太常见。如果您有这方面的经验或是疑惑，也欢迎与我进行交流。</p>
<p>现在Web  2.0网站越来越热门了，此类项目的数据量也越来越大，从近几年的讨论形式可以看出，越来越多的人在强调什么大规模、高性能、或是海量数据。然后，似乎每 个人都会横向切分、纵向切分、缓存、分离。我猜，再接下来，估计又会有许多人以用关系型数据库为耻了吧？但是，想想这样的问题：博客园和JavaEye都 是国内技术社区的翘楚，它们都只用了1台数据库服务器。<a href="http://stackoverflow.com/">StackOverflow</a>是<a href="http://www.joelonsoftware.com/items/2009/12/13.html">世界上最大的编程网站</a>（它是使用ASP.NET MVC写的，兄弟们记住这个经典案例吧），似乎也只用了1台还是2台数据库服务器（可能配置比较高）及SQL Server。因此，即便是单台服务器，即便是使用关系型数据库，它在性能方面的潜力也是非常之高的。</p>
<p>因 此，数据分片应该只在需要的时候才做，因为它带来的复杂度会比中心存储的方式高出很多。这带来的结果是，可能您的应用程序还没有用足架构的能力就已经失败 了，这样各种投资也已经浪费了。假如您一开始用最简单的方式去做，可能很快会带来成长所需要空间及资源，此时再做更多投资进行架构优化也不迟——<a href="http://www.infoq.com/cn/presentations/hongqn-douban">架构不是一蹴而就，而是演变得来的</a>。当然，第一次投入多少复杂度是个需要权衡的东西，这也是考验架构师能力的地方。架构不是空中楼阁，而是各种真实资源调配的结果。</p>
</div>
<p>&nbsp;</p>
<p>转自：http://blog.zhaojie.me/2010/03/sharding-by-id-characteristic.html</p>
]]></content:encoded>
			<wfw:commentRss>http://yulans.cn/uncategorized/%e4%b8%80%e7%a7%8d%e4%bb%a5id%e7%89%b9%e5%be%81%e4%b8%ba%e4%be%9d%e6%8d%ae%e7%9a%84%e6%95%b0%e6%8d%ae%e5%88%86%e7%89%87%ef%bc%88sharding%ef%bc%89%e7%ad%96%e7%95%a5/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>多些时间能少写些代码</title>
		<link>http://yulans.cn/uncategorized/%e5%a4%9a%e4%ba%9b%e6%97%b6%e9%97%b4%e8%83%bd%e5%b0%91%e5%86%99%e4%ba%9b%e4%bb%a3%e7%a0%81</link>
		<comments>http://yulans.cn/uncategorized/%e5%a4%9a%e4%ba%9b%e6%97%b6%e9%97%b4%e8%83%bd%e5%b0%91%e5%86%99%e4%ba%9b%e4%bb%a3%e7%a0%81#comments</comments>
		<pubDate>Fri, 28 Oct 2011 09:35:23 +0000</pubDate>
		<dc:creator>cmpan</dc:creator>
				<category><![CDATA[未分类]]></category>

		<guid isPermaLink="false">http://yulans.cn/?p=886</guid>
		<description><![CDATA[在现在这个浮躁的时期，再加上敏捷咨询师们念的歪经，他们让人感觉上就像是软件产品是可以在很短的时间内高质量的完成的，这令那些管理者们很兴奋， 就像巴甫洛夫的条件反射实验中的狗看到了肉就会流口水那样兴奋。他们使用TDD，快速迭代，不断重构，持续集成直至持续部署的方法在进行软件开发。 软件开发真是这样的吗？难道不需要花时间去思考吗？对此，有些观点在Todd的《“品质在于构建过程”吗？》以及《Bob大叔和Jim Coplien对TDD的论战》中谈到过了。我只想想表达下面的观点： 软件的精髓在于设计，设计是一件很费大脑的事件。对于软件来说，设计没有完美的，它总是一件需要取舍需要权衡 的事，比如：时间换空间，空间换时间，TCP或UDP，同步还是异步，数据冗余还不冗余等等。那怕是一个小小的observers模式是pull方式还是 push方式 都需要仔细讨论。这些的东西需要时间和做前期尝试。 TDD、快速原型和迭代可能会对软件和团队产生负面影响。在一开始，你需 要花很大的精力来让你的软件从无到有（做过软件的人都知道，从零开始写代码是很痛苦的事），但是因为你没有想好，先做再说，所以，后期你会面临更多的质量 问题而让你需要花更多的时间精力。当然，那些咨询师会让你用持续集成和持续部署这样的方法。但我想告诉你，这并不解决你软件设计的缺陷。举个例子—— TDD、迭代、原型只关注功能性需求，其不会关注非功能性需求，比如性能问题，高可用性问题，系统维护问题（模块的耦合问题），等等。而这些问题往往都可 以让你的软件设计重新来过。 重构是恶梦，重构应该越少越好。当你维护一个复杂的系统时你会知道重构是一件多么恐怖的事情（参看《重构代码的7个阶段》）。如果一开始没有想好，你要面临的不单单是re-design, re-architect，还要面对时间和人力成本的增加，最难的是你还要面对的是团队士气因为不断的rework而逐渐低落并产生厌倦和懈怠情绪。 &#160; 所以，如果你能有多一些时间去和客户讨论一下需求和未来可能的变化，去调查一下实现的技术难点和细节，去和其他有经验的人讨论并推敲一下架构和设 计，去思考设计上的缺陷，那么，你的coding会变得非常地直，直到你一眼就看到尽头，你的测试案例也会写得非常地好，你会几乎不需要重构，于是，你会 在未来少写很多代码，从而你的软件开发会越来越轻松，直到技术开始换代。 我现在在做的项目，花了几乎4个月的时间来做设计，在这个过程中，我们反复思考、讨论和权衡若干种实现方法，并尽可能地穷举所有的场景和细节以及未 来可能的变化（那怕是那些简单的模块），有个模块被重写了至少三次，每次都是写到一半就被推翻重写，我们整个团队不断地在和其它团队讨论，并在对系统不断 地认识中对系统进行简化和优化，并力求达到完美。现在看来，没有贸然使用Scrum是明智的。 这就好像我们修路造桥一样，我们需要花大量的时间勘测地形地质，分析数据，思考可能出现的各种问题（各种自然灾害），评估不同的设计方案，而不是先尽快建好再说。 所以，多一些时间，不是让你多做几次迭代，多完成几个模块，而是可以让你少写一些代码，更快的交付一个更好的产品。 我相信你会有很多疑问，下面是我觉得你可能会有下面的一些观点，让我一条一条来回复： 首当其冲的一定会是项目的deadline，或是那种你没有活语权的项目。比如做那种“甲乙方合同式的项目”，我把这种项目统一认为是“外包项目”，在这种项目性质下，你很难有话语权。对此，我觉得，1）作为乙方的你还是应该和甲方在项目计划上争取一下，晓之以情，动之以理。2）如果不行，只能在时间、需求范围和质量上做一个权衡。另外，在这种情况下你要找一个方法，把你的压力和痛苦分担给用户和领导。（找到这个方法的前提需要你找到用户和领导他们害怕什么，嘿嘿） 过度设计和纸上谈兵。有人说会不会设计太多，造成过度设计，或是在设计上花太多的时间。这有可能。我上一家公 司的一个项目团队就花了1年多的时间来不停不停的开会和做设计，结果release的时候还有1000多个bug。这个问题的原因是，这个团队的设计是在 纸上谈兵，开会是开神仙会，讨论的设计都是浮云。所以，设计并不是讨论和思考，还需要去尝试，我认为当你的设计完成的时候，你的骨干核心代码都基本完成了。 我的团队成员水平太差，不会思考。首先，先恭喜你找到一堆码农，当然，这不怪你，这是中国教育和大环境的问题，让人不会思考。对于这样的情况，我有两个建议，1）量力而行，使多大的碗就吃多少饭。2）鼓励思考，那怕那些想法很不靠谱，因为如果不开始，那么将永远不会思考。 必需使用快速迭代。很多公司都在强行上敏捷，他们希望产品越快release越好，而没有充分的时间思考和讨论。对于这种项目，我的建议是，1）找有丰富经验的人来做。2）迭代过程中力求架构和程序逻辑的简单，简单，再简单，力求代码间的高内聚，低耦合。不然，重构的时候你就好玩了。 创业团队必需要快。做得快就是做得好吗？很多时候，不是谁快谁就能笑到最后的。这样的例子太多了。第一个做出来的人并不一定就会占领市场，其很有可能会成为先驱。 有钱的公司才会让团队用更多的时间去思考。错了，你们没有见过有钱的公司，有钱的公司可以招一堆干不成活的人，可以把事搞乱了再新来过，甚至可以把做失败的项目换个名字再重新立项。这些真正的有钱的公司只求快，只求人多，不怕做错决定。像我们这些没钱的人，干什么事都是小心翼翼地，生怕做错决定。 &#160; 转自：http://coolshell.cn/articles/5686.html &#160;]]></description>
			<content:encoded><![CDATA[<p>在现在这个浮躁的时期，再加上敏捷咨询师们念的歪经，他们让人感觉上就像是软件产品是可以在很短的时间内高质量的完成的，这令那些管理者们很兴奋， 就像巴甫洛夫的条件反射实验中的狗看到了肉就会流口水那样兴奋。他们使用TDD，快速迭代，不断重构，持续集成直至持续部署的方法在进行软件开发。<br />
<span id="more-886"></span><br />
软件开发真是这样的吗？难道不需要花时间去思考吗？对此，有些观点在Todd的《“品质在于构建过程”吗？》以及《Bob大叔和Jim Coplien对TDD的论战》中谈到过了。我只想想表达下面的观点：</p>
<ul>
<li><strong>软件的精髓在于设计，设计是一件很费大脑的事件</strong>。对于软件来说，设计没有完美的，它总是一件需要取舍需要权衡 的事，比如：时间换空间，空间换时间，TCP或UDP，同步还是异步，数据冗余还不冗余等等。那怕是一个小小的observers模式是pull方式还是 push方式 都需要仔细讨论。这些的东西需要时间和做前期尝试。</li>
</ul>
<ul>
<li><strong>TDD</strong>、<strong>快速原型和迭代可能会对软件和团队产生负面影响</strong>。在一开始，你需 要花很大的精力来让你的软件从无到有（做过软件的人都知道，从零开始写代码是很痛苦的事），但是因为你没有想好，先做再说，所以，后期你会面临更多的质量 问题而让你需要花更多的时间精力。当然，那些咨询师会让你用持续集成和持续部署这样的方法。但我想告诉你，这并不解决你软件设计的缺陷。举个例子—— TDD、迭代、原型只关注功能性需求，其不会关注非功能性需求，比如性能问题，高可用性问题，系统维护问题（模块的耦合问题），等等。而这些问题往往都可 以让你的软件设计重新来过。</li>
</ul>
<ul>
<li><strong>重构是恶梦，重构应该越少越好</strong>。当你维护一个复杂的系统时你会知道重构是一件多么恐怖的事情（参看《<a title="重构代码的7个阶段" href="http://coolshell.cn/articles/5201.html" target="_blank">重构代码的7个阶段</a>》）。如果一开始没有想好，你要面临的不单单是re-design, re-architect，还要面对时间和人力成本的增加，最难的是你还要面对的是团队士气因为不断的rework而逐渐低落并产生厌倦和懈怠情绪。</li>
</ul>
<p>&nbsp;</p>
<p>所以，如果你能有多一些时间去和客户讨论一下需求和未来可能的变化，去调查一下实现的技术难点和细节，去和其他有经验的人讨论并推敲一下架构和设 计，去思考设计上的缺陷，那么，你的coding会变得非常地直，直到你一眼就看到尽头，你的测试案例也会写得非常地好，你会几乎不需要重构，于是，你会 在未来少写很多代码，从而你的软件开发会越来越轻松，直到技术开始换代。</p>
<p>我现在在做的项目，花了几乎4个月的时间来做设计，在这个过程中，我们反复思考、讨论和权衡若干种实现方法，并尽可能地穷举所有的场景和细节以及未 来可能的变化（那怕是那些简单的模块），有个模块被重写了至少三次，每次都是写到一半就被推翻重写，我们整个团队不断地在和其它团队讨论，并在对系统不断 地认识中对系统进行简化和优化，并力求达到完美。现在看来，没有贸然使用Scrum是明智的。</p>
<p>这就好像我们修路造桥一样，我们需要花大量的时间勘测地形地质，分析数据，思考可能出现的各种问题（各种自然灾害），评估不同的设计方案，而不是先尽快建好再说。</p>
<p>所以，<strong>多一些时间，不是让你多做几次迭代，多完成几个模块，而是可以让你少写一些代码，更快的交付一个更好的产品</strong>。</p>
<p>我相信你会有很多疑问，下面是我觉得你可能会有下面的一些观点，让我一条一条来回复：</p>
<ul>
<li><strong>首当其冲的一定会是项目的deadline，或是那种你没有活语权的项目。</strong>比如做那种“甲乙方合同式的项目”，我把这种项目统一认为是“外包项目”，在这种项目性质下，你很难有话语权。对此，我觉得，1）作为乙方的你还是应该和甲方在项目计划上争取一下，晓之以情，动之以理。2）如果不行，只能在时间、需求范围和质量上做一个权衡。另外，<strong>在这种情况下你要找一个方法，把你的压力和痛苦分担给用户和领导。</strong>（找到这个方法的前提需要你找到用户和领导他们害怕什么，嘿嘿）</li>
</ul>
<ul>
<li><strong>过度设计和纸上谈兵</strong>。有人说会不会设计太多，造成过度设计，或是在设计上花太多的时间。这有可能。我上一家公 司的一个项目团队就花了1年多的时间来不停不停的开会和做设计，结果release的时候还有1000多个bug。这个问题的原因是，这个团队的设计是在 纸上谈兵，开会是开神仙会，讨论的设计都是浮云。所以，<strong>设计并不是讨论和思考，还需要去尝试，</strong>我认为当你的设计完成的时候，你的骨干核心代码都基本完成了。</li>
</ul>
<ul>
<li><strong>我的团队成员水平太差，不会思考</strong>。首先，先恭喜你找到一堆码农，当然，这不怪你，这是中国教育和大环境的问题，让人不会思考。对于这样的情况，我有两个建议，1）量力而行，使多大的碗就吃多少饭。2）鼓励思考，那怕那些想法很不靠谱，因为如果不开始，那么将永远不会思考。</li>
</ul>
<ul>
<li><strong>必需使用快速迭代</strong>。很多公司都在强行上敏捷，他们希望产品越快release越好，而没有充分的时间思考和讨论。对于这种项目，我的建议是，1）找有丰富经验的人来做。2）迭代过程中力求架构和程序逻辑的简单，简单，再简单，力求代码间的高内聚，低耦合。不然，重构的时候你就好玩了。</li>
</ul>
<ul>
<li><strong>创业团队必需要快</strong>。做得快就是做得好吗？很多时候，不是谁快谁就能笑到最后的。这样的例子太多了。第一个做出来的人并不一定就会占领市场，其很有可能会成为先驱。</li>
</ul>
<ul>
<li><strong>有钱的公司才会让团队用更多的时间去思考</strong>。错了，你们没有见过有钱的公司，有钱的公司可以招一堆干不成活的人，可以把事搞乱了再新来过，甚至可以把做失败的项目换个名字再重新立项。这些真正的有钱的公司只求快，只求人多，不怕做错决定。像我们这些没钱的人，干什么事都是小心翼翼地，生怕做错决定。</li>
</ul>
<p>&nbsp;</p>
<p>转自：http://coolshell.cn/articles/5686.html</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://yulans.cn/uncategorized/%e5%a4%9a%e4%ba%9b%e6%97%b6%e9%97%b4%e8%83%bd%e5%b0%91%e5%86%99%e4%ba%9b%e4%bb%a3%e7%a0%81/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>对于PHP大型开发框架的看法</title>
		<link>http://yulans.cn/php/%e5%af%b9%e4%ba%8ephp%e5%a4%a7%e5%9e%8b%e5%bc%80%e5%8f%91%e6%a1%86%e6%9e%b6%e7%9a%84%e7%9c%8b%e6%b3%95</link>
		<comments>http://yulans.cn/php/%e5%af%b9%e4%ba%8ephp%e5%a4%a7%e5%9e%8b%e5%bc%80%e5%8f%91%e6%a1%86%e6%9e%b6%e7%9a%84%e7%9c%8b%e6%b3%95#comments</comments>
		<pubDate>Wed, 26 Oct 2011 02:00:09 +0000</pubDate>
		<dc:creator>cmpan</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[实践经验]]></category>

		<guid isPermaLink="false">http://yulans.cn/?p=880</guid>
		<description><![CDATA[PHP从诞生以来就受到广大编程爱好者的喜欢，成为中小站长的好帮手，并培养了大量的PHP编程人员，但是随着PHP的应用越发广泛，很多时候已不限于从事中小网站的应用，一些大型PHP项目也屡见不鲜。 &#160; 当我们选择PHP开发大型项目时，就不得不考虑开发效率、开发规范、后期维护等问题，这时大家往往会选择一款人们所认可的开发框架，目前所流行的Zend Framework、Yii、Symfony、CodeIgniter、CakePHP等都声称有着开发大型应用的能力。 &#160; 新框架层出不穷，但当我们真正应用这些框架去实现产品又总是会有各种不同的问题产生： 一、大型框架的背后往往有着较为深厚的结构理论，最熟悉的莫过于MVC、ORM这样耳熟能详的理论术语，还有很多深度面向对象方面的知识，但是真正了解这些的人却为数不多，使得应用门槛急剧攀升；除此大型框架中的应用细节更是纷繁复杂，学习成本也相对较高，这对于原本只是定位中小应用的PHP变得尤为尴尬。 &#160; 二、PHP做为一门脚本语言，它的运行往往基于宿主进程（如：apache、php-fpm），在单次请求上经历创建进程、初始化环境、编译脚本、运行引擎、输出、资源回收、进程销毁等一系列过程，在编程语言层面综合运行效率上要比编译型语言慢上2-3个数量级，伴随着消耗大量的系统资源，在此基础上我们还要搭建及加载复杂的开发框架更是增加了其运行成本。而在大型应用中从不缺乏特殊需求，有时PHP加大型框架的运行效率就是致命的。 &#160; 三、大型框架所考虑的因素过多，开发人员在应用时需要额外关注代码以外的细节过多，如：非标准约定、冗长的手册、琐碎的配置、复杂的文件目录结构、难以限制的合理性约束、千姿百态的类库等等，使得大多数程序员开发过程屡糟困惑，提高开发效率也成了空话。 &#160; 四、最致命的一点，框架作者不断地寻找银弹，试图制作出一个满足所有需求的怪物。大型应用对系统的松散耦合性要求很高，通常不可能在开发层面直接对数据进行操作，见一个简单的SOA模型（附图），数据层和业务层几乎是物理隔离的，而在业务层的开发上只针对数据层提供的服务接口进行访问。从目前的PHP开发框架来说（特别是MVC模型），通常使用ORM来直接对数据库表进行抽象，并直接加以CRUD操作，靠谱的大型应用是不会这做的（或许适合VPS，但大型应用会选择VPS ??）。 &#160; 总结，PHP大型框架真的还处于很尴尬的地位，但是从另一方面来说，好的PHP大型框架真的是一个值得很多人学习的好榜样，其中蕴涵了大量的设计理念、设计模式、代码优化、语言特性、软件工程等知识体系，融汇php精髓但又远超PHP本身。 最后还是要对这些大型PHP框架说一流行话，“学之者生，用之者死”。 &#160; &#160; 转自：http://www.cnblogs.com/lajabs/archive/2011/10/14/2212704.html]]></description>
			<content:encoded><![CDATA[<p>PHP从诞生以来就受到广大编程爱好者的喜欢，成为中小站长的好帮手，并培养了大量的PHP编程人员，但是随着PHP的应用越发广泛，很多时候已不限于从事中小网站的应用，一些大型PHP项目也屡见不鲜。<br />
<span id="more-880"></span><br />
&nbsp;</p>
<p>当我们选择PHP开发大型项目时，就不得不考虑开发效率、开发规范、后期维护等问题，这时大家往往会选择一款人们所认可的开发框架，目前所流行的Zend Framework、Yii、Symfony、CodeIgniter、CakePHP等都声称有着开发大型应用的能力。</p>
<p>&nbsp;</p>
<p>新框架层出不穷，但当我们真正应用这些框架去实现产品又总是会有各种不同的问题产生：</p>
<p>一、大型框架的背后往往有着较为深厚的结构理论，最熟悉的莫过于MVC、ORM这样耳熟能详的理论术语，还有很多深度面向对象方面的知识，但是真正了解这些的人却为数不多，使得应用门槛急剧攀升；除此大型框架中的应用细节更是纷繁复杂，学习成本也相对较高，这对于原本只是定位中小应用的PHP变得尤为尴尬。</p>
<p>&nbsp;</p>
<p>二、PHP做为一门脚本语言，它的运行往往基于宿主进程（如：apache、php-fpm），在单次请求上经历创建进程、初始化环境、编译脚本、运行引擎、输出、资源回收、进程销毁等一系列过程，在编程语言层面综合运行效率上要比编译型语言慢上2-3个数量级，伴随着消耗大量的系统资源，在此基础上我们还要搭建及加载复杂的开发框架更是增加了其运行成本。而在大型应用中从不缺乏特殊需求，有时PHP加大型框架的运行效率就是致命的。</p>
<p>&nbsp;</p>
<p>三、大型框架所考虑的因素过多，开发人员在应用时需要额外关注代码以外的细节过多，如：非标准约定、冗长的手册、琐碎的配置、复杂的文件目录结构、难以限制的合理性约束、千姿百态的类库等等，使得大多数程序员开发过程屡糟困惑，提高开发效率也成了空话。</p>
<p>&nbsp;</p>
<p>四、最致命的一点，框架作者不断地寻找银弹，试图制作出一个满足所有需求的怪物。大型应用对系统的松散耦合性要求很高，通常不可能在开发层面直接对数据进行操作，见一个简单的SOA模型（附图），数据层和业务层几乎是物理隔离的，而在业务层的开发上只针对数据层提供的服务接口进行访问。从目前的PHP开发框架来说（特别是MVC模型），通常使用ORM来直接对数据库表进行抽象，并直接加以CRUD操作，靠谱的大型应用是不会这做的（或许适合VPS，但大型应用会选择VPS ??）。</p>
<p>&nbsp;</p>
<p>总结，PHP大型框架真的还处于很尴尬的地位，但是从另一方面来说，好的PHP大型框架真的是一个值得很多人学习的好榜样，其中蕴涵了大量的设计理念、设计模式、代码优化、语言特性、软件工程等知识体系，融汇php精髓但又远超PHP本身。</p>
<p>最后还是要对这些大型PHP框架说一流行话，“学之者生，用之者死”。</p>
<p>&nbsp;</p>
<p><img class="aligncenter" src="http://pic002.cnblogs.com/images/2011/316539/2011101423393198.jpg" alt="" /></p>
<p>&nbsp;</p>
<p>转自：http://www.cnblogs.com/lajabs/archive/2011/10/14/2212704.html</p>
]]></content:encoded>
			<wfw:commentRss>http://yulans.cn/php/%e5%af%b9%e4%ba%8ephp%e5%a4%a7%e5%9e%8b%e5%bc%80%e5%8f%91%e6%a1%86%e6%9e%b6%e7%9a%84%e7%9c%8b%e6%b3%95/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>从敏捷宣言理解敏捷交互设计</title>
		<link>http://yulans.cn/%e6%9e%b6%e6%9e%84/%e4%bb%8e%e6%95%8f%e6%8d%b7%e5%ae%a3%e8%a8%80%e7%90%86%e8%a7%a3%e6%95%8f%e6%8d%b7%e4%ba%a4%e4%ba%92%e8%ae%be%e8%ae%a1</link>
		<comments>http://yulans.cn/%e6%9e%b6%e6%9e%84/%e4%bb%8e%e6%95%8f%e6%8d%b7%e5%ae%a3%e8%a8%80%e7%90%86%e8%a7%a3%e6%95%8f%e6%8d%b7%e4%ba%a4%e4%ba%92%e8%ae%be%e8%ae%a1#comments</comments>
		<pubDate>Sun, 16 Oct 2011 10:45:08 +0000</pubDate>
		<dc:creator>cmpan</dc:creator>
				<category><![CDATA[架构]]></category>

		<guid isPermaLink="false">http://yulans.cn/?p=876</guid>
		<description><![CDATA[敏捷交互设计是敏捷方法论向交互设计领域的延伸，它提倡让所有相关人参与到设计过程中，迭代演进式地进行交互设计。从2010年开始，已经有越来越的团队在不同程度上使用敏捷交互设计的方法，而放弃了流程化的传统产品设计过程。 事实上，敏捷交互设计方法在很多方面都充分体现了敏捷价值观，因此，理解敏捷交互设计实践的最好方法是从记录在敏捷宣言中的价值观开始。 个体和交互胜过流程和工具 一个传统交互设计的流程一般分成以下几个步骤进行： 任务分析：任务分析基于功能列表（一般来自于客户的功能说明书）──在功能性需求的基础上拆分出人物流程和场景； 页面流程：根据任务分析的结果，为每一个大任务下的子任务中覆盖的功能制作页面流程； 信息建模：根据页面流程的设计出一套完整的信息框架，满足用户所有功能性需求； 原型设计：基于信息建模，设计出低保真原型，交给美工进行页面美化； 视觉设计：基于原型设计，对页面进行美化，最终产出高保真原型，同时编写设计说明； 在传统流程中，我们可以看到非常细致的分工──产品经理负责功能的拆解和分类，以及页面流转；交互设计师设计信息架构和具体交互行为；视觉设计师负责美化页面；前端开发人员负责高保真原型。 你是否体看见了传统瀑布式开发的影子？弊端显而易见： 分工造成的局限性──每个人都用自己的视角进行工作，无法形成统一的产品视角（Vision）； 分工造成的“不可评价性”──你没权利对产品经理的功能拆解有异义，因为你不是这方面的专家； 需求在传递中产生了失真的风险──需要靠大量文档进行记录； 客户没法说不──当客户需要到整个流程的最后看到一个或者两个大而全的设计方案时，他无法提出任何有价值的反馈，这本身就是用一个贵重的半成品绑架客户； 跟软件交付中的敏捷实践一样，敏捷交互设计倡导全功能团队，避免过于明显的分工，和基于分工特有的流程。仅有的流程是基于产品逐步清晰化的过程，而非基于人员的技能，所有人都应该参与到这个过程中来。敏捷交互设计主要分以下几个步骤： 寻找产品方向(Inspire)：抛开需求列表，从目标人群的期待体验出发，寻找可能存在的产品方向； 定位产品需求(Identify)：定位本产品需要提供什么样的消费者体验； 设计产品体验(Ideate)：对决定的目标体验进行设计； 验证产品设计(Implement)：快速制作原型，并频繁进行用户测试，迭代式改进。 图1. 从体验中寻找交付范围，把功能列表放在一边 除了流程简化和分工融合，在工具的选择上，敏捷交互设计也与传统方式有所不同──对于交互的推崇高于对特定工具的选择。 所有在敏捷交互设计中使用的工具，都应该遵循一条原则：它必须推动设计团队成员间的交互，而不是简单提升单个成员的工作效率。 基于此，敏捷交互设计中推崇各种轻量级工具，而不是大型的第三方软件，例如纸质原型Paper Protityping而非Visio或Axure此类原型工具。过于精细的结果往往会增加协作和反馈的门槛，虽然可能提升单个成员工作效率，却达不到鼓励交互的目的。 图2. 使用轻量级的工具进行交互设计 可工作的软件胜过完备的文档 敏捷软件交付过程中，每个迭代的核心产出是不足够完美，但却满足一个完整业务场景的软件──端到端流程的可完成，而不需要面面俱到的完美。 而传统开发方式中在长时间内只是各个功能模块中功能的堆砌，无法在短时间内实现端到端场景，那么文档便成为串联各个功能保证有序开发的必需品。 传统交互设计也存在这个问题。往往一个标准交互设计阶段的文档分三个方面，它们是： 内容方面(Content)：内容的层次和整体信息架构设计； 视觉方面(Visual)：整站风格的视觉设计文档； 交互方面(Interaction)：整站高保真交互设计原型； 仔细分析这些文档的生产过程，我们不难发现以下特点： 它们都是以整站为目标，试图覆盖所有使用场景； 它们的生产过程是线性的，直到三者全部完成才能够指导开发； 正因为每个环节的过程是孤立的，无法形成统一的认识，文档传递经常发生失真； 一个完美的东西很难得到产品方向性的关键反馈； 敏捷交互设计试图在解决以上问题。 敏捷交付的核心在于尽早地交付出可进行端到端测试的代码，而非完备文档，而敏捷交互设计的核心则在于尽早地交付出可以进行端到端可测试的原型，同样亦非完备文档。 而这里说的“端到端可测试的原型”包含以下含义： 端到端：必须设计出符合合理使用场景的端到端流程，这个流程会覆盖一个典型用户最核心的使用场景，所有的交互设计应该第一时间收敛在这个端到端场景周围，而非“整站”功能的分割展示； 可测试原型：不需要完美的原型，只需要所设计端到端场景中涉及到的原型，同时，原型的完备性上必须达到内容、视觉、交互三者细致力度一致，在测试中，三者力度的不一致往往会隐藏问题，例如，如果测试的原型视觉上过于完美，就会减弱用户在交互上的关注； 假设我们把交互设计的四周拆分成每周一个迭代，每周交付一个覆盖端到端场景，且在内容、视觉和交互方面都相对完备的原型收集反馈或进行测试，和传统项目相比，我们是否可以更早地得到客户反馈，是否可以让设计过程更透明，是否毋须完备的过程文档？答案自然是肯定的。 图3 逐步细化的原型，不停进行用户测试 当然，这样的过程必然需要多模块（这里的模块指技能的差别）之间的融合，实现全团队的运作。 你会问我，这是否意味着我们的交互设计师需要学会使用IIlustrtor进行视觉设计？或者视觉设计师需要学会HTML+CSS+jQuery制作高保真原型？ 是的，在很多情况下，敏捷交互设计团队中的设计师确实需要具备多种功能的T型人才──他们有专业的技能，也可以在其他方面有足够的贡献。 分工的结果只能导致能力的停滞不前、产品视角的缺失、职能不可替代的风险、协作软的低效、沟通的浪费等等，而唯一的好处在于，可以更快和“更不被抱怨（如德鲁克说过程文档的价值在于管理抱怨）”产生一堆堆相互分裂且无法让客户挑错的文档。 客户协作胜过合同谈判 让我们举例说明在传统交互设计阶段出现的场景：一份标书、一份功能列表、一份到处使用的所谓用户体验调查问卷，在交互设计的开始阶段，我们和客户在 一起进行“需求调研”，其实这些不重要，我们只需要对比我们之前的产品都有哪些功能可能有重复，类似产品有哪些可以借鉴，调研往往只是形式，关键还是未来 [...]]]></description>
			<content:encoded><![CDATA[<p>敏捷交互设计是敏捷方法论向交互设计领域的延伸，它提倡让所有相关人参与到设计过程中，迭代演进式地进行交互设计。从2010年开始，已经有越来越的团队在不同程度上使用敏捷交互设计的方法，而放弃了流程化的传统产品设计过程。</p>
<p>事实上，敏捷交互设计方法在很多方面都充分体现了敏捷价值观，因此，理解敏捷交互设计实践的最好方法是从记录在敏捷宣言中的价值观开始。<br />
<span id="more-876"></span></p>
<h2><strong>个体和交互胜过流程和工具</strong></h2>
<p>一个传统交互设计的流程一般分成以下几个步骤进行：</p>
<ol>
<li>任务分析：任务分析基于功能列表（一般来自于客户的功能说明书）──在功能性需求的基础上拆分出人物流程和场景；</li>
<li>页面流程：根据任务分析的结果，为每一个大任务下的子任务中覆盖的功能制作页面流程；</li>
<li>信息建模：根据页面流程的设计出一套完整的信息框架，满足用户所有功能性需求；</li>
<li>原型设计：基于信息建模，设计出低保真原型，交给美工进行页面美化；</li>
<li>视觉设计：基于原型设计，对页面进行美化，最终产出高保真原型，同时编写设计说明；</li>
</ol>
<p>在传统流程中，我们可以看到非常细致的分工──产品经理负责功能的拆解和分类，以及页面流转；交互设计师设计信息架构和具体交互行为；视觉设计师负责美化页面；前端开发人员负责高保真原型。</p>
<p>你是否体看见了传统瀑布式开发的影子？弊端显而易见：</p>
<ol>
<li>分工造成的局限性──每个人都用自己的视角进行工作，无法形成统一的产品视角（Vision）；</li>
<li>分工造成的“不可评价性”──你没权利对产品经理的功能拆解有异义，因为你不是这方面的专家；</li>
<li>需求在传递中产生了失真的风险──需要靠大量文档进行记录；</li>
<li>客户没法说不──当客户需要到整个流程的最后看到一个或者两个大而全的设计方案时，他无法提出任何有价值的反馈，这本身就是用一个贵重的半成品绑架客户；</li>
</ol>
<p>跟软件交付中的敏捷实践一样，敏捷交互设计倡导全功能团队，避免过于明显的分工，和基于分工特有的流程。仅有的流程是基于产品逐步清晰化的过程，而非基于人员的技能，所有人都应该参与到这个过程中来。敏捷交互设计主要分以下几个步骤：</p>
<ol>
<li>寻找产品方向(Inspire)：抛开需求列表，从目标人群的期待体验出发，寻找可能存在的产品方向；</li>
<li>定位产品需求(Identify)：定位本产品需要提供什么样的消费者体验；</li>
<li>设计产品体验(Ideate)：对决定的目标体验进行设计；</li>
<li>验证产品设计(Implement)：快速制作原型，并频繁进行用户测试，迭代式改进。</li>
</ol>
<p><img src="http://www.infoq.com/resource/articles/xzc-agile-interaction-design/zh/resources/image1.jpg" border="0" alt="" /></p>
<p><strong>图1. 从体验中寻找交付范围，把功能列表放在一边</strong></p>
<p>除了流程简化和分工融合，在工具的选择上，敏捷交互设计也与传统方式有所不同──对于交互的推崇高于对特定工具的选择。</p>
<p>所有在敏捷交互设计中使用的工具，都应该遵循一条原则：它必须推动设计团队成员间的交互，而不是简单提升单个成员的工作效率。</p>
<p>基于此，敏捷交互设计中推崇各种轻量级工具，而不是大型的第三方软件，例如纸质原型Paper Protityping而非Visio或Axure此类原型工具。过于精细的结果往往会增加协作和反馈的门槛，虽然可能提升单个成员工作效率，却达不到鼓励交互的目的。</p>
<p><img src="http://www.infoq.com/resource/articles/xzc-agile-interaction-design/zh/resources/image2.jpg" border="0" alt="" /></p>
<p><strong>图2. 使用轻量级的工具进行交互设计</strong></p>
<h2><strong>可工作的软件胜过完备的文档</strong></h2>
<p>敏捷软件交付过程中，每个迭代的核心产出是不足够完美，但却满足一个完整业务场景的软件──端到端流程的可完成，而不需要面面俱到的完美。</p>
<p>而传统开发方式中在长时间内只是各个功能模块中功能的堆砌，无法在短时间内实现端到端场景，那么文档便成为串联各个功能保证有序开发的必需品。</p>
<p>传统交互设计也存在这个问题。往往一个标准交互设计阶段的文档分三个方面，它们是：</p>
<ol>
<li>内容方面(Content)：内容的层次和整体信息架构设计；</li>
<li>视觉方面(Visual)：整站风格的视觉设计文档；</li>
<li>交互方面(Interaction)：整站高保真交互设计原型；</li>
</ol>
<p>仔细分析这些文档的生产过程，我们不难发现以下特点：</p>
<ol>
<li>它们都是以整站为目标，试图覆盖所有使用场景；</li>
<li>它们的生产过程是线性的，直到三者全部完成才能够指导开发；</li>
<li>正因为每个环节的过程是孤立的，无法形成统一的认识，文档传递经常发生失真；</li>
<li>一个完美的东西很难得到产品方向性的关键反馈；</li>
</ol>
<p>敏捷交互设计试图在解决以上问题。</p>
<p>敏捷交付的核心在于尽早地交付出可进行端到端测试的代码，而非完备文档，而敏捷交互设计的核心则在于尽早地交付出可以进行端到端可测试的原型，同样亦非完备文档。</p>
<p>而这里说的“端到端可测试的原型”包含以下含义：</p>
<ol>
<li>端到端：必须设计出符合合理使用场景的端到端流程，这个流程会覆盖一个典型用户最核心的使用场景，所有的交互设计应该第一时间收敛在这个端到端场景周围，而非“整站”功能的分割展示；</li>
<li>可测试原型：不需要完美的原型，只需要所设计端到端场景中涉及到的原型，同时，原型的完备性上必须达到内容、视觉、交互三者细致力度一致，在测试中，三者力度的不一致往往会隐藏问题，例如，如果测试的原型视觉上过于完美，就会减弱用户在交互上的关注；</li>
</ol>
<p>假设我们把交互设计的四周拆分成每周一个迭代，每周交付一个覆盖端到端场景，且在内容、视觉和交互方面都相对完备的原型收集反馈或进行测试，和传统项目相比，我们是否可以更早地得到客户反馈，是否可以让设计过程更透明，是否毋须完备的过程文档？答案自然是肯定的。</p>
<p><img src="http://www.infoq.com/resource/articles/xzc-agile-interaction-design/zh/resources/image3.jpg" border="0" alt="" /></p>
<p><strong>图3 逐步细化的原型，不停进行用户测试 </strong></p>
<p>当然，这样的过程必然需要多模块（这里的模块指技能的差别）之间的融合，实现全团队的运作。</p>
<p>你会问我，这是否意味着我们的交互设计师需要学会使用IIlustrtor进行视觉设计？或者视觉设计师需要学会HTML+CSS+jQuery制作高保真原型？</p>
<p>是的，在很多情况下，敏捷交互设计团队中的设计师确实需要具备多种功能的T型人才──他们有专业的技能，也可以在其他方面有足够的贡献。</p>
<p>分工的结果只能导致能力的停滞不前、产品视角的缺失、职能不可替代的风险、协作软的低效、沟通的浪费等等，而唯一的好处在于，可以更快和“更不被抱怨（如德鲁克说过程文档的价值在于管理抱怨）”产生一堆堆相互分裂且无法让客户挑错的文档。</p>
<h2><strong>客户协作胜过合同谈判</strong></h2>
<p>让我们举例说明在传统交互设计阶段出现的场景：一份标书、一份功能列表、一份到处使用的所谓用户体验调查问卷，在交互设计的开始阶段，我们和客户在 一起进行“需求调研”，其实这些不重要，我们只需要对比我们之前的产品都有哪些功能可能有重复，类似产品有哪些可以借鉴，调研往往只是形式，关键还是未来 的二至四周；</p>
<p>然后最后一次见客户也许就是最终文档提交的那天，给客户看一套精美的PSD文档，一般我们会做出A和B方案，其中不乏我们臆想出来的需求，有时只为让那个区域看起来不那么空，客户高兴地选择了其中的一套方案后，交付正式开始。</p>
<p>这就是基于合同进行设计的典型场景，谁也不知道合同中的功能列表意味着什么，而对于客户来说，它确实是购物单，真正交付时已忘记为什么需要。</p>
<p>但这不妨碍交互设计师进行设计，大部分时候他们并没有仔细思考这个功能真正的使用场景，而是把它“画”出来，让它看起来很美，却不管它如何实现和如何被用户使用。</p>
<p>这是基于合同进行设计自然而然的结果，在每个功能上，设计师都希望当成对自己的挑战，他们绞尽脑汁收集类似产品类似功能，尽可能取悦客户，说服他们采用更炫更丰富的交互方式，而作为不对交付负责的人他们毋须承担责任。</p>
<p>而敏捷交互设计希望打破这种基于合同或者换言之，基于功能列表的设计模式。它希望在设计的全过程将客户参与进来，让客户了解某个标书上的功能也许没 有意义，或者不是当下应该解决的问题──一个交付项目范围的最好确定时机就是在交互设计过程中客户的全程参与。客户参与的优势有以下几点：</p>
<ol>
<li>过程的透明化提升客户的安全感，随时保持对设计进度的了解；</li>
<li>最好的反馈模式就是让客户亲身参与设计过程；</li>
<li>了解最终用户和业务模式的是客户，客户是不可多得的资源；</li>
<li>当设计结果是功能列表的重新确认，对于合同中的内容便达成了新的共识；</li>
<li>客户参与的过程是建立信任的最佳机会，良好的信任关系应该在最开始就建立；</li>
<li>在产品设计方向上和客户达成一致，而不是仅靠标书或者访谈结果的支言片语；</li>
<li>提升参与感有助于培养更负责的客户，在交付阶段，之前的努力将会获得巨大的回报；</li>
<li>有效控制设计的变化，当客户亲身参与设计决定过程时，很多盲目的设计变化会减少很多；</li>
</ol>
<p><img src="http://www.infoq.com/resource/articles/xzc-agile-interaction-design/zh/resources/image4.jpg" border="0" alt="" /></p>
<p><strong>图4 和客户一起进行设计</strong></p>
<h2><strong>拥抱变化胜过遵循计划</strong></h2>
<p>当你的设计不能和客户一起进行，你只是个标书需求列表的简单执行者，需求的变化便是理所当然的事情。</p>
<p>在有些传统交互设计流程里甚至出现“需求冻结”这样的流程──如果你已经对某个环节的文档进行进行签收，就不能在“需求冻结”后改变主意。这样的结果无非两个：一尽量不要签收那个环节的文档；二在需求没有冻结的时候抓紧机会改变主意。</p>
<p>从敏捷宣言的角度来看，之前的三句话是最后一句话的基础，如果不能做到对个体和交互的尊重，把可用软件当作产生核心价值的东西，并且努力和客户进行协作，谈拥抱变化只是空话。在敏捷交互设计中，遵循同样的道理：</p>
<p>对个体和交互的尊重──当需求发生变化时，因为在新的流程中对产品视角的重视，可首先对需求变化的价值进行判断，即它是不是匹配达成一致的产品视 角；真正需求变化时，因为分工的模糊以及流程的简化，可更灵活地调配资源进行处理；再者因为大量使用轻量级的工具，修改成本大大降低；</p>
<p>关注可工作的软件──因为我们把可进行的端到端场景测试作为敏捷交互设计过程中最重要的目标，当出现需求变化时，可通过审视变化本身“是否包含在端 到端场景中？如果不产生这样的变化，用户因此有何种损失”来判断需求变化本身的价值；同时，因为能够尽早的得到端到端场景原型设计，需求本身往往来自于用 户测试的结果，这样的需求变化往往是有价值的，有理可依的；</p>
<p>客户协作的重要性──需求变化往往来自于客户的不确定性，很多情况下这种不确定又来自于一个新产品背后业务模式的不确定，客户协作帮助在设计过程中及早发现和解决这种来自业务方面的不确定，同时，客户协作对客户责任感的培养也大大减少了来自于客户不负责任的需求摆动。</p>
<p>这就是敏捷交互设计可以很好的管理用户需求的根本原因。</p>
<p>从敏捷推行到现在，在交付领域已经走向成熟，越来越多的团队开始采用敏捷方法进入开发，但从软件交付的全流程来看，早于交付的交互设计环节目前依然以传统设计方式为主。</p>
<p>随着消费型软件大行其道，用户对软件使用体验的要求越来越高，同时新产品的推陈出新对快速产品设计提出新的要求，这样的背景使得传统交互设计流程不 得不做出一些新的调整以拥抱更加频繁的变化，这也是为什么很多交互设计团队开始努力尝试具有敏捷价值观的敏捷交互设计进行产品设计的主要原因。</p>
<p>最后，让我们来比较一下传统交互设计方法和敏捷交互设计方法的区别：</p>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="295"><strong>传统交互设计</strong></td>
<td width="295"><strong>敏捷交互设计</strong></td>
</tr>
<tr>
<td width="295">没有交付团队的参与；客户参与度低；设计团队中各职能分工明确；</td>
<td width="295">交互设计师、用户研究者、视觉设计师、前端开发者、客户代表、以及开发团队代表都完整参与整个交互设计的过程，并只有能力区分而弱化职责分工；</td>
</tr>
<tr>
<td width="295">客户需求文档中的功能列表是贯穿设计过程的主线；</td>
<td width="295">基于终端使用者期待体验的设计过程，往往客户功能列表只作为参考；</td>
</tr>
<tr>
<td width="295">各自有各自对产品的理解，无法达成共识；</td>
<td width="295">对产品设计方向的形成共识是贯穿整个交互设计阶段；</td>
</tr>
<tr>
<td width="295">使用大型的交互设计软件；</td>
<td width="295">鼓励使用白板、海报、贴纸、手绘等轻量级的工具；</td>
</tr>
<tr>
<td width="295">客户只在开始和结束参与项目；</td>
<td width="295">客户全程参与设计活动；</td>
</tr>
<tr>
<td width="295">主要以文档制作为主；</td>
<td width="295">主要以Workshop工作坊活动为主，高互动过程；</td>
</tr>
<tr>
<td width="295">设计师单独和封闭工作；</td>
<td width="295">设计师合作式的工作，随时把工作的产出物展示，接受反馈；</td>
</tr>
<tr>
<td width="295">设计师能力专一；</td>
<td width="295">鼓励T型人才；</td>
</tr>
<tr>
<td width="295">缺少用户测试；</td>
<td width="295">在不同精细度的原型上快速进行用户测试，迭代式演进设计；</td>
</tr>
<tr>
<td width="295">大而全的设计；</td>
<td width="295">只设计足够的交互；</td>
</tr>
<tr>
<td width="295">远离客户以避免变化；</td>
<td width="295">和客户在一起鼓励变化；</td>
</tr>
<tr>
<td width="295">不对交付负责，肆意发挥；</td>
<td width="295">对交付负责，在成本接受的范围内创新；</td>
</tr>
</tbody>
</table>
<p>转自：http://www.infoq.com/cn/articles/xzc-agile-interaction-design</p>
]]></content:encoded>
			<wfw:commentRss>http://yulans.cn/%e6%9e%b6%e6%9e%84/%e4%bb%8e%e6%95%8f%e6%8d%b7%e5%ae%a3%e8%a8%80%e7%90%86%e8%a7%a3%e6%95%8f%e6%8d%b7%e4%ba%a4%e4%ba%92%e8%ae%be%e8%ae%a1/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>深入浅出REST</title>
		<link>http://yulans.cn/%e6%9e%b6%e6%9e%84/%e6%b7%b1%e5%85%a5%e6%b5%85%e5%87%barest</link>
		<comments>http://yulans.cn/%e6%9e%b6%e6%9e%84/%e6%b7%b1%e5%85%a5%e6%b5%85%e5%87%barest#comments</comments>
		<pubDate>Sun, 16 Oct 2011 10:43:33 +0000</pubDate>
		<dc:creator>cmpan</dc:creator>
				<category><![CDATA[架构]]></category>

		<guid isPermaLink="false">http://yulans.cn/?p=874</guid>
		<description><![CDATA[不知你是否意识到，围绕着什么才是实现异构的应用到应用通信的“正确”方式，一场争论正进行的如火如荼：虽然当前主流的方式明显地集中在基于 SOAP、WSDL和WS-*规范的Web Services领域，但也有少数人用细小但洪亮的声音主张说更好的方式是REST，表述性状态转移（REpresentational State Transfer）的简称。在本文中，我不会涉及争论的话题，而是尝试对REST和RESTful HTTP应用集成做实用性的介绍。以我的经验，有些话题一旦触及就会引来众多的讨论，当涉及到这方面话题的时候，我会深入详细地阐述。 REST关键原则 大部分对REST的介绍是以其正式的定义和背景作为开场的。但这儿且先按下不表，我先提出一个简单扼要的定义：REST定义了应该如何正确地使用 （这和大多数人的实际使用方式有很大不同）Web标准，例如HTTP和URI。如果你在设计应用程序时能坚持REST原则，那就预示着你将会得到一个使用 了优质Web架构（这将让你受益）的系统。总之，五条关键原则列举如下： 为所有“事物”定义ID 将所有事物链接在一起 使用标准方法 资源多重表述 无状态通信 下面让我们进一步审视这些原则。 为所有“事物”定义ID 在这里我使用了“事物”来代替更正式准确的术语“资源”，因为一条如此简单的原则，不应该被淹没在术语当中。思考一下人们构建的系统，通常会找到一 系列值得被标识的关键抽象。每个事物都应该是可标识的，都应该拥有一个明显的ID——在Web中，代表ID的统一概念是：URI。URI构成了一个全局命 名空间，使用URI标识你的关键资源意味着它们获得了一个唯一、全局的ID。 对事物使用一致的命名规则（naming scheme）最主要的好处就是你不需要提出自己的规则——而是依靠某个已被定义，在全球范围中几乎完美运行，并且能被绝大多数人所理解的规则。想一下你 构建的上一个应用（假设它不是采用RESTful方式构建的）中的任意一个高级对象（high-level object），那就很有可能看到许多从使用唯一标识中受益的用例。比如，如果你的应用中包含一个对顾客的抽象，那么我可以相当肯定，用户会希望将一个指 向某个顾客的链接，能通过电子邮件发送到同事那里，或者加入到浏览器的书签中，甚至写到纸上。更透彻地讲：如果在一个类似于Amazon.com的在线商 城中，没有用唯一的ID（一个URI）标识它的每一件商品，可想而知这将是多么可怕的业务决策。 当面对这个原则时，许多人惊讶于这是否意味着需要直接向外界暴露数据库记录（或者数据库记录ID）——自从多年以来面向对象的实践告诫我们，要将持 久化的信息作为实现细节隐藏起来之后，哪怕是刚有点想法都常会使人惊恐。但是这条原则与隐藏实现细节两者之间并没有任何冲突：通常，值得被URI标识的事 物——资源——要比数据库记录抽象的多。例如，一个定单资源可以由定单项、地址以及许多其它方面（可能不希望作为单独标识的资源暴露出来）组成。标识所有 值得标识的事物，领会这个观念可以进一步引导你创造出在传统的应用程序设计中不常见的资源：一个流程或者流程步骤、一次销售、一次谈判、一份报价请求—— 这都是应该被标识的事物的示例。同样，这也会导致创建比非RESTful设计更多的持久化实体。 下面是一些你可能想到的URI的例子： http://example.com/customers/1234 http://example.com/orders/2007/10/776654 http://example.com/products/4554 http://example.com/processes/salary-increase-234 正如我选择了创建便于阅读的URI——这是个有用的观点，尽管不是RESTful设计所必须的——应该能十分容易地推测出URI的含义：它们明显地标识着单一“数据项”。但是再往下看： http://example.com/orders/2007/11 http://example.com/products?color=green 首先，这两个URI看起来与之前的稍有不同——毕竟，它们不是对一件事物的标识，而是对一类事物集合的标识（假定第一个URI标识了所有在2007年11月份提交的定单，第二个则是绿颜色产品的集合）。但是这些集合自身也是事物（资源），也应该被标识。 注意，使用唯一、全局统一的命名规则的好处，既适用于浏览器中的Web应用，也适用于机对机（machine-to-machine，m2m）通信。 来对第一个原则做下总结：使用URI标识所有值得标识的事物，特别是应用中提供的所有“高级”资源，无论这些资源代表单一数据项、数据项集合、虚拟亦或实际的对象还是计算结果等。 将所有事物链接在一起 接下来要讨论的原则有一个有点令人害怕的正式描述：“超媒体被当作应用状态引擎（Hypermedia as the engine of application state）”，有时简写为HATEOAS。（严格地说，这不是我说的。）这个描述的核心是超媒体概念，换句话说：是链接的思想。链接是我们在HTML中常见的概念，但是它的用处绝不局限于此（用于人们网络浏览）。考虑一下下面这个虚构的XML片段： &#60;order self="http://example.com/customers/1234"&#62; &#60;amount&#62;23&#60;/amount&#62; &#60;product ref="http://example.com/products/4554"&#62; [...]]]></description>
			<content:encoded><![CDATA[<p>不知你是否意识到，围绕着什么才是实现异构的应用到应用通信的“正确”方式，一场争论正进行的如火如荼：虽然当前主流的方式明显地集中在基于 SOAP、WSDL和WS-*规范的Web  Services领域，但也有少数人用细小但洪亮的声音主张说更好的方式是REST，表述性状态转移（REpresentational State  Transfer）的简称。在本文中，我不会涉及争论的话题，而是尝试对REST和RESTful  HTTP应用集成做实用性的介绍。以我的经验，有些话题一旦触及就会引来众多的讨论，当涉及到这方面话题的时候，我会深入详细地阐述。<br />
<span id="more-874"></span></p>
<h2>REST关键原则</h2>
<p>大部分对REST的介绍是以其正式的定义和背景作为开场的。但这儿且先按下不表，我先提出一个简单扼要的定义：REST定义了应该如何正确地使用 （这和大多数人的实际使用方式有很大不同）Web标准，例如HTTP和URI。如果你在设计应用程序时能坚持REST原则，那就预示着你将会得到一个使用 了优质Web架构（这将让你受益）的系统。总之，五条关键原则列举如下：</p>
<ul>
<li>为所有“事物”定义ID</li>
<li>将所有事物链接在一起</li>
<li>使用标准方法</li>
<li>资源多重表述</li>
<li>无状态通信</li>
</ul>
<p>下面让我们进一步审视这些原则。</p>
<h2>为所有“事物”定义ID</h2>
<p>在这里我使用了“事物”来代替更正式准确的术语“资源”，因为一条如此简单的原则，不应该被淹没在术语当中。思考一下人们构建的系统，通常会找到一 系列值得被标识的关键抽象。每个事物都应该是可标识的，都应该拥有一个明显的ID——在Web中，代表ID的统一概念是：URI。URI构成了一个全局命 名空间，使用URI标识你的关键资源意味着它们获得了一个唯一、全局的ID。</p>
<p>对事物使用一致的命名规则（naming  scheme）最主要的好处就是你不需要提出自己的规则——而是依靠某个已被定义，在全球范围中几乎完美运行，并且能被绝大多数人所理解的规则。想一下你 构建的上一个应用（假设它不是采用RESTful方式构建的）中的任意一个高级对象（high-level  object），那就很有可能看到许多从使用唯一标识中受益的用例。比如，如果你的应用中包含一个对顾客的抽象，那么我可以相当肯定，用户会希望将一个指 向某个顾客的链接，能通过电子邮件发送到同事那里，或者加入到浏览器的书签中，甚至写到纸上。更透彻地讲：如果在一个类似于Amazon.com的在线商 城中，没有用唯一的ID（一个URI）标识它的每一件商品，可想而知这将是多么可怕的业务决策。</p>
<p>当面对这个原则时，许多人惊讶于这是否意味着需要直接向外界暴露数据库记录（或者数据库记录ID）——自从多年以来面向对象的实践告诫我们，要将持 久化的信息作为实现细节隐藏起来之后，哪怕是刚有点想法都常会使人惊恐。但是这条原则与隐藏实现细节两者之间并没有任何冲突：通常，值得被URI标识的事 物——资源——要比数据库记录抽象的多。例如，一个定单资源可以由定单项、地址以及许多其它方面（可能不希望作为单独标识的资源暴露出来）组成。标识所有 值得标识的事物，领会这个观念可以进一步引导你创造出在传统的应用程序设计中不常见的资源：一个流程或者流程步骤、一次销售、一次谈判、一份报价请求—— 这都是应该被标识的事物的示例。同样，这也会导致创建比非RESTful设计更多的持久化实体。</p>
<p>下面是一些你可能想到的URI的例子：</p>
<pre>http://example.com/customers/1234

http://example.com/orders/2007/10/776654

http://example.com/products/4554

http://example.com/processes/salary-increase-234</pre>
<p>正如我选择了创建便于阅读的URI——这是个有用的观点，尽管不是RESTful设计所必须的——应该能十分容易地推测出URI的含义：它们明显地标识着单一“数据项”。但是再往下看：</p>
<pre>http://example.com/orders/2007/11

http://example.com/products?color=green</pre>
<p>首先，这两个URI看起来与之前的稍有不同——毕竟，它们不是对一件事物的标识，而是对一类事物集合的标识（假定第一个URI标识了所有在2007年11月份提交的定单，第二个则是绿颜色产品的集合）。但是这些集合自身也是事物（资源），也应该被标识。</p>
<p>注意，使用唯一、全局统一的命名规则的好处，既适用于浏览器中的Web应用，也适用于机对机（machine-to-machine，m2m）通信。</p>
<p>来对第一个原则做下总结：使用URI标识所有值得标识的事物，特别是应用中提供的所有“高级”资源，无论这些资源代表单一数据项、数据项集合、虚拟亦或实际的对象还是计算结果等。</p>
<h2>将所有事物链接在一起</h2>
<p>接下来要讨论的原则有一个有点令人害怕的正式描述：“超媒体被当作应用状态引擎（Hypermedia as the engine of application state）”，有时简写为HATEOAS。（严格地说，这不是我说的。）这个描述的核心是<strong>超媒体</strong>概念，换句话说：是<strong>链接</strong>的思想。链接是我们在HTML中常见的概念，但是它的用处绝不局限于此（用于人们网络浏览）。考虑一下下面这个虚构的XML片段：</p>
<pre>&lt;order self="http://example.com/customers/1234"&gt;
   &lt;amount&gt;23&lt;/amount&gt;
   &lt;product ref="http://example.com/products/4554"&gt;
   &lt;customer ref="http://example.com/customers/1234"&gt;
&lt;/customer&gt; &lt;/product&gt;&lt;/order&gt;</pre>
<p>如果你观察文档中product和customer的链接，就可以很容易地想象到，应用程序（已经检索过文档）如何“跟随”链接检索更多的信息。当然，如果使用一个遵守专用命名规范的简单“id”属性作为链接，也是可行的——<strong>但是仅限于应用环境之内</strong>。使用URI表示链接的优雅之处在于，链接可以指向由不同应用、不同服务器甚至位于另一个大陆上的不同公司提供的资源——因为URI命名规范是全球标准，构成Web的所有资源都可以互联互通。</p>
<p>超媒体原则还有一个更重要的方面——应用“状态”。简而言之，实际上服务器端（如果你愿意，也可以叫服务提供者）为客户端（服务消费者）提供一组链 接，使客户端能通过链接将应用从一个状态改变为另一个状态。稍后我们会在另一篇文章中探究这个方面的影响；目前，只需要记住：链接是构成动态应用的非常有 效的方式。</p>
<p>对此原则总结如下：任何可能的情况下，使用链接指引可以被标识的事物（资源）。也正是超链接造就了现在的Web。</p>
<h2>使用标准方法</h2>
<p>在前两个原则的讨论中暗含着一个假设：接收URI的应用程序可以通过URI明确地<strong>做</strong>一些有意义的事情。如果你在公共汽车上看到一个URI，你可以将它输入浏览器的地址栏中并回车——但是你的浏览器如何知道需要对这个URI做些什么呢？</p>
<p>它知道如何去处理URI的原因在于所有的资源都支持同样的接口，一套同样的方法（只要你乐意，也可以称为操作）集合。在HTTP中这被叫做动词 （verb），除了两个大家熟知的（GET和POST）之外，标准方法集合中还包含PUT、DELETE、HEAD和OPTIONS。这些方法的含义连同 行为许诺都一起定义在HTTP规范之中。如果你是一名OO开发人员，就可以想象到RESTful  HTTP方案中的所有资源都继承自类似于这样的一个类（采用类Java、C#的伪语法描述，请注意关键的方法）：</p>
<pre>class Resource {
     Resource(URI u);
     Response get();
     Response post(Request r);
     Response put(Request r);
     Response delete();
}</pre>
<p>由于所有资源使用了同样的接口，你可以依此使用GET方法检索一个<strong>表述</strong>（representation）——也 就是对资源的描述。因为规范中定义了GET的语义，所以可以肯定当你调用它的时候不需要对后果负责——这就是为什么可以“安全”地调用此方法。GET方法 支持非常高效、成熟的缓存，所以在很多情况下，你甚至不需要向服务器发送请求。还可以肯定的是，GET方法具有<strong>幂等性</strong>[译 注：指多个相同请求返回相同的结果]——如果你发送了一个GET请求没有得到结果，你可能不知道原因是请求未能到达目的地，还是响应在反馈的途中丢失了。 幂等性保证了你可以简单地再发送一次请求解决问题。幂等性同样适用于PUT（基本的含义是“更新资源数据，如果资源不存在的话，则根据此URI创建一个新 的资源”）和DELETE（你完全可以一遍又一遍地操作它，直到得出结论——删除不存在的东西没有任何问题）方法。POST方法，通常表示“创建一个新资 源”，也能被用于调用任<span style="color: #000000;">意</span><span style="color: #000000;">过程</span>，因而它既不安全也不具有幂等性。</p>
<p>如果你采用RESTful的方式暴露应用功能（如果你乐意，也可以称为服务功能），<strong>那这条原则和它的约束同样也适用于你</strong>。如果你已经习惯于另外的设计方式，则很难去接受这条原则——毕竟，你很可能认为你的应用包含了超出这些操作表达范围的逻辑。请允许我花费一些时间来让你相信不存在这样的情况。</p>
<p>来看下面这个简单的采购方案例子：</p>
<p><img src="http://www.infoq.com/resource/articles/rest-introduction/zh/resources/figure1.jpg" alt="Sample Scenario" width="200" /></p>
<p>可以看到，例子中定义了两个服务程序（没有包含任何实现细节）。这些服务程序的接口都是为了完成任务（正是我们讨论的 OrderManagement和CustomerManagement服务）而定制的。如果客户端程序试图使用这些服务，那它必须针对这些特定接口进行 编码——不可能在这些接口定义之前，使用客户程序去有目的地和接口协作。这些接口定义了服务程序的应用协议（application  protocol）。</p>
<p>在RESTful HTTP方式中，你将通过组成<strong>HTTP应用协议</strong>的通用接口访问服务程序。你可能会想出像这样的方式：</p>
<p><img src="http://www.infoq.com/resource/articles/rest-introduction/zh/resources/figure2.jpg" alt="Sample Scenario, done RESTfully" width="400" /></p>
<p>可以看到，服务程序中的特定操作被映射成为标准的HTTP方法——为了消除歧义，我创建了一组全新的资源。“这是骗人的把戏”，我听见你叫嚷着。 不，这不是欺骗。标识一个顾客的URI上的GET方法正好相当于getCustomerDetails操作。有人用三角形形象化地说明了这一点：</p>
<p><img src="http://www.infoq.com/resource/articles/rest-introduction/zh/resources/figure3.jpg" alt="Knobs one can turn" width="200" /></p>
<p>把三个顶点想象为你可以调节的按钮。可以看到在第一种方法中，你拥有许多操作，许多种类的数据以及固定数量的“实例”（本质上和你拥有的服务程序数 量一致）。在第二种方法中，你拥有固定数量的操作，许多种类的数据和许多调用固定方法的对象。它的意义在于，证明了通过这两种方式，你基本上可以表示任何 你喜欢的事情。</p>
<p>为什么使用标准方法如此重要？从根本上说，它使你的应用成为Web的一部分——应用程序为Web变成Internet上最成功的应用所做的贡献，与 它添加到Web中的资源数量成比例。采用RESTful方式，一个应用可能会向Web中添加数以百万计的客户URI；如果采用CORBA技术并维持应用的 原有设计方式，那它的贡献大抵只是一个“端点（endpoint）”——就好比一个非常小的门，仅仅允许有钥匙的人进入其中的资源域。</p>
<p>统一接口也使得所有理解HTTP应用协议的组件能与你的应用交互。通用客户程序（generic client）就是从中受益的组件的例子，例如curl、wget、代理、缓存、HTTP服务器、网关还有Google、Yahoo!、MSN等等。</p>
<p>总结如下：为使客户端程序能与你的资源相互协作，资源应该正确地实现默认的应用协议（HTTP），也就是使用标准的GET、PUT、POST和DELETE方法。</p>
<h2>资源多重表述</h2>
<p>到目前为止我们一直忽略了一个稍微复杂的问题：客户程序如何知道该怎样处理检索到的数据，比如作为GET或者POST请求的结果？原因是，HTTP 采取的方式是允许数据处理和操作调用之间关系分离的。换句话说，如果客户程序知道如何处理一种特定的数据格式，那就可以与所有提供这种表述格式的资源交 互。让我们再用一个例子来阐明这个观点。利用HTTP内容协商（content negotiation），客户程序可以请求一种特定格式的表述：</p>
<pre>GET /customers/1234 HTTP/1.1
Host: example.com
Accept: application/vnd.mycompany.customer+xml</pre>
<p>请求的结果可能是一些由公司专有的XML格式表述的客户信息。假设客户程序发送另外一个不同的请求，就如下面这样：</p>
<pre>GET /customers/1234 HTTP/1.1
Host: example.com
Accept: text/x-vcard</pre>
<p>结果则可能是VCard格式的客户地址。（在这里我没有展示响应的内容，在其HTTP  Content-type头中应该包含着关于数据类型的元数据。）这说明为什么理想的情况下，资源表述应该采用标准格式——如果客户程序对HTTP应用协 议和一组数据格式都有所“了解”，那么它就可以用一种有意义的方式<strong>与世界上任意一个RESTful HTTP应用交互</strong>。 不幸的是，我们不可能拿到所有东西的标准格式，但是，或许我们可以想到在公司或者一些合作伙伴中使用标准格式来营造一个小环境。当然以上情况不仅适用于从 服务器端到客户端的数据，反之既然——倘若从客户端传来的数据符合应用协议，那么服务器端就可以使用特定的格式处理数据，而不去关心客户端的类型。</p>
<p id="scroll_to_here">在实践中，资源多重表述还有着其它重要的好处：如果你为你的资源提供HTML和XML两种表述方式，那这些资源不仅可以被你的应用所用，还可以被任意标准Web浏览器所用——也就是说，你的应用信息可以被所有会使用Web的人获取到。</p>
<p>资源多重表述还有另外一种使用方式：你可以将应用的Web UI纳入到Web  API中——毕竟，API的设计通常是由UI可以提供的功能驱动的，而UI也是通过API执行动作的。将这两个任务合二为一带来了令人惊讶的好处，这使得 使用者和调用程序都能得到更好的Web接口。</p>
<p>总结：针对不同的需求提供资源多重表述。</p>
<h2>无状态通信</h2>
<p><strong>无状态通信</strong>是我要讲到的最后一个原则。首先，需要着重强调的是，虽然REST包含无状态性（statelessness）的观念，但这并不是说暴露功能的应用不能有状态——<br />
事实上，在大部分情况下这会导致整个做法没有任何用处。REST要求状态要么被放入资源状态中，要么保存在客户端上。或者换句话说，服务器端不能保持除了 单次请求之外的，任何与其通信的客户端的通信状态。这样做的最直接的理由就是可伸缩性——  如果服务器需要保持客户端状态，那么大量的客户端交互会严重影响服务器的内存可用空间（footprint）。（注意，要做到无状态通信往往需要需要一些 重新设计——不能简单地将一些session状态绑缚在URI上，然后就宣称这个应用是RESTful。）</p>
<p>但除此以外，其它方面可能显得更为重要：无状态约束使服务器的变化对客户端是不可见的，因为在两次连续的请求中，客户端并不依赖于同一台服务器。一 个客户端从某台服务器上收到一份包含链接的文档，当它要做一些处理时，这台服务器宕掉了，可能是硬盘坏掉而被拿去修理，可能是软件需要升级重启——如果这 个客户端访问了从这台服务器接收的链接，它不会察觉到后台的服务器已经改变了。</p>
<h2>理论上的REST</h2>
<p>我承认：以上我所说的REST不是真正的REST，而且我可能有点过多地热衷于简单化。但因为我想有一个与众不同的开场，所以没有在一开始就介绍其正式的定义和背景。现在就让我们稍微简要地介绍一下这方面的内容。</p>
<p>首先，先前我并没有明确地区分HTTP、RESTful HTTP和REST。要理解这些不同方面之间的关系，我们要先来看看REST的历史。</p>
<p><a title="Roy T. Fielding" href="http://www.ics.uci.edu/%7Efielding/">Roy T. Fielding</a>在他的<a id="ewd-" title="博士学位论文" href="http://www.ics.uci.edu/%7Efielding/pubs/dissertation/top.htm">博士学位论文</a>（实际上你应该访问这个链接——至少对于一篇学术论文来说，它是相当易读的。此论文已被翻译成<a id="nev8" title="中文" href="http://www.redsaga.com/opendoc/REST_cn.pdf">中文</a>） 中定义了术语REST。Roy曾是许多基本Web协议的主要设计者，其中包括HTTP和URIs，并且他在论文中对这些协议提出了很多想法。（这篇论文被 誉为“REST圣经”，这是恰当的——毕竟，是作者发明了这个术语，所以在定义上，他写的任何内容都被认为是权威的。）在论文中，Roy首先定义一种方法 论来谈论<strong>架构风格</strong>——高级、抽象的模式，来表达架构方法背后的核心理念。每一个架构风格由一系列的<strong>约束</strong>（constraints）定义形成。架构风格的例子包括“没有风格”（根本没有任何约束）、管道和过滤器（pipe and filter）、客户端/服务器、分布式对象以及——你猜到它了——REST。</p>
<p>如果对你来说这些听起来都太抽象了，那就对了——REST在本质上是一个可以被许多不同技术实现的高层次的风格，而且可以被实例化——通过为它的抽 象特性赋上不同的值。比如，REST中包含资源和统一接口的概念——也就是说，所有资源都应该对这些相同的方法作出反应。但是REST并没有说明是哪些方 法，或者有多少方法。</p>
<p>REST风格的一个“化身”便是HTTP（以及一套相关的一套标准，比如URI），或者稍微抽象一些：Web架构自身。接着上面的例子，HTTP使 用HTTP动词作为REST统一接口的“实例”。由于Fielding是在Web已经（或者至少是大部分）“完善”了之后才定义的REST风格，有人可能 会争论两者是不是100%的匹配。但是无论如何，整体上来说Web、HTTP和URI仅仅是REST风格的一个主要实现。不过，由于Roy  Fielding即是REST论文的作者，又对Web架构设计有过深远的影响，两者相似也在情理之中。</p>
<p>最后，我在前面一次又一次地使用着术语“RESTful  HTTP”，原因很简单：许多使用HTTP的应用因为一些理由并没有遵循REST原则，有人会说使用HTTP而不遵循REST原则就等同于滥用HTTP。 当然这听起来有点狂热——事实上违反REST约束的原因通常是，仅仅因为每个约束带来的设计权衡可能不适合于一些特殊情况。但通常，违背REST约束的原 因可归咎于对其好处认知的缺乏。来看一个明显的反面案例：使用HTTP  GET调用类似于删除对象的操作，这违反了REST的安全约束和一般性常识（客户程序不应为此负责，服务器端开发人员大概不是有意而为之）。但在随后的文 章中，我会提及更多这样或那样的对HTTP的滥用。</p>
<h2>总结</h2>
<p>本文试图对REST（Web架构）背后的概念提供快速的介绍。RESTful HTTP暴露功能的方式与RPC、分布式对象以及Web  Services是不相同的；要真正理解这些不同是需要一些心态的转变。不管你构建的应用是仅仅想暴露Web  UI还是想把API变成Web的一份子，了解下REST的原则还是有好处的。</p>
<p><a href="http://www.innoq.com/blog/st/"><strong>Stefan Tilkov</strong></a><strong>是InfoQ SOA社区的首席编辑，并且是位于德国和瑞士的</strong><a id="hdph" title="innoQ" href="http://www.innoq.com/"><strong>innoQ</strong></a><strong>公司的共同创始人、首席顾问和REST狂热分子首领。</strong></p>
<p><strong>查看英文原文</strong>：<a id="mxm5" title="A Brief Introduction to REST" href="http://www.infoq.com/articles/rest-introduction">A Brief Introduction to REST</a></p>
<p>转自：http://www.infoq.com/cn/articles/rest-introduction</p>
]]></content:encoded>
			<wfw:commentRss>http://yulans.cn/%e6%9e%b6%e6%9e%84/%e6%b7%b1%e5%85%a5%e6%b5%85%e5%87%barest/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>HTML的base标签在ie6下的问题解决</title>
		<link>http://yulans.cn/%e5%89%8d%e7%ab%af%e8%ae%be%e8%ae%a1/html%e7%9a%84base%e6%a0%87%e7%ad%be%e5%9c%a8ie6%e4%b8%8b%e7%9a%84%e9%97%ae%e9%a2%98%e8%a7%a3%e5%86%b3</link>
		<comments>http://yulans.cn/%e5%89%8d%e7%ab%af%e8%ae%be%e8%ae%a1/html%e7%9a%84base%e6%a0%87%e7%ad%be%e5%9c%a8ie6%e4%b8%8b%e7%9a%84%e9%97%ae%e9%a2%98%e8%a7%a3%e5%86%b3#comments</comments>
		<pubDate>Wed, 12 Oct 2011 09:33:52 +0000</pubDate>
		<dc:creator>cmpan</dc:creator>
				<category><![CDATA[WEB]]></category>
		<category><![CDATA[前端设计]]></category>

		<guid isPermaLink="false">http://yulans.cn/?p=868</guid>
		<description><![CDATA[当我们用url重写的时候，用base标签可以使页面中的链接、图片地址等网址相关的元素基于我们设置的链接，如下 但是发现在ie6下页面中的图片都不出来了，这是ie的一个bug，解决方案: 1）base的href属性使用完整的路径,如使用，不使用 2）base标签和标签之间不要有任何html标签]]></description>
			<content:encoded><![CDATA[<p>当我们用url重写的时候，用base标签可以使页面中的链接、图片地址等网址相关的元素基于我们设置的链接，如下<br />
<base href="/ctx/" /></p>
<p>但是发现在ie6下页面中的图片都不出来了，这是ie的一个bug，解决方案:<br />
1）base的href属性使用完整的路径,如使用<base href="http://yulans.cn/item/" />，不使用<base href="/item/" /><br />
2）base标签和</head>标签之间不要有任何html标签</p>
]]></content:encoded>
			<wfw:commentRss>http://yulans.cn/%e5%89%8d%e7%ab%af%e8%ae%be%e8%ae%a1/html%e7%9a%84base%e6%a0%87%e7%ad%be%e5%9c%a8ie6%e4%b8%8b%e7%9a%84%e9%97%ae%e9%a2%98%e8%a7%a3%e5%86%b3/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>nginx 使用 ssl</title>
		<link>http://yulans.cn/nginx/nginx-%e4%bd%bf%e7%94%a8-ssl</link>
		<comments>http://yulans.cn/nginx/nginx-%e4%bd%bf%e7%94%a8-ssl#comments</comments>
		<pubDate>Fri, 30 Sep 2011 03:46:31 +0000</pubDate>
		<dc:creator>cmpan</dc:creator>
				<category><![CDATA[Nginx]]></category>

		<guid isPermaLink="false">http://yulans.cn/?p=866</guid>
		<description><![CDATA[使用如下命令并根据提示输入信息，生成证书 cd /usr/local/nginx/conf openssl genrsa -des3 -out localhost.key 1024 #创建密钥 openssl req -new -key localhost.key -out localhost.csr openssl rsa -in localhost.key -out localhost_nopass.key # 生成浏览器浏览网页时不需要输入密码的密钥 openssl x509 -req -days 365 -in localhost.csr -signkey localhost.key -out localhost.crt #生成证书 在nginx的server配置中加如下配置使用证书 ssl on; ssl_certificate localhost.crt; ssl_certificate_key localhost_nopass.key; 现在生成的证书是不受信任的，如果需要受信任的证书，需要证书颁发机构颁发（需要用钱解决）。 免费的证书颁发机构：http://www.startssl.com/ 较低版本的ie浏览器可能不支持。]]></description>
			<content:encoded><![CDATA[<p>使用如下命令并根据提示输入信息，生成证书<br />
cd /usr/local/nginx/conf </p>
<p>openssl genrsa -des3 -out localhost.key 1024  #创建密钥<br />
openssl req -new -key localhost.key -out localhost.csr<br />
openssl rsa -in localhost.key -out localhost_nopass.key  # 生成浏览器浏览网页时不需要输入密码的密钥<br />
openssl x509 -req -days 365 -in localhost.csr -signkey localhost.key -out localhost.crt  #生成证书</p>
<p>在nginx的server配置中加如下配置使用证书</p>
<p>        ssl on;<br />
        ssl_certificate localhost.crt;<br />
        ssl_certificate_key localhost_nopass.key;</p>
<p>现在生成的证书是不受信任的，如果需要受信任的证书，需要证书颁发机构颁发（需要用钱解决）。<br />
免费的证书颁发机构：http://www.startssl.com/ 较低版本的ie浏览器可能不支持。</p>
]]></content:encoded>
			<wfw:commentRss>http://yulans.cn/nginx/nginx-%e4%bd%bf%e7%94%a8-ssl/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>最丑陋的PHP命名空间</title>
		<link>http://yulans.cn/php/%e6%9c%80%e4%b8%91%e9%99%8b%e7%9a%84php%e5%91%bd%e5%90%8d%e7%a9%ba%e9%97%b4</link>
		<comments>http://yulans.cn/php/%e6%9c%80%e4%b8%91%e9%99%8b%e7%9a%84php%e5%91%bd%e5%90%8d%e7%a9%ba%e9%97%b4#comments</comments>
		<pubDate>Thu, 29 Sep 2011 05:12:29 +0000</pubDate>
		<dc:creator>cmpan</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[实践经验]]></category>

		<guid isPermaLink="false">http://yulans.cn/?p=863</guid>
		<description><![CDATA[PHP5.3加入了命名空间，很高兴有了这个特性，把自己写的框架改成使用命名空间的，发现很不爽。 写了个没有命名空间的函数 function myFnc(){} 调用的时候如果页面里声明了命名空间，就必须用 \myFnc() 来调用。 写了一个没有命名空间的类 class MyClass {} 调用的时候如果页面里声明了命名空间，就必须用 new \MyClass() 来调用。 为什么声明了命名空间就不能直接用 myFnc()、new MyClass() 类调用全局函数和类呢？ 万恶之源在于画蛇的时候添了一足。 那条足在哪呢？ 就是相对命名空间 我定义了如下命名空间 namespace sp\A; namespace sp\A\B; namespace sp\A\B\C; 在namespace sp\A;中，我可以使用 use B\ClassName、use B\C\ClassName来使用命名空间 namespace sp\A\B和namespace sp\A\B\C下的类，这是使用相对命名空间来访问命名空间，这造成混乱不少。 有相对就会有绝对，而绝对访问方式是： use \spA\B\ClassName; use \sp\A\B\C\ClassName; 看见use中的开头的反斜杠“\”了吗？他就是万恶之源中的万恶之源。 为什么一定要搞个相对命名空间出来而不统一直接用 use spA\B\ClassName; use sp\A\B\C\ClassName; 如果这样我们没有命名空间的类可以直接用 new MyClass()，而不用 new \MyClass()， 调用没有命名空间的函数就可以直接用 muFnc()，而不必用 [...]]]></description>
			<content:encoded><![CDATA[<p>PHP5.3加入了命名空间，很高兴有了这个特性，把自己写的框架改成使用命名空间的，发现很不爽。</p>
<p>写了个没有命名空间的函数 function myFnc(){}<br />
调用的时候如果页面里声明了命名空间，就必须用 \myFnc() 来调用。<br />
写了一个没有命名空间的类 class MyClass {}<br />
调用的时候如果页面里声明了命名空间，就必须用 new \MyClass() 来调用。</p>
<p>为什么声明了命名空间就不能直接用 myFnc()、new MyClass() 类调用全局函数和类呢？<br />
万恶之源在于画蛇的时候添了一足。<br />
那条足在哪呢？<br />
就是相对命名空间</p>
<p>我定义了如下命名空间<br />
namespace sp\A;<br />
namespace sp\A\B;<br />
namespace sp\A\B\C;</p>
<p>在namespace sp\A;中，我可以使用 use B\ClassName、use B\C\ClassName来使用命名空间 namespace sp\A\B和namespace sp\A\B\C下的类，这是使用相对命名空间来访问命名空间，这造成混乱不少。<br />
有相对就会有绝对，而绝对访问方式是：<br />
use \spA\B\ClassName;<br />
use \sp\A\B\C\ClassName;</p>
<p>看见use中的开头的反斜杠“\”了吗？他就是万恶之源中的万恶之源。</p>
<p>为什么一定要搞个相对命名空间出来而不统一直接用<br />
use spA\B\ClassName;<br />
use sp\A\B\C\ClassName;</p>
<p>如果这样我们没有命名空间的类可以直接用 new MyClass()，而不用 new \MyClass()，<br />
调用没有命名空间的函数就可以直接用 muFnc()，而不必用 \myFnc()</p>
<p>画蛇何必添足呢！</p>
]]></content:encoded>
			<wfw:commentRss>http://yulans.cn/php/%e6%9c%80%e4%b8%91%e9%99%8b%e7%9a%84php%e5%91%bd%e5%90%8d%e7%a9%ba%e9%97%b4/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP数据类型隐性转换的陷阱</title>
		<link>http://yulans.cn/php/php%e6%95%b0%e6%8d%ae%e7%b1%bb%e5%9e%8b%e9%9a%90%e6%80%a7%e8%bd%ac%e6%8d%a2%e7%9a%84%e9%99%b7%e9%98%b1</link>
		<comments>http://yulans.cn/php/php%e6%95%b0%e6%8d%ae%e7%b1%bb%e5%9e%8b%e9%9a%90%e6%80%a7%e8%bd%ac%e6%8d%a2%e7%9a%84%e9%99%b7%e9%98%b1#comments</comments>
		<pubDate>Fri, 16 Sep 2011 21:45:04 +0000</pubDate>
		<dc:creator>cmpan</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[实践经验]]></category>

		<guid isPermaLink="false">http://yulans.cn/?p=856</guid>
		<description><![CDATA[之前写过一篇《PHP的动态特性》总结了部分PHP的特性，因为动态语言的特性，我们使用PHP时倍感便利，但是便利的同时会引来一些陷阱，不得不防。 我这里说的是php5+上跑的，php4的请飘过。 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149先把错误报告打开，以防看不到错误信息 &#60;?php error_reporting&#40;E_ALL&#41;; ini_set&#40;'display_errors', true&#41;; ?&#62; 根据php manual 中 http://www.php.net/manual/zh/language.operators.comparison.php “Comparison Operators” 一章的说明可知，number 和string进行比较的时候，会先将string类 型首先转化为number，然后再进行比较操作。 1.类型自动转换为数组 当我们把一个非数组的变量当做数组来调用的时候，该变量在调用时数据类型临时自动转换成数组。 例如如下代码： &#60;?php $str = 'string'; var_dump&#40;$str&#91;'aaa'&#93;&#41;; &#160;// string(1) &#34;s&#34; var_dump&#40;$str&#41;; &#160;// string(6) &#34;string&#34; if&#40;$str&#91;'aaa'&#93; === $str&#91;0&#93;&#41; &#123; &#160; &#160; print &#34;===&#34;; &#125; ?&#62; 如下例子可以明显的看出下标类型自动转换在发生。 &#60;?php &#160; $link = '&#60;a href=&#34;http://yulans.cn&#34;&#62;yulans&#60;/a&#62;'; &#160; $key = '1-10'; &#160; echo [...]]]></description>
			<content:encoded><![CDATA[<p>      之前写过一篇《PHP的动态特性》总结了部分PHP的特性，因为动态语言的特性，我们使用PHP时倍感便利，但是便利的同时会引来一些陷阱，不得不防。<br />
<span id="more-856"></span><br />
      我这里说的是php5+上跑的，php4的请飘过。</p>
<div class="codecolorer-container php mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:680px;height:auto;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br />46<br />47<br />48<br />49<br />50<br />51<br />52<br />53<br />54<br />55<br />56<br />57<br />58<br />59<br />60<br />61<br />62<br />63<br />64<br />65<br />66<br />67<br />68<br />69<br />70<br />71<br />72<br />73<br />74<br />75<br />76<br />77<br />78<br />79<br />80<br />81<br />82<br />83<br />84<br />85<br />86<br />87<br />88<br />89<br />90<br />91<br />92<br />93<br />94<br />95<br />96<br />97<br />98<br />99<br />100<br />101<br />102<br />103<br />104<br />105<br />106<br />107<br />108<br />109<br />110<br />111<br />112<br />113<br />114<br />115<br />116<br />117<br />118<br />119<br />120<br />121<br />122<br />123<br />124<br />125<br />126<br />127<br />128<br />129<br />130<br />131<br />132<br />133<br />134<br />135<br />136<br />137<br />138<br />139<br />140<br />141<br />142<br />143<br />144<br />145<br />146<br />147<br />148<br />149<br /></div></td><td><div class="php codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">先把错误报告打开，以防看不到错误信息<br />
<span style="color: #000000; font-weight: bold;">&lt;?php</span><br />
<span style="color: #990000;">error_reporting</span><span style="color: #009900;">&#40;</span><span style="color: #009900; font-weight: bold;">E_ALL</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #990000;">ini_set</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'display_errors'</span><span style="color: #339933;">,</span> <span style="color: #009900; font-weight: bold;">true</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #000000; font-weight: bold;">?&gt;</span><br />
<br />
根据php manual 中 http://www.php.net/manual/zh/language.operators.comparison.php <br />
“Comparison Operators” 一章的说明可知，number 和string进行比较的时候，会先将string类<br />
型首先转化为number，然后再进行比较操作。<br />
<br />
1.类型自动转换为数组<br />
当我们把一个非数组的变量当做数组来调用的时候，该变量在调用时数据类型临时自动转换成数组。<br />
例如如下代码：<br />
<span style="color: #000000; font-weight: bold;">&lt;?php</span><br />
<span style="color: #000088;">$str</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">'string'</span><span style="color: #339933;">;</span><br />
<span style="color: #990000;">var_dump</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$str</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'aaa'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> &nbsp;<span style="color: #666666; font-style: italic;">// string(1) &quot;s&quot;</span><br />
<span style="color: #990000;">var_dump</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$str</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> &nbsp;<span style="color: #666666; font-style: italic;">// string(6) &quot;string&quot;</span><br />
<br />
<span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$str</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'aaa'</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">===</span> <span style="color: #000088;">$str</span><span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">0</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">print</span> <span style="color: #0000ff;">&quot;===&quot;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span><br />
<span style="color: #000000; font-weight: bold;">?&gt;</span><br />
<br />
如下例子可以明显的看出下标类型自动转换在发生。<br />
<span style="color: #000000; font-weight: bold;">&lt;?php</span> &nbsp;<br />
<span style="color: #000088;">$link</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">'&lt;a href=&quot;http://yulans.cn&quot;&gt;yulans&lt;/a&gt;'</span><span style="color: #339933;">;</span> &nbsp;<br />
<span style="color: #000088;">$key</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">'1-10'</span><span style="color: #339933;">;</span> &nbsp;<br />
<span style="color: #b1b100;">echo</span> <span style="color: #0000ff;">&quot;<span style="color: #006699; font-weight: bold;">$link</span>[<span style="color: #006699; font-weight: bold;">$key</span>]<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">;</span> &nbsp;<span style="color: #666666; font-style: italic;">// 同 $link[1]</span><br />
<span style="color: #b1b100;">echo</span> <span style="color: #0000ff;">&quot;{<span style="color: #006699; font-weight: bold;">$link</span>[<span style="color: #006699; font-weight: bold;">$key</span>]}<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">;</span> &nbsp;<span style="color: #666666; font-style: italic;">// 同 $link[1]</span><br />
<span style="color: #666666; font-style: italic;">//echo &quot;$link['$key']\n&quot;; &nbsp;// 报错</span><br />
<span style="color: #b1b100;">echo</span> <span style="color: #0000ff;">&quot;{<span style="color: #006699; font-weight: bold;">$link</span>['<span style="color: #006699; font-weight: bold;">$key</span>']}<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">;</span> &nbsp;<span style="color: #666666; font-style: italic;">// 同 $link[0]</span><br />
<span style="color: #000000; font-weight: bold;">?&gt;</span> &nbsp;<br />
<br />
这里字符串在 var_dump($str['aaa']) 被临时转换成了数组 array('s','t','r','i', 'n','g')，而用关联数组方式<br />
$str['aaa']读取索引数组的值，关联数组的下标'aaa'将被转换成整形下标，<br />
因而在这里的$str['aaa']全等于$str[0]。<br />
<br />
其他数据类型隐性转换成数组也隐藏有陷阱，一般都不是报出undefined index错误。举例如下代码：<br />
<span style="color: #000000; font-weight: bold;">&lt;?php</span><br />
<span style="color: #009933; font-style: italic;">/**<br />
&nbsp;* 测试变量隐性转换成数组<br />
&nbsp;*<br />
&nbsp;* @param mixed $param<br />
&nbsp;*/</span><br />
<span style="color: #000000; font-weight: bold;">function</span> test2Arr<span style="color: #009900;">&#40;</span><span style="color: #000088;">$param</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #990000;">var_dump</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$param</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'abc'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span><br />
<br />
test2Arr<span style="color: #009900;">&#40;</span><span style="color: #009900; font-weight: bold;">false</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// NULL</span><br />
test2Arr<span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">123</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// NULL</span><br />
test2Arr<span style="color: #009900;">&#40;</span><span style="color:#800080;">123.456</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// NULL</span><br />
test2Arr<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'string'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// string(1) &quot;s&quot;</span><br />
test2Arr<span style="color: #009900;">&#40;</span><span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'abc'</span><span style="color: #339933;">=&gt;</span><span style="color: #0000ff;">'text'</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> &nbsp;<span style="color: #666666; font-style: italic;">// string(4) text</span><br />
test2Arr<span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">new</span> ArrayObject<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// Notice: undefined index: abc</span><br />
<br />
<span style="color: #000000; font-weight: bold;">?&gt;</span><br />
<br />
解决办法：<br />
函数参数数据类型是数组的时候，防止用户输入字符串导致错误<br />
如下例子，当添加用户的时候，我们要求用户必须输入用户名。没有哪个SB把要求是数组的参数传入<br />
字符串，但是防人之心不可无，说不定我连续工作超过十几个小时后一不小心就成那个SB了，又或许<br />
某人想绕过代码执行操作。<br />
<span style="color: #000000; font-weight: bold;">&lt;?php</span><br />
<span style="color: #009933; font-style: italic;">/**<br />
&nbsp;* 添加用户(错误的写法)<br />
&nbsp;* <br />
&nbsp;* @param array $user<br />
&nbsp;*/</span><br />
<span style="color: #000000; font-weight: bold;">function</span> addUser<span style="color: #009900;">&#40;</span><span style="color: #000088;">$user</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span><span style="color: #990000;">empty</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$user</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'name'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span> &nbsp;<span style="color: #666666; font-style: italic;">// 这里当输入类型是不为空的字符串的时候会出错，</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #b1b100;">echo</span> <span style="color: #0000ff;">&quot;用户名必填<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #b1b100;">return</span> <span style="color: #009900; font-weight: bold;">false</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #666666; font-style: italic;">// do sth.</span><br />
<br />
&nbsp; &nbsp; <span style="color: #b1b100;">echo</span> <span style="color: #0000ff;">&quot;测试<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #b1b100;">return</span> <span style="color: #009900; font-weight: bold;">true</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span><br />
<br />
<span style="color: #009933; font-style: italic;">/**<br />
&nbsp;* 添加用户(正确的写法)<br />
&nbsp;* <br />
&nbsp;* @param array $user<br />
&nbsp;*/</span><br />
<span style="color: #000000; font-weight: bold;">function</span> addUser2<span style="color: #009900;">&#40;</span><span style="color: #000088;">$user</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span><span style="color: #990000;">is_array</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$user</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">||</span> <span style="color: #990000;">empty</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$user</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'name'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #b1b100;">echo</span> <span style="color: #0000ff;">&quot;用户名必填<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #b1b100;">return</span> <span style="color: #009900; font-weight: bold;">false</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #666666; font-style: italic;">// do sth.</span><br />
<br />
&nbsp; &nbsp; <span style="color: #b1b100;">echo</span> <span style="color: #0000ff;">&quot;测试<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #b1b100;">return</span> <span style="color: #009900; font-weight: bold;">true</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span><br />
<br />
<span style="color: #000088;">$user</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">'xiaoxiao'</span><span style="color: #339933;">;</span><br />
addUser<span style="color: #009900;">&#40;</span><span style="color: #000088;">$user</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
addUser2<span style="color: #009900;">&#40;</span><span style="color: #000088;">$user</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
<br />
<span style="color: #000000; font-weight: bold;">?&gt;</span><br />
<br />
<br />
2.纯数字字符串比较时自动转换成整形超过范围时发生溢出<br />
<span style="color: #000000; font-weight: bold;">&lt;?php</span><br />
<span style="color: #000088;">$x1</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">'111111111111111111'</span><span style="color: #339933;">;</span><br />
<span style="color: #000088;">$x2</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">'111111111111111112'</span><span style="color: #339933;">;</span><br />
<br />
<span style="color: #b1b100;">echo</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$x1</span> <span style="color: #339933;">===</span> <span style="color: #000088;">$x2</span><span style="color: #009900;">&#41;</span> ? <span style="color: #0000ff;">&quot;true&quot;</span> <span style="color: #339933;">:</span> <span style="color: #0000ff;">&quot;false&quot;</span><span style="color: #339933;">;</span> &nbsp;<span style="color: #666666; font-style: italic;">// false 如我们所愿，这两个字符串确实不一样。</span><br />
<span style="color: #b1b100;">echo</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$x1</span> <span style="color: #339933;">==</span> <span style="color: #000088;">$x2</span><span style="color: #009900;">&#41;</span> ? <span style="color: #0000ff;">&quot;true&quot;</span> <span style="color: #339933;">:</span> <span style="color: #0000ff;">&quot;false&quot;</span><span style="color: #339933;">;</span> &nbsp; &nbsp;<span style="color: #666666; font-style: italic;">// true 这里被偷偷的转换类型了，</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span style="color: #666666; font-style: italic;">// 成了 echo (intval($x1) == intval($x2)) ? &quot;true&quot; : &quot;false&quot;; 整形溢出</span><br />
<br />
<span style="color: #000000; font-weight: bold;">?&gt;</span><br />
<br />
<br />
3、整形和字符串比较时数据类型隐性转换有可能发生问题<br />
<span style="color: #000000; font-weight: bold;">&lt;?php</span><br />
<span style="color: #000088;">$number</span> <span style="color: #339933;">=</span> <span style="color: #cc66cc;">0</span><span style="color: #339933;">;</span><br />
<span style="color: #000088;">$string</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">'text'</span><span style="color: #339933;">;</span> <br />
<br />
<span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$number</span> <span style="color: #339933;">==</span> <span style="color: #000088;">$string</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">print</span> <span style="color: #0000ff;">&quot;true&quot;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">print</span> <span style="color: #0000ff;">&quot;false&quot;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span><br />
<br />
<span style="color: #000000; font-weight: bold;">?&gt;</span><br />
<br />
很遗憾这里输出的是 true<br />
我们知道 $number === $string 肯定是false，手册上说 === 是比较值&amp;&amp;数据类型，而用 == 只是比较值，<br />
$number == $string 这里不是比较值吗？ '0' 和 'text' 明显不一样啊。小心了，这里的$string是<br />
先被秘密转成和$number一样的整形再比较的，$number == (int)$string的确是true<br />
<br />
<br />
4. in_array 小陷阱<br />
<span style="color: #000000; font-weight: bold;">&lt;?php</span><br />
<br />
<span style="color: #990000;">var_dump</span><span style="color: #009900;">&#40;</span><span style="color: #990000;">in_array</span><span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">0</span><span style="color: #339933;">,</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'s'</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> &nbsp;<span style="color: #666666; font-style: italic;">// true</span><br />
<br />
<span style="color: #000000; font-weight: bold;">?&gt;</span><br />
因为in_array会将0 和's' 进行比较，0是number类型，'s'是string类型， 's'转化为number的结果为0，<br />
而0 == 0 的结果是true，所以in_array(0, array('s', 'ss'))的结果也是true<br />
<br />
如果把in_array 的第三个参数strict设置为 true，比较的时候 就会判断值和类型是否都相当。<br />
如果都相当的话，才会返回true，否则返回false.</div></td></tr></tbody></table></div>
]]></content:encoded>
			<wfw:commentRss>http://yulans.cn/php/php%e6%95%b0%e6%8d%ae%e7%b1%bb%e5%9e%8b%e9%9a%90%e6%80%a7%e8%bd%ac%e6%8d%a2%e7%9a%84%e9%99%b7%e9%98%b1/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

