★★★本博客欢迎转载,但请注明版权、原文链接,谢谢。
Memento..
My stories in my way..
posts - 39,comments - 591,trackbacks - 13
摘要: Azure Services Platform入门专题受到了越来越多人的关注,并被网络上许多网站进行转载。作者感到非常荣幸。目前这套入门教程已基本成型,我将它做成索引便于大家查阅。对 IT168 等网站无视国家法律未经作者许可擅自篡改版权进行转载的龌龊侵权行为表示最深的鄙视。阅读全文
posted @ 2009-04-17 16:59 流牛木马 阅读(6532) 评论(13) 编辑
摘要: 相信很多“牛人”都被一些宅男学弟问过这个问题:学长,我作为一个初学程序的菜鸟,要怎么样才能提交自己的编程水平,变成牛人呢?本文就以“统计Dota玩什么英雄最容易拿MVP”为例,做一次抛砖引玉。阅读全文
posted @ 2011-12-31 12:49 流牛木马 阅读(2123) 评论(14) 编辑
摘要: 三年前,一位叫郑昀的高手发布社会化推荐产品“玩聚SR”。 我们并不是在探讨如何简单地复制郑昀以前的工作,而是探讨如何站在巨人的肩膀上,把社会化推荐引擎做得更好、更全面、更讨用户喜欢(预览地址http://dullcat.com)。阅读全文
posted @ 2011-11-30 02:01 流牛木马 阅读(1664) 评论(17) 编辑

Chrome 浏览器现在可谓如日中天,与 Firefox 共同蚕食着 IE 的市场,网页开发者们可能也会开始转向这一新兴的浏览器,来调校自己的网站。而随着 Chrome 插件作者们的介入,为网页开发者提供便利的插件也纷纷崭露头角。

1. Firebug Lite

firebuglite

Firebug 绝对是最受网页开发者喜爱的 Fifefox 插件(没有之一)。通过它可以在浏览器中查看并编辑任何网页的 HTML,CSS 以及 JavaScript,并在修改的同时地看到改动后的效果。Firebug Lite 是针对 Chrome 浏览器的一个精简版本,可以用来查找网页中的错误以便快速更正。

尽管 Firebug Lite 尚不能包括 Firebug 所有功能,但也已经秉承其精粹,且已经提供了针对高级用户控制台界面。

2. IE Tab

ietab

微软的 Internet Explorer 浏览器会让很多网页设计者头痛,但目前为止在大众范围内仍是被使用最多的浏览器。IE Tab 扩展实际上是在 Chrome 的一个标签页里用 IE 打开网页,通过它你可以查看网页在 IE 用户的眼里是否显示正常。

3. Eye Dropper

eyedropper

通过 EyeDropper,可以查看 Chrome 浏览的网页中任意颜色的信息。点击扩展的按钮,会打开一个下拉式的色轮界面,其中有一个取色器按钮(Pick color from webpage),点击它,再点击网页中任何位置,就可以查看所点击位置像素的颜色在色轮中的位置、RGB 水平以及 HTML 颜色代码。

4. Chrome SEO

measureit

安装好该插件后点击插件按钮就会显示当前网站与搜索引擎优化相关的信息,包括反向链接,Alexa排名、Google PageRank排名等流量值数,在社会化书签网站中的热门程度等。

5. Lorem Ipsum Generator

loremipsum

Lorem Ipsum Generator 扩展可以用来在设计网页时生成填充网页内容的文本,用以调适格式,免去手动输入此类文字浪费的时间。该插件十分小巧,在易于使用的同时也不会占用过多的内存,在需要时可以信手拈来。

6. Resolution Test

resolutiontest

顾名思义,Resolution Test 就是通过调整浏览器的大小来查看网站在不同的常见分辨率下的情况。作为网页设计者一般都会配备高分辨率的显示器,但一般的网页访问者来说通常都不会这么奢侈,这个扩展的作用就是用来确保网站的格式在这部分人看来也是正常的。

7. Speed Tracer

speedtracer

Speed Tracer 通过浏览器内建的测量工具来记录网页应用在执行不同任务时所用的时间,以便于开发者找到致使网站运行缓慢的原因。该插件可以显示浏览器在解读执行布局、Javascript时所有的时间以及其它一些细节。

这个插件唯一的缺点在于,必须使用“–enable-extension-timeline-api”这个参数来启动 Chrome 浏览器才能让其正常运作。但既然是网站开发者,这点麻烦就是小菜一碟了,对吧?

8. MeasureIt!

measureit

MeasureIt! 名字就很直观。它可以显示出所浏览网页中任意元素的尺寸(像素宽度和高度)。和这个列表中很多插件一样,这个插件也有 Firefox 版本。

9. 1-Click Web Proxy

 

 

 地址: https://chrome.google.com/extensions/detail/kjdehhkgdgjcekacdccoflccmhbkefce

这个插件应该是国人做的。因为她的中文名字是——  一键***!

她已经连续一个月排到Chrome Extension 官方Top Rated的第7名了~ 囧~ 看来还真是迎合国民的需求啊~

真相在这里:https://chrome.google.com/extensions/list/rating 

做IT的经常去国外网站查资料,遇到打不开的网页,比如google groups 、blogspot等,只需要点击这个插件的按钮就可以了。速度还挺不错。

 

 

10. Pendule

pendule

Pendule 会弹出一个简单易用且设计精美的控制面板,其中包含了很多对于开发者来说很有帮助的任务选项。比如重新载入或是禁用 CSS,查看 JavaScript 代码,隐藏图片,取色器,测量尺,查看页面源代码,以及多种编码验证工具等。是一个很不错的功能基础且全面的开发者用扩展。

11. BuiltWith

builtwith

BuiltWith 可以给出关于当前网页的一个资料信息,列出了网站中所使用的相关技术。它会显示网页中所使用的小挂件、网站管理员所用的网站流量分析追踪服务、网页所使用的框架以及广告平台等等。

posted @ 2010-09-29 00:21 流牛木马 阅读(37179) 评论(17) 编辑

    在实际建立工作流模型时,往往会有很多相似步骤。相似步骤间的逻辑,往往又会包括大量的if..else..判断。

    例如,我们先看一个简单的报销流程。它很可能会是这样的:

 

image

 

     看似很简单的四个步骤。但客户会告诉我们: 部门经理应该分为两个步骤,因为有些部门设立了“副经理”职位,有些部门甚至“经理”职位空缺,只设“副经理”。换言之,“经理”与“副经理”至少有一个。

     于是我们将流程图改为以下情况:

image

 

     我们发现,增加了一个步骤后,需要增加四条“流程路径”。例如,按照途中的说明,当流程以及流转到了“部门副经理”时,取得下一个步骤需要做一次“if…else…”。如果当前部门有“部门经理”,则下一步流转到“部门经理”步骤;如果当前部门没有“部门经理”,则直接流转到“会计”步骤。

     这似乎能够满足客户的要求。但客户的组织机构往往非常复杂,他很可能在某一个时刻又告诉我们:我们共有两级部门,一共四个经理职位,均需要按照上述方式进行流转。

     这是非常清晰明了的一个需求。于是我们又继续修改流程模型为:

image

    此时我们就陷入了困境。这新增的两个步骤,带来了更多的逻辑关系,且每个逻辑关系都更为复杂。

    例如,假如当前流程以及流转到了“二级部门副经理”处,工作流平台取得下一步骤需要做的判断至少有:

  1. 如果该部门有“二级部门经理”,则流转到“二级部门经理”;
  2. 如果该部门没有“二级部门经理”,但有“一级部门副经理”,则流转到“一级部门副经理”;
  3. 如果该部门没有“二级部门经理”,也没有“一级部门副经理”,但有“一级部门经理”,则流转到“一级部门经理”
  4. 如果该部门没有“二级部门经理”,也没有“一级部门副经理”,也没有“一级部门经理”,则流转到“会计”步骤

     可以看到,仅仅“二级部门副经理”这一个步骤的“选择下一步骤”逻辑,就包含了4个“if…else…”判断,这是非常丑陋的。开发组往往兵强马壮,此种情况也可以通过大量的人工来保证。不过,可以预计的是,客户的多种流程(如报销、列账、冲销、预付等),都有类似的部门经理逻辑。大量的重复工作,无法保证不出错。

     更为恐怖的是,假如客户有一天,变成了三个层级的部门组织架构了呢? 从“三级部门副经理”开始,一直往上,到“会计”步骤,那是不是仍然采用这样简单粗暴的方式? 再按照这种方式来做的话,也许我们就直接崩溃了。

     怎么办呢?

     其实,在GoF的23个设计模式中,就已经提到了一个经典的解决方案:职责链模式

职责链(Chain of Responsibility)模式
    责任链模式是一种对象的行为模式【GOF95】。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织链和分配责任。

 

职责链模式的UML图:

_OEZ3HQO2}~EI1E94FEE1DT

 

这个UML图非常有意思。我们可以注意到,Handler角色自己聚合成自己。这有点类似于《数据结构》里描述的“链式结构”。

 

抽象处理者(Handler)角色:定义出一个处理请求的接口。如果需要,接口可以定义出一个方法,以设定和返回对下家的引用。这个角色通常由一个抽象类或接口实现。

具体处理者(ConcreteHandler)角色:具体处理者接到请求后,可以选择将请求处理掉,或者将请求传给下家。由于具体处理者持有对下家的引用,因此,如果需要,具体处理者可以访问下家。

 

     我们将这个设计模式运用到刚才所提到的实际环境中。

     这时,我们的“抽象处理者”(Handler)是一个能够返回下一步步骤名称的处理类。对于上面提到的“二级部门”的情况,我们将会有五个“具体处理者”(ConcreteHandler):二级部门副经理、二级部门经理、一级部门副经理、一级部门经理、会计。 他们属于两个类别:“经理类”与“会计类”。

     “会计类”的处理无逻辑判断。即,当步骤选择器流入“会计类”时,直接返回“会计类”的步骤名。

     “经理类”就复杂一些,需要判断当前部门各个层级经理的有无、各个层级经理的是否已经审批。这两种判断可以抽象为两个数组参数的比较。我们设定两个数组参数byte[] candidates与byte[] approvers。第一个数组表示,候选步骤的有与无;第二个数字表示,在数据库记录中,所有步骤的当前审批状态.

     例如,假如一个部门有“二级部门副经理、二级部门经理、一级部门经理”,没有“一级部门副经理”,即所有职位从小到大排列,第三大的职位空缺,其余职位齐全——则byte[] candidates的值可以设为{1,1,0,1}。在流程刚刚开始时,所有步骤均未审批,byte[] approvers值为{0,0,0,0};假如当前流程已经流转到“二级部门副经理”步骤,则byte[] approvers可以的值为{1,0,0,0}。

     业务规则有了,参数有了,具体该如何处理呢?其实就是比较两个数组中相同index对应值的相等与否。

     例如,假设当前 candidates为{1,1,0,1},approvers为{1,0,0,0}。从数组索引0开始,依次往后,我们可以知道“需要”“二级部门副经理”审批,当前“二级部门副经理”已经审批;“需要”“二级部门经理”审批,当前“二级部门经理”还未审批。很明显,流程下一步应该流转到“二级部门经理”步骤。

     以上的自然语言描述翻译为算法为:按照索引从小到大的顺序,依次比较candidates数组与approvers数组的元素值是否相等;如果相等,跳到下一个索引处继续比较;如果不等,则返回当前索引所对照的步骤名称。

     按照我们设计好的“职责链模式”实现这个整体算法。即:每个“经理”处理者类,只判断属于自己索引的两数组元素,如果相等,则“处理”(返回步骤名);如果不等,则移交下家进行后续判断。

      首先构造处理者基类:

 

处理者基类
   //处理者基类
    abstract class Handler
    {
       
protected int dutyId;//处理者对应的数据键值
        protected string stepName;//处理者对应步骤的名称
        protected Handler successor;//后继处理者

       
public void SetSuccessor(Handler successor)
        {
           
this.successor = successor;
        }

       
//处理任务的抽象方法,必须在每个子类进行Override
        abstract public string HandleRequest(byte[] candidates, byte[] approvers);
    }

 

 

 

   构造两个类型的处理者子类:经理子类,会计子类。

 

具体处理者
   /// <summary>
   
/// 会计处理者
   
/// </summary>
    class Accounting : Handler
    {
       
//会计处理者需要赋予“流程步骤名”
        public Accounting(string _stepName)
        {
            stepName
= _stepName;
        }

       
//当职责链到达会计步骤时,直接进入会计步骤
        override public string HandleRequest(byte[] candidates, byte[] approvers)
        {
           
return stepName;
        }
    }

   
/// <summary>
   
/// 经理处理者
   
/// </summary>
    class Manager : Handler
    {
       
//经理处理者需要赋予“流程步骤名”与对应的“数据键值”
        public Manager(int _dutyId, string _stepName)
        {
            dutyId
= _dutyId;
            stepName
= _stepName;
        }
       
override public string HandleRequest(byte[] candidates, byte[] approvers)
        {
          //如果两个值不相等,说明这个步骤还未处理过。立即返回该步骤的名称。
           
if (candidates[dutyId] != approvers[dutyId])
               
return stepName;
           
else
               
if (successor != null)
                   
return successor.HandleRequest(candidates, approvers);
               
else
                   
throw new ArgumentNullException("没有指定successor!");
        }
    }

 

    客户端:

客户端(示例)
  //客户端
    public class StepSelector
    {
       
//假设初始情况是:有二级部门副经理、有二级部门经理、
       
//无一级部门副经理、有一级部门经理
        byte[] candidates;

       
//初始时,所有人均为审批
        byte[] approvers;

       
//传入工作流平台的任务Id,初始化当前状态
        StepSelector(string taskId)
        {
           
//◇到业务平台中取得所有的候选步骤
            candidates = GetCandidates(taskId);

           
//例如,初始情况可能是这样的:有二级部门副经理、有二级部门经理、
           
//无一级部门副经理、有一级部门经理。
           
//则设置如下:
           
//candidates = new byte[] { 1, 1, 0, 1 };

           
//◇到流程平台中取得当前已经完成的步骤
            approvers = GetApprovers(taskId);

           
//初始时,所有人均为审批
           
//approvers = new byte[] { 0, 0, 0, 0 };

        }

       
//客户端主要方法: 传入整体数据、当前状态,取得下一步骤的名称
        public string GetNextStep(byte[] candidates, byte[] approvers)
        {
            Manager LevelTwoViceManager
= new Manager(0, "二级部门副经理审批");
            Manager LevelTwoManager
= new Manager(1, "二级部门经理审批");
            Manager LevelOneViceManager
= new Manager(2, "二级部门经理审批");
            Manager LevelOneManager
= new Manager(3, "二级部门经理审批");
            Accounting accounting
= new Accounting("公司会计审核");

            LevelTwoViceManager.SetSuccessor(LevelTwoManager);
            LevelTwoManager.SetSuccessor(LevelOneViceManager);
            LevelOneViceManager.SetSuccessor(LevelOneManager);
            LevelOneManager.SetSuccessor(accounting);

           
//从最低值为“二级部门副经理”开始处理请求
            return LevelTwoViceManager.HandleRequest(candidates, approvers);
        }
    }

 

    请注意“客户端”的代码,它完完全全地示例了职责链模式的调用方法。总体分为三步:1. 实例化处理者。2. 设置后继关系 3.从最前端的处理者开始处理。

    值得一提的是,这样的结构是符合“开放封闭原则”的(对修改封闭,对扩展开放)。也就是说,无论是需要两个部门经理来审批,还是四个部门经理来审批,甚至扩展到六个部门经理审批,都是不需要修改已有的业务逻辑代码,只需要在客户端中增加更多的处理者类实例、增加每个类的后继处理者、设置参数,就可以了。

ChainOfResponsibility

posted @ 2010-04-14 20:45 流牛木马 阅读(1461) 评论(4) 编辑

     两年前,微软亚洲研究院出版了《编程之美》一书,其中的第一章第一节“让CPU占用率曲线听你指挥”一文,可谓是在全国范围内掀起了一股玩转CPU占用率曲线热。我本人甚至在坐公交的时候都在思考如何折腾CPU占用率曲线。

     书中给出了这样一张让人耳目一新的图,吸引了万千IT少年:

       image

 

     也许您会觉得这还不够好玩。我也是。对了,今天流牛木马打算与各位看官讨论的,是以下这幅图的效果——双核的CPU,左手画圆,右手画方!

image

 

     该书出版的时候,我正在微软亚洲研究院创新工程组实习,该书的好几位作者都是我的好朋友,如李东、陈远等 ——于是我可以负责任地告诉大家,该书的所有实习生作者 ,当时他们使用的都是Intel P4 3.4Ghz单核CPU ! o(∩_∩)o

    今天突然想到了这里,就打算拿我的Thinkpad T60来试试。CPU是Genuine Intel(R) CPU T2400 @1.83Ghz , 双核。

    首先我将书中提供的C++代码翻译成了C# .翻译过程没任何技术难度,仅供各位看官了解个大概:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            const double SPLIT = 0.01;
            const int COUNT = 200;
            const double PI = 3.14159265;
            const int INTERVAL = 100;
            double[] busySpan = new double[COUNT];  //array of busy times
            double[] idleSpan = new double[COUNT];  //array of idle times
            int half = INTERVAL / 2;
            double radian = 0.0;
            for (int i = 0; i < COUNT; i++)
            {
                busySpan[i] = (double)(half + (Math.Sin(PI * radian) * half));
                idleSpan[i] = INTERVAL - busySpan[i];
                radian += SPLIT;
            }
            double startTime = 0;
            int j = 0;
            while (true)
            {
                j = j % COUNT;
                startTime = Environment.TickCount;
                while ((Environment.TickCount - startTime) <= busySpan[j]) ;
                System.Threading.Thread.Sleep((int)idleSpan[j]);
                j++;
            }
        }
    }
}

将这段代码直接F5, 运行结果如下:

1

    果然,与大家意料的一样,两个CPU中都会显示出非常扭曲、不规则的正弦曲线。

    按照书中的说法,C++程序可以使用WinAPI里的SetThreadAffinityMast()函数,将程序固定到某个特定的CPU上执行。

    查了下MSDN,当然,在.NET里,也有Manage的相似代码。见http://msdn.microsoft.com/zh-tw/library/system.diagnostics.process.processoraffinity(VS.80).aspx

    按照MSDN,在代码中加入了简单的两句:

     System.Diagnostics.Process p = System.Diagnostics.Process.GetCurrentProcess();             
          p.ProcessorAffinity = (IntPtr)0x0001;

    然后再次F5, CPU中的曲线如下图所示:

2

   1号CPU成规则的正弦曲线相当完美,2号CPU使用率一直为0.

   看来这就是书中想让大家达到的效果了。

 

   这个效果让人感觉非常枯燥乏味。2号CPU的资源就浪费掉了,有没有办法将它利用起来呢?

   让我们来分析这两句代码:

while ((Environment.TickCount - startTime) <= busySpan[j]) ;

System.Threading.Thread.Sleep((int)idleSpan[j]);

     它的意思是,在规定好的时间间隔内(程序中规定了是100毫秒),满负载运行(通过空循环)构成正弦函数需要运行的时间(由之前的计算得到),其余时间就睡觉。如运行69毫秒,睡觉31毫秒,那么在这100毫秒的时间段,CPU的平均占用率就是69%。由于CPU占用率曲线是1秒钟更新一次的,程序使用的每100毫秒一个平均值节点,曲线的平滑度足够了。

    继续发散。刚才的例子,如果是运行69毫秒,睡觉50毫秒呢? 那么当前的100毫秒CPU占用率仍然会是69%,但如果算法不加调整,仍然以100毫秒为一个平均值节点,这就影响到了下一个100毫秒的CPU占用率。前一个影响后一个,如多米诺骨牌一样,整体的曲线就完全错误了。

    也就是说,我们用来控制CPU平均占用率的时间段,每段必须相等。

    我们考虑到以上的代码中,在1号CPU中运行了69毫秒,剩下了31毫秒如果转到2号CPU中运行,1号CPU的占用就会降到0,但与此同时我们就可以在2号CPU中“画方”了。

    概括起来说就是,当1号CPU小寐的时候,程序在2号CPU中“画方”;1号CPU小寐结束的时候,2号CPU中当前周期的“方”必须完成。

    需要注意的是,假如1好CPU画了69毫秒,那么2号CPU一定只能画31毫秒。

    按照这个思路,我们先实验一下简单的:在1号CPU中画正弦曲线后的休息时间段,我们在2号CPU中画余弦曲线。呵呵,初中学的三角函数你忘了吗?

    我们将原代码中的空循环改为:

            while (true)
            {
                j = j % COUNT;
                p.ProcessorAffinity = (IntPtr)0x0001;
                startTime = Environment.TickCount;
                while ((Environment.TickCount - startTime) <= busySpan[j]) ;
                p.ProcessorAffinity = (IntPtr)0x0002;
                while ((Environment.TickCount - startTime) <= idleSpan[j]) ;
                j++;
            }

    “左手正弦,右手余弦”结果差强人意:

image

     我们注意到这个曲线没有之前单CPU运行时那么精确的。

     原因是我们切换CPU的运营占用了时间。例如,以前正弦画了69毫秒,我们计算出余弦应该画31毫秒,但切换CPU的操作可能占用了3毫秒(或更多、或每次耗时都不同),整体就不在100毫秒这个间隔范围了,所以曲线结果有误差。

 

    到这里,我的思路已经说完了,相信大家已经明白。

 

     不过, 走到这一步,追求完美的我们,的确是遇到了一点障碍。

     我经过一些周期上的微调,并且将余弦函数改为了简单的方波函数,最终效果正如本文开头展示的一样,还是不错的。

    调整办法?呵呵,真不好意思,我暂时还没有想到一个足够说服大家、可以公式化的调整办法,就请各位看官在这里八仙过海,各显神通咯~

 

image

posted @ 2010-03-24 00:50 流牛木马 阅读(3830) 评论(33) 编辑