分类目录归档:学习笔记

2013年个人总结

又到年末,依旧感叹时间的飞逝。隔了大半年,才这里写上一篇文章,而且是年度总结,感觉更是无从下笔。

如果按客观格式写点个人总结:

1)这一年做了什么事情?
2)上一年的总结的计划的事情进展的如何?
3)具体分析完成的事情或目标情况,如完成的百分比,有哪些偏差,有哪些好坏等等?
4)明年的计划和展望是什么?

可是,还是觉得难以表达,难以总结。

这一年,过得还是很「混沌」和「迷茫」,创业团队快两年了,还是处于探索期,在尝试不同的方向,团队成员有疲惫,失去信心的状态。我很是自责,在产品规划、设计和运营这方面经验很不够(我是一个技术能力马马虎虎的coder)。但非常感谢团队的信任!

上一年(2012年的个人总结)计划中,无一实现。但是这年感觉过的很忙很忙,停下来静思,却一点也不充实,无积累。

总之,能力(先产品设计,部门协调,市场营销等能力,这些能力也说得很泛)还很浅很浅,学习再学习,专注再专注。

2014年,2013过去,1314,瞄~

2014年,电子商务又会有一个怎样的发展?阿里双11一天的350亿销售额更说明电商在咄咄逼传统行业。

2014年,移动互联网会进阶到怎样的时代,2013年可以说是移动互联网的创业大潮的青春时代,看过《浪潮之巅》,大家应该就站在移动互联网浪潮之颠吧。所以,骚年们,咱生得逢时啊,但是怎么抓住呢?那个愁啊!

所以,2014年,我的关键字有大家说得老烂的字词 「电子商务」、「移动互联网开发技术」,还有一个非常关键的「爱情->婚姻」。(大伙祝福我吧)

最后,虽然很啰嗦,很负面的呱啦了一段,但马年伊始,祝福大伙想啥「马上」有啥!哈哈…

PHP的单例模式

单例模式被当作职责模式,被用来在应用程序中创建一个单一的功能访问点。比如,在OOP中,一个对象只负责一个特定的任务(一个对象去访问数据库等)。

作用:
1)节约系统资源。单例类仅有一个实例存在。
2)维持应用程序状态的同步

特点(至少以下四个特点):
1)必须拥有访问级别为private的构造函数。因为单例类不能在其它类中直接实例化,只能被其自身实例化。
2)拥有一个保存类的实例的静态成员变量。
3)拥有一个访问实例的公共的静态方法。
4)必须创建一个空的访问级别为private的__clone()方法。单例类不会创建实例的副本,而是内部存储的实例返回一个引用。

举例:数据库连接职责的集中控制


class Db
{
   private $_db;   // 构建对象时其值被填充
  public static $_instance;   // 静态变量,用于保存类仅有的一个实例

  private function __construct()
  {
    $this->_db = pg_connnect('.....');
  }

  // 私有的__clone方法消除了PHP语言中可以复制对象,防止破坏单一职责
 private __clone() {}

  // 单例模式的实际构造
 public static function getInstance()
 {
    // 检查静态实例变量是否已保存Db类的一个实例
   if (! (self::$_instance instanceof self)) {
     self::$_instance = new self();
   }

   return self::$_instance;
 }

  public functon query($sql)
  {
     // 执行查询的code
  }
}

静态特性的“缺陷”

静态特性应用在变量,类和方法上,带来的好处是无可厚非的:

1)作为静态变量(用static关键字修饰的函数变量,创建时可提供默认的初始化值,其初始化值不能是表达式),在某一函数执行后,其值仍然不会丢失。这可在全局变量作为模拟静态时,消除了变量名称冲突的现象。

2)作为类的静态属性或方法,其变量值或方法对每个类的实例都是有效的,即是所有的实例共享这个成员或方法。这样可以避免创建类的实例,省略实例化的代码,更高效些(因为类的每个实例都会占用一小部分的系统资源)。

然后,静态类会对特定类型的测试产生影响。因为静态特性的使用会导致类之间通过名称绑定一起,使得单独的测试某个组件变得更加困难。因为类的名称是硬编码的,这样测试时,这个类不容易被模拟。

有 这么个设计原则,叫IoC(控制反转),它试图在OOP中去掉所以相互依赖的现象。相互依赖现象少了,越容易单独测试某个组件。也使对象具有更好的多态性和封装性。这是对于复杂的系统来说,是非常重要的。显然,使用静态特性,IoC就受到限制。

在PHP的应用中,静态特性还是很受欢迎的。现在,自己只能等着瞧它如何“走”~

简单思考

来个简单的滑动显示评价等级星标识。即是假如有个星标识,当鼠标移到到第三个星标识时,前三个星“满色”(充满颜色),后二个没有颜色。看了一些例子,有写得很棒的,很玄的。下边给个很简单的:

function stars(num)
{
	var items = document.getElementById('rating_stars').getElementsByTagName('a');

	if (items.length) {
		for (var i=0; i < items.length; i++) {
			items[i].className = i <= num ? '' : 'disabled';
		}
	}
}

对a标签的className定义下,就可以看到效果。 很简单也很不美观,但效率还是过得去吧,不过有时候觉得,想法简单点,可能会有好的局面和收获。

Ecshop模板类的一个bug

Ecshop类的模板类(includes/cls_template.php),简化的着实好用。但可否注意到这个bug:

简单举例说:

在模板里面的某一个smarty标签有:{foreach from=$goods_list item=goods} , 还有 {$goods.goods_name}等

在PHP文件注册变量有:$smarty->assign(‘goods_list’, $goods_list);  $smarty->assign(‘goods’, $goods);

注意到smarty语法foreach语句中的item的名字是goods,而又有一个goods标签名被注册;其模板类有考虑防止注册相同标签的变量(即$this->_val[var_name]中的var_name防止重复),所以它在处理var_name的时候,在_compile_foreach_start(tag_args)处理,利用$this->_patchstack成员(array类型)暂存储foreach中的处理后的item名字(即对goods后面再加入随机数字,以防重复)。然后再注册变量的时候,判断下标为item的名字的$this->_patchstack变量是否存在,然后再以其值作为$this->_val数组的下标,而进行赋值。

然而,它在处理$this->_patchstack中,有一个必要的任务,存储一个变量的时候,用完它的时候,必须废了它。它在这个关节上,有bug(从make_val方法入手查起就了然)。所以导致在多个foreach在item的名字,如果和单独一个变量名相同时候(上边提到的goods),就会导致生成编译文件的时候,对单独变量名的:eg:编译后的本应该是$this->_val[‘goods’][‘goods_name’], 却是 $this->_var[‘goods_0_95615500_1302147485’][‘goods_name’],这样$smarty->assign(‘goods’, goods)注册的变量,就不可赋值到对应的变量中,从而显示不出数据。

罗嗦了那么多,就是该废它的时候,没废到。在select($tag)方法中,当tag{0} == ‘/’和substr($tag, 1) == ‘foreach’的时候,在$this->_foreachmark == ‘foreachelse’的时候,没有array_pop($this->_patchstack)。允许它,问题解决。

还有,模板类它一开始并为声明_patchstack其成员变量,这本身埋下不稳定的因素。

 

浅谈ECSHOP

许多人应该ecshop并不陌生,它粉丝不少。它对我php的入门学习帮助不少,在此非常感谢它的开源(http://www.ecshop.com)!

总的来说,它的架构清晰,易扩展,可以适合二次开放大型应用的雏形。但它细节的东西还比较粗糙,逻辑任务分解不到位(有些代码块太长,冗余多),稳定性欠佳。我这也是很笼统的说它缺点,它的程序对于入门不久phper也是很容易阅读。本人文笔不够,建议去看它,就知道其中缺点,更能学到许多有用的东西。到时欢迎讨论。也是希望可以帮助改进ecshop,支持开源!

下来就说点ecshop的问题,挑个它模板机制吧,它是模板机制核心在includes/cls_template.php,是改写smarty模板引擎的。改得不错,很方便,Just for it。它里面的诸多细节还需要改进。

它有个动态库文件,就是可以后台添加,删除在前台某个区域显示的内容。如添加广告显示,某类的产品等呀。所以,它不是实时显示内容(不进行缓存)。

这个处理它在cls_template.php中template类的smarty_prefilter_preCompile()方法,在这方法中,对动态库文件的处理,仅仅是加入smarty标签,如{assign var=”” value=”” },然后,比如首页index.php, 写起控制程序的时候,又写了并调用了assign_dynamic()函数,里面不干什么,也是注册动态库的smarty变量。这里虽然通过一个“桥”,来实现对相同的smarty的变量赋值不同的值(如{assign var=”cat_goods”  value=”cat_goods_’. $id .'”}(smarty_prefilter_preCompile()方法实现), $smarty->assign(‘cat_goods_’.$id, $cat_goods) [assign_dynamic()函数实现]),觉得这不错–对动态添加或删除某个显示内容,但是,smarty_prefilter_preCompile()的复杂且多的正则开销,每个方法或函数连接数据库的开销,smarty变量命名的不稳定性(如果方法和函数中某个变量写的不一致,就不能对应赋值啦),这些开销省下来,是多么的可观啊!呼呼~,可以省去assign_dynamic()函数,在preg_replace_callback()中的调用函数,在进行匹配处理的时候,就进行相应的赋值。

还有,smarty_prefilter_preCompile()对动态库文件那么辛苦的工作,就是为了在对应的include的动态库文件前,加入对应{assign var=” value=”}。既然在后台处理模版布局的时候,ecshop对添加的动态文件会在对应的模板,即是对应的.dwt文件里添加<!– #BeginLibraryItem “*.lbi” –>,何不添加一个标识(唯一),在smarty_prefilter_preCompile用str_replace()替换,不轻松许多吗?!

不过,这样也一个弊端,就是对模板可视化编辑的时候,添加的动态库文件不能按原来的规则(即<!– #BgeinLibraryItem ”*.lib” –>, Dreamweaver的模板文件)显示出来。

如果不用标识替换,即保持原来处理方法。在smarty_prefilter_preCompile()方法中,觉得不需要用正则来{include file=”动态库文件”}来前面加上{assign var=”” value=””},而且还要整个$GLOBALS[‘libs’]来协助。稳定性可想而知了。何不用explode敲入数组处理(处理匹配的第一个就可以,这样说很多人不清楚,它的动态库文件存储在数组中,在某个区域内,相同类型的包含的动态文件是相同的,所以在前面{assign}来赋不同的值。在遍历该动态库文件,遇到匹配字符,立即处理便可。)。用处理处理效率高很多,也减少许多不稳定行因素。

再稍稍说下category.php页,有些once write的感觉,很容易看到在相同的应用下调用get_extension_goods()函数许多次,而get_extension_goods()每次都需要和数据库“握握手”。太过奢侈啦!里面可以改进许多,有兴趣者欢迎Email探讨~

以上也是个人看法,自己能力还不够强,还很有待指点。BTW,shopex比ecshop完善许多,可惜不开源。希望ecshop越来越高级,支持开源!

附上改过后的code: ecshop_20101226.zip

设计模式

我们在写程序中遇到许多问题,会发现一些的解决方法很是类似。其实,这些解决方法原理,也是许多programer广泛重复使用的方法。这就涉及到设计模式 — 用来描述在程序设计中解决特定问题的一种可重复的方法。

但是,如果过度使用,为了增加设计的灵活性和架构的适应性,以便应对可能永远不会出现的未来需求,导致应用的实现被该用哪些设计模式比较恰当等这些因素所阻碍,这就是所谓的“分析瘫痪”状态。

所以,设计模式只在一些特定的情形中才是有用的。

有个小小体会,程序的“职责分离”,尽量创建小的,易读的,易修改的代码块,用来解决特定的问题。

数列

还是记下来,方便使用:

1 等差数列
1)公差d:d = an-an-1
2)通项公式:an = a1 + (n-1) d = S1 = Sn – Sn-1 (n >= 2)
3)等差中项:A = (a+b) / 2
4)前n项和:
Sn = n(a1 + an) / 2 = na1 + n(n-1) * d / 2
5)a1 + an = a2 + an-1 = ….

2 等比数列
1)公比q: q = an / an-1
2)通项公式:an = a1qn-1
3)等比中项:b2 = ac
4)前n项和:Sn = a1(1-qn) / (1-q) (q != 1)
5)a1an = a2an-1 = …..

动态级联下拉框

今天需要遇到添加商品的某一类型的时候,原来的类型只有一个下拉框,虽然下拉框有层次缩进,但太长了,很不好使用,于是,想有这么一个处理:

1)首先生成根类的下拉框
2)选择某一个父类的时候,随后生成一个其子类的下拉框,如果有子类的子类(即孙子),不列出它们。
3)以此类推

完成以上功能的话,非JavaScript莫属了。

这里有一个难点,如果处理好只能生成当前父类的子类(如果存在的话),而且不能重复生成子类的下拉框(select)。变化到同级中的另一个父类的时候,之前父类的子类(还有由点击子类生成的其子类(即孙子类)的下拉框,再类推以下几级),都要消失,而是生成当前指向的父类的子类。

说得乱七八糟的吧。其实,也简单,我们要处理的是,当前某层类位置以后东东,我们需要一个东西记录这些东东,以便选择同层中另一个类时候的删除它们,插进该类的子类。

以上也只是自己想得处理这问题的一个方法,当前能力还非常有待提高,所以很期待高人指点。下边贴出代码,JavaScript好些东西是用于致学(即是用的时候,查看了下参考手册,很多东西没了解,用的比较生硬),还有,以下只支持IE,非常郁闷!其它浏览器不支持Event对象的srcElement属性哦!

类别的XML文档category.xml

HTML DOM:

处理xml并生成category的select菜单的JavaScript:xml_category.js

/**
 * @param   xml_name     xml文档的路径
 * @param   target_id      html的DOM的ID
 */
function xml_category(xml_name, target_id)
{
  var self = this;             // 私有变量,指向对象本身,在JS“类”的封装和访问机制,理解还有待加强。
  
   this.xmlDoc       = loadXMLDoc(xml_name)        // loadXMLDoc函数见/?p=302
   this.targetID     = document.getElementByID(target_id);
   this.catTree      = new Array();                       // 记录select的情况

   /**
    *  产生select下拉框
    */
   this.createSelect = function(parent_id)
   {
      var xmlDoc = self.xmlDoc;
      var cat, cat_num;

      // 以下为了都兼容各浏览器,不使用用与IE的XML DOM的selectNodes() 方法用一个 XPath(//cat[pid=*])[*表传来对应的parent_id] 查询选择节点.其它浏览器可参考Document.evaluate()。
      cat = xmlDoc.getElementByTagName("cat");
      cat_num = parseInt(cat.length);
     
      if (cat_num > 0) // 类别存在
      {
         var slt;
         var is_set = false;                                            // 为标志该层是否有类别,即有option选项
         slt = document.createElement("SELECT")              // 创建一个select元素节点
         slt.onchange = self.createChildSelect;                 // onchange事件

         // 每次都是整树遍历啊! !--
         for (var i=0, j=0; i < cat_num; i++)
         {
            // 同层的类别
            if (parseInt(cat[i].getAttribute("pid")) === parseInt(parent_id))
            {
               // option选项
               slt.optons[j] = new Option(cat[i].childNodes[0].nodeValue, cat[i].getAttribute("id"));
               is_set = true;
               j++; 
            }
         }

         // 有选项值
         if (is_set)
         {
             self.catTree.push(slt);          // 记录这个select下拉框
             self.targetID.appendChild(slt);
         }
         else
         {
            slt = null;
         }

      }
   }

   /** 
    * 生成子类
    */
  this.createChildSelect = function()
  {
       var slt_len = self.catTree.length;
       
       if (slt_len > 0)
       {
            var s = 0;                     // 标记当前触发option在catTree的位置(如果存在的情况)

            // 很不顺眼的遍历
            for (var i=0; i < slt_len; i++)
            { 
                // srcElement是IE的属性。 !--
                if (self.catTree[i] == event.srcElement)
                {
                    s = i ; break;
                }
            }

            // 删除该层位置以后所有的东东
            for (var i= slt_len - 1; i > s; i--)
            {
                 self.targetID.removeChild(self.catTree[i]);
                 seft.catTree.pop();
             }

            self.createSelect(event.srcElement.value);
       }

  }

}

下来在之前HTML文档位于id=”cat”写下如下JS,便可实现了:

var xml = xml_category("xml_category.xml", "cat")
xml.createSelect(0);

以上处理,还非常欠缺灵活性,就是初始化这个select下拉框的时候,如果输入的parent_id不是根类的ID,而是子类,或以下几层的子类的ID的时候,不能生成相应的select下拉框。

到此,写下这点记录,希望高人指点,也继续更进~

UTF-8的BOM

在window下面用记事本编辑文件的时候,如果保存为UNICODE或UTF-8,分别会在文件的开头加上两个字节“\xFF\xFE”(UNICODE)和三个字节“\xEF\xBB\xBF”(UTF-8)。在读取的时候就可能会遇到问题,但是不同的环境对这几个多于字符的处理也不一样。

Unicode规范中有一个BOM的概念。全名Byte Order Mark(字节序标记)。

BOM告诉编辑器当前文件采用何种编码,方便编辑器识别(如果接收者收到以EF BB BF开头的字节流,就知道这是UTF-8编码了),也为了支援UTF-16,UTF-32才加上。

 在开发中,如果不涉及太多区域的应用,BOM作用不是很大,在php设计时没有考虑BOM的问题,这三个字节会直接输出,如果这时在程序里调用了session函数,就会出问题了。所以一般情况下,文件应该使用 Unicode (UTF-8) 编码保存。同时不要使用字节序标记(BOM)。所以,生成文件时,过滤“\xEF\xBB\xBF”即可。