Living a Simple Life is a Happy Life

有饭吃,自由自在,就非常开心

How to Xargs Separate Parameters

| Comments

做过很多遍了,每次还是得现查,记一下:

echo "'param 1' 'param 2'" | xargs -n1 | xargs -I@ echo \[@\] \[@\]

output:

1
2
[param 1] [param 1]
[param 2] [param 2]

xargs里面替换字符串

ls|xargs -I @  echo "mv @ @.pack"|sed 's/.json.pack.pack/.pack/g'|bash

Python编程实战 - 笔记1

| Comments

这本书讲的挺实在的,设计模式的部分又复习了一遍。另外又学了几个Python3的新decorator。

创建型设计模式

抽象工厂 (Abstract Factory)

  • 名字就用AbstractFactory好了,不要起什么BaseFactory之类的
  • 相关类都放到对应的Factory Class里面,不要暴露给外面了

建造者模式 (Build)

  • 和工厂的区别就是保存了创建对象时各个部分的细节

工厂模式

  • 根据情况实例化对象
  • 还是注意和抽象工厂的区别,抽象工厂是将创建对象的行为抽象出来,而工厂模式则是根据要创建的对象类型实例化

原型模式 (Prototype)

  • 这个模式其实在javascript的根本,不过Python的实现方法还真是五花八门,我说直接用copy就好了嘛

单例模式 (Singleton)

  • 我最中意的一种实现:
1
2
3
4
5
6
7
8
9
10
11
class Borg:
    _shared_state = {}
    def __init__(self):
        self.__dict__ = self._shared_state

class Singleton(Borg):
    def __init__(self, arg):
        Borg.__init__(self)
        self.val = arg
    def __str__(self):
        return self.val

结构型设计

  • 作用就是改装对象,或者把小对象合并为大对象

Adapter

  • 其实就是转接方法

Bridge

  • 把方法抽象出来

Composite

  • Python里面有一种省一点内存的写法,可以直接用CompositeItem和Item两个类来实现,不过我觉得不直观

decorator

  • 几个新的decorator:

    • @functools_wraps : 装饰器工厂

    • @statically_typed : 类型检查

  • 另外@ensure类修饰符可以用来简化设置property的代码

Facade

  • 这个模式其实是天天在做的,就是把接口聚合的好看一点
  • 其实思想可以推广到很多方面,比如Docker,就是LXC的一个Facader,而且做的比较好看,于是大家就都来用了

Flyweight

  • 管理许多小对象的时候用引用
  • Python用__slot__ Attribute来做最方便
  • 里面关于用shelve对象存储class attribute的思路挺实用的

Multiline Comments in Docker File

| Comments

为了减少Image的fs layout数目,Dockerfile中经常会把多个命令集中到一个 RUN 指令下。

多行之后可读性就很差了。

有个比较偏门的写注释的方法:

1
2
3
4
5
6
7
8
9
10
11
RUN mkdir -p /home/build/npm3 && \
    mkdir -p /home/build/smartprobe && \
    mkdir -p /home/build/bpc3 && \
    `#====================================================` \
    `#=============checkout and install rpms==============` \
    svn export xxx && \
    rpm -ivh --force --nodeps *.rpm && \
    `#====================================================` \
    `#=============checkout and install python==============` \
    ... && \

比较实用,推荐之。

Different Users in Same Unix Group Can Not Run Mongod

| Comments

事情的缘起是这样的…….

想要在Dockerfile中启动一个MongoDB,之后编译为Docker image。(不要问我问什么要在docker image中存一个mongodb数据库,真实世界的需求你永远想不到)

Docker build不支持 –privileged,所以默认的/etc/init.d/mongod 这个脚本中的

1
runuser -s /bin/bash mongod -c 'ulimit -S -c 0 >/dev/null 2>&1 ; numactl --interleave=all /usr/bin/mongod -f /etc/mongod.conf'

这种写法就死翘翘了。

github上有这个Issue:

https://github.com/docker/docker/issues/1916

大家讨论了1年多,对于怎么解决,还是没有个所以然。(话说要再吐槽一下github的issue了,一般大一点的项目,一个issue跨度以年来论,长篇大论读完也不容易呀)

最后只好在Dockerfile中这么搞:

1
2
3
4
mongod --fork -f /etc/mongod.conf && \
mongod --shutdown -f /etc/mongod.conf && \
chown mongod:mongod /opt/lib/mongodbpath -R

这样build就顺利完成了。

可是启动这个image为container后,执行:

1
/etc/init.d/mongod start

报错:

1
 [initandlisten] warning couldn't write to / rename file /datadir/journal/prealloc.0: couldn't open file /datadir/journal/prealloc.0 for writing errno:1 Operation not permitted

虾米,明明已经加了mongod的group了。而且errorno是1,不是 "errno:13 Permission denied",有点奇怪。

问题在这里:

https://jira.mongodb.org/browse/SERVER-7583

要再加一个指令:

1
setcap cap_fowner+ep /usr/bin/mongod

就是这么折腾。

第一个AndroidApp-华容道

| Comments

第一个Android APP上线,很简单的一个小游戏:智取华容道

GooglePlay: https://play.google.com/store/apps/details?id=me.happy123.aklotski

百度应用下载地址: http://shouji.baidu.com/game/item?docid=7547918

小米商店下载地址: http://app.mi.com/detail/87656

老是对自己说,要不断学习,跟上时代浪潮。但是悲哀的发现,自己是跟的越来越吃力了。

早在大连搞电视机的时候,那时候Android刚出来,就对自己说,要好好看看。无奈之后各处辗转,这事就搁下了。

非不能也,实不为也!

过年空出来一个周,Python党也要写Android App了。

找了本 HeadFirst Java ,一天的时间扫了一遍Java的语法,写了几个小例子练练手。之后直接对着Android官方文档开干了。

流程就是 google -> 坑 -> 继续google ….,如此循环不已……

用父亲打扑克的机器,开个Eclipse基本上卡半天,本着一颗折腾的心,硬是PS出来几张背景图,小游戏<华容道>出炉啦。

图片和关卡都来自于代码发芽网,作者非常大度的授权我使用,非常感谢:

http://fayaa.com/

开发日志:

很巧的,我在知乎看到了类似”如何20天零基础开发Android游戏” 的问题。大家都是对这种问题不屑一顾的,但我从开始到完成恰好十来天,可以作为参考记录一下。

Day1

确立市场方向,要做个益智类游戏,啥好呢?啥好呢?我把所有的Android的华容道下载下来玩了一遍,嗯,都很粗糙,看来有的玩,就是它了。

这个时候,其实你已经完成一半工作了。没错,就是那句什么 决定出发的时候,旅行就完成了一半。 这个时候就不要纠结于做什么了,关键是如何做。

Day2

开始搭建开发环境,先后折腾了Android Studio和Eclipse,家里翻墙实在慢呀,另外我用父亲打扑克的机器跑Android Studio是在跑不动啊,最后装了Eclipse。推荐个链接,有兄弟收集了开发环境套装国内下载,你值得拥有。

http://www.cnblogs.com/bjzhanghao/archive/2012/11/14/android-platform-sdk-download-mirror.html

Day3

开始学习Java,神马?你都不会Java还来搞Android。嗯,我也没想到,有一天我要搞Java!要搞Java啊,我对它的印象还是航空母舰啊~~~。没办法,找了本 Head First Java 电子书,照着书里面的例子一个个敲,没错,不论懂不懂,就一个个敲。,幸亏 Head First 系列专门照顾低智商读者的,像我这样的一天敲到了300多页的例子。

Day4

不知不觉就把600页的书敲完了。嗯,看来那种xx天精通xxx,也不是骗人的,现在我的状态,估计能出去忽悠一些HR了。

Day5

先不管Android,搞一个穷人版的华容道。就开始封装三个类,什么接口啊,多态啊,统统不用,就是简单继承。搞了个核心库,棋盘就用 0011 先画出来了。嗯,最后可以玩了。不过只有我一个人懂得怎么玩。 不过很有成就感啊,看来一天写个俄罗斯方块之类的不是难事啊。

Day6

这个核心库很关键啊,挂了直接影响后面的开发啊。但是我用 0011 看的很累啊。赶紧google之,看看Java怎么搞测试~~~,好,最后集成了JUnit,顺便学了学怎么在Eclipse里面跑测试,最后写了一堆测试用例,果然发现了BUG,某些棋子竟然能对角线走动,罪过罪过。

Day7

Android程序怎么搞,现在八字还没一撇呢。热情啊,就要消耗光了。

赶紧找了本网上号称最简单的Android开发教程 第一行代码 ,再开始对着敲。没错,我就是只会这一招。

Day8

还在敲~~~,敲不完了。不是打字慢,而是电脑一开Android 手机模拟器就陷入昏睡模式,一气之下不敲了,直接接上手机开干。

Day9-Day13

游戏逻辑早写完了,我对Android App的开发印象就是:画View。没错,就是处理各种图像拉伸,各种尺寸适配,各种布局位置,各种奇葩的手机尺寸,各种API新老接口整合。

简单列举下中间遇到的坑:

  • 怎么在各种尺寸下画出一个比较漂亮的棋盘布局

  • 怎么用相对布局把几个菜单搞得整齐一点

  • 怎么PS出一个不那么生硬的背景,没错,我还同时开了Photoshop做美工,这个时候电脑已经处在超脱状态

  • 怎么在两个View直接传数据,怎么处理活动的生存周期

  • 怎么加背景音乐,怎么搞成服务,怎么能让程序退出的时候结束服务,怎么搞得好听一点,没错,这个时候我又客串DJ,研究了MP3各码率下音质和文件大小的关系,最后以专业乐评人的视角选择了一首古筝曲

  • 晕,现在为止还是只有一关呢,怎么办?我到 代码发芽 网上,写个Python脚本爬下来了所有关卡,同时邮件问作者申请授权

  • 到底是拖动棋子效果好呢,还是点击移动棋子效果好呢?我又化身PM,反反复复走最简单的一关, 十八步 ,最后确定还是点击效果好。没错,到目前为止,这个游戏我只玩通了三关,我的智商就是这样。最后我爸还解决了 横刀立马 ,我怎么也不行。

  • 发现一个重大问题,原来我一直以为只有五虎上将的,一共五横五竖十个长条棋子,突然发现有一关里面出现了魏延,我晕,再调吧

  • 又发现个问题,Android的默认sqlite模块好像没说怎么把已有的数据库集成进去,怎么办,Google吧

Day14

到各个Android市场注册账号,提交APP,等审核,之间经历若干PS工作,这也是辛苦活啊。

~~~~~

就是这样,我在各种大坑小坑瞎搞之后,瞎搞出来了。。。

结论

  • 所谓零基础xx天搞APP是可以的,但是有条件,你只是Android零基础或者Java零基础,其它编程基础还是要有的,不然期间大坑小坑肯定要浪费很多时间

  • 熟练工和初级工效率差距巨大,一天写个游戏APP不是开玩笑

  • 这样写出的APP完成度低,要加入完整的体验流程,工作量还是很大的

Javascript设计模式 - 笔记3

| Comments

javascript 里面的继承是个非常复杂的话题,一言蔽之,就是你要替解释器干点活。另外,javascript属于使用原型式继承的语言,这个比较少见,所以直觉上不好拐弯。

先回顾比较简单的办法

类式继承

一个简单的类

1
2
3
4
5
6
7
8
9
10
11
/* class Person */
function Person(name) {
    this.name = name;
}

Person.prototype.getName = functino() {
    return this.name;
}

var reader = new Person('brainzhang');
reader.getName();

好,下面定义一个它的子类

1
2
3
4
5
6
7
8
/* Class Author */
function Author(name, books) {
    Person.call(this, name);
    this.books = books;
}
Author.prototype = new Persion();  //set up the prototype chain
Author.prototype.contructor = Author; //set the constructor attribute to author
Author.getBooks = function() {return this.books;}

容易费解的是这两行:

1
2
Author.prototype = new Persion();  //set up the prototype chain
Author.prototype.contructor = Author; //set the constructor attribute to author

javascript中,每个对象都有一个原型对象,在创建一个对象时,javascript会自动将其原型对象设置为其构造函数的prototype属性所指的对象。 在访问对象的某个成员时,如果这个成员未见于当前对象,那么javascript会沿着原型链向上逐一访问每个原型对象(最顶端为Object.prototype对象),直到找到这个成员为止。 这意味着,为了让一个类继承另一个类,只需将子类的prototype设置为基类的一个实例即可。

第二行将prototype的constructor属性重新设置为Author。是因为: 定义一个构造函数时,其默认的prototype对象是一个Object类型的实例,其contructor属性会被设置为构造函数本身。如果手工将prototype设置为另一个对象,就要重新设置其constructor属性。

最后,为了简化类的声明,可以将这些工作封装在extend函数中:

1
2
3
4
5
6
function extend(subClass, superClass) {
    var F = function(){};
    F.prototype = superClass.prototype;
    subClass.prototype = new F();
    subClass.prototype.contructor = subClass;
}

作为改进,定义了一个新对象F,避免基类对象过大,创建实例浪费资源。

但是这样还有个小缺点,声明 Author的时候,还要显式的调用一下Person.call(),下面这个版本进一步做了改进:

1
2
3
4
5
6
7
8
9
10
11
function extend(subClass, superClass) {
    var F = function(){};
    F.prototype = superClass.prototype;
    subClass.prototype = new F();
    subClass.prototype.contructor = subClass;

    subClass.superclass = superClass.prototype;
    if(superClass.prototype.contructor == Object.prototype.constructor) {
        superClass.prototype.contructor = superClass;
    }
}

增加了一个superclass属性来直接访问基类,这样声明Author的时候可以这么写:

1
2
3
4
5
6
7
/* Class Author */
function Author(name, books) {
    Author.superclass.contructor.call(this, name);
    this.books = books;
}
extend(Author, Person);
Author.getBooks = function() {return this.books;}

原型式继承

TODO

Git Cheat

| Comments

整理一下经常忘记的Git命令

remote 同步

1
2
3
4
$ git remote -v                                                     // 查看当前远程版本库
$ git remote add cocos2d-x git://github.com/cocos2d/cocos2d-x.git   // 添加原始版本库
$ git fetch cocos2d-x                                               // 获取原始版本库的更新
$ git merge cocos2d-x/master                                        // 合并原始版本库的代码到当前版本库中,合并前确保当前分支是master

remote branch

1
2
3
4
5
6
$ git branch -a                                                     //查看所有分支
$ git checkout -b branches/xxx origin/branches/xxx                  //chekcout远程分支
$ git push origin --delete <branchname>                             //删除远程分支
$ git push origin --delete tag <tagname>                            //删除远程tag
$ git push origin :<branchName>                                     //推送一个空分支到远程分支,相当于删除分支
$ git push origin :refs/tags/<tagName>                              //推送一个空tag到远程,相当于删除tag

删除不存在对应远程分支的本地分支

假设这样一种情况:

  1. 我创建了本地分支b1并pull到远程分支 origin/b1;
  2. 其他人在本地使用fetch或pull创建了本地的b1分支;
  3. 我删除了 origin/b1 远程分支;
  4. 其他人再次执行fetch或者pull并不会删除这个他们本地的 b1 分支,运行 git branch -a 也不能看出这个branch被删除了,如何处理?
1
git fetch -p                                                        //在fetch之后删除掉没有与远程分支对应的本地分支

重命名远程分支

在git中重命名远程分支,其实就是先删除远程分支,然后重命名本地分支,再重新提交一个远程分支

1
2
3
$ git push --delete origin devel                                    //删除远程分支
$ git branch -m devel develop                                       //重命名本地分支
$ git push origin develop                                           //推送本地分支

把本地tag推送到远程

1
$ git push --tags

获取远程tag

1
$ git fetch origin tag <tagname>

合并branch上的指定文件

branches/A 上修改了一个文件A.h,新增了一个文件B.h,删除了一个文件C.h。

1
2
3
4
5
6
7
$ git checkout master                                               //首先切换到master分支

$ git checkout -p branches/A A.h                                    //不切换branch,把RemLanbranches/A上的A.h更新到当前分支

$ git checkout branches/A B.h                                       //去掉-p参数,新增该B.h文件

$ rm C.h                                                            //删除文件目前还没找到其他办法,但效果是一样的

分支的衍合

参考:http://git-scm.com/docs/git-rebase

diff

只显示两个分支间的差异,如果你想找出‘master’,‘test’的共有 父分支和’test’分支之间的差异,你用3个‘.’来取代前面的两个’.’ 。

1
$ git diff master..test

显示你当前的索引和上次提交间的差异;这些内容在不带”-a”参数运行 “git commit”命令时就会被提交。

1
$ git diff --cached

显示你工作目录与上次提交时之间的所有差别,这条命令所显示的 内容都会在执行”git commit -a”命令时被提交。

1
$ git diff HEAD

如果你要查看当前的工作目录与另外一个分支的差别,你可以用下面的命令执行: 这会显示你当前工作目录与另外一个叫’test’分支的差别。你也以加上路径限定符,来只 比较某一个文件或目录。

1
$ git diff test

显示你当前工作目录下的lib目录与上次提交之间的差别(或者更准确的 说是在当前分支)。

1
$ git diff HEAD -- ./lib

如果不是查看每个文件的详细差别,而是统计一下有哪些文件被改动,有多少行被改 动,就可以使用‘–stat’ 参数。

1
$ git diff --stat

Javascript设计模式 - 笔记2

| Comments

如何封装一个对象

门户大开型

最简单的办法就是按传统方法创建一个类,用一个函数来做其构造器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var Book = function(isbn, title, author) {
    if (isbn === undefined) {
        throw new Error('Book constructor requires an isbn.');
    }
    this.isbn = isbn;
    this.title = title || 'No title specified';
    this.author = author || 'No title specified';
}

//define by attr
Book.prototype.display = function() {...};

//define by object literals
Book.prototype = {
    display: function(){...},
    checkIsdn: function(){...}
};
  • 优点:简单
  • 缺点:没有保护,需要加各种校验。但内部的成员还是有很大可能被修改的。

语法修饰增强型

用setattr,getattr等赋值取值方法及命名规范区别私有成员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
var Book = function(isbn, title, author) {
    if (isbn === undefined) {
        throw new Error('Book constructor requires an isbn.');
    }
    this.setIsbn(isbn);
    this.setTitle(title);
    this.setAuthor(author);
}

//define by attr
Book.prototype.display = function() {...};

//define by object literals
Book.prototype = {
    _checkIsdn: function() {...}

    setIsbn: function(isbn) {
        if!(this._checkIsbn(isbn)) {
            throw new Error("Invalid isbn");
        }
        this._isbn = isbn;
    }

    getIsbn: function() {
        return this._isbn;
    }
    .......
};
  • 优点:简单,安全性也有所增强
  • 缺点:不是真正的私有成员,内部的成员还是有很大可能被修改的。

闭包实现私有成员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
var Book = function(iisbn, ititle, iauthor) {

    //private attributes
    var isbn, title, author;

    //private method
    function _checkIsbn(iisbn) {
        ...
    }

    //privileged methods
    this.getIsbn = function() {
        return isbn;
    };

    this.setIsbn = function(iisbn) {
        this._checkIsbn(iisbn) ...
        isbn = iisbn;
    }

    .......

    //contructor code
    this.setIsbn(iisbn);
    this.setTitle(ititle);
    this.setAuthor(iauthor);
};

//public, non-privileged methods

Book.prototype = {
    display: fucntion(){},
    ....
}

这里应用了js的闭包特性,isbn等属性不再通过this来引用,而是放到函数的构造器里面。既要访问到私有成员,又要对外的方法放到函数的构造中,对私有成员没有依赖的函数用prototype。

  • 优点:比较完整的模拟了private特性
  • 缺点:private方法不再存在prototype里面,这样没生成一个新的对象实例都会为每个每个私有方法和特权方法生成一个新副本,耗费内存。

实现静态方法和属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
var Book = (function() {

    //private static attributes
    var numberOfBooks = 0;

    //private static method
    function checkIsbn(iisbn) {
        ...
    }

    //return the contructor
    return function(iisbn, ititle, iauthor) {
        //private attributes
        var isbn, title, author;

        //privileged methods
        this.getIsbn = function() {
            return isbn;
        };

        this.setIsbn = function(iisbn) {
            this._checkIsbn(iisbn) ...
            isbn = iisbn;
        }
        .......

        //contructor code
        numOfBooks++;
        this.setIsbn(iisbn);
    }
})();

//public, static methods
Book.converTotitleCase = function(){...};

//public, non-privileged methods

Book.prototype = {
    display: fucntion(){},
    ....
}

这里和闭包实现私有成员的区别就在于构造函数变成了一个内嵌函数,这样就创建了一个闭包,可以把静态的私有成员声明在最顶层。

实现常量

常量就设置为一个私有静态属性,用大写区分即可。我认为没有必要实现一个取值器去限制,用CONST前缀从代码风格上约束即可。

Javascript设计模式 - 笔记1

| Comments

富有表现力的javascript

弱类型语言

javascript中有三种原始类型:布尔型、数值型(不区分浮点数和整数)和字符串型。

此外,还有对象类型和包含可执行代码的函数类型。前者是一种复合类型(数组是一种特殊的对象)。

最后,还有空类型(null)和未定义类型(undefined)。

原始数据类型按值传送,其他数据类型则按引用传送。

函数是一等对象

  • 匿名函数
1
2
3
4
5
(function(){
    var foo = 10;
    var bar = 2;
    alert(foo * bar);
})();
  • 闭包
1
2
3
4
5
6
7
8
9
var baz;
(function(){
    var foo = 10;
    var bar = 2;
    baz = function(){
        return foo * bar;
    }
})();
baz(); //baz可以访问foo和bar,即使是在匿名函数外面执行
  • 作用域、嵌套函数和闭包

    • js中,只有函数具有作用域:

      在一个函数内部声明的变量,外部无法访问; 定义在一个函数中的变量在该函数的内嵌函数中是可以访问的

    • js中的作用域是词法性的:

      函数运行在定义他们的作用域中,而不是调用他们的作用域中; 可以利用这个特性定义静态方法和属性;

对象的易变性(mutable)和内省(introspection)

  • 易变性:js中可以对象前定义的类和实例化的对象进行修改
  • 内省:js中可以在运行时检查对象所具有的属性和方法

接口

  • 接口也是一种对象,判断一个类是否是实现了某类接口,就是传入这个接口,而后比较。

    java有专门的接口类,C++有虚基类,而C在linux kernel中的对象设计方法实际上也是一种接口实现,这都是接口在语言层面支持的体现

  • 封装(encapsulation)和信息隐藏:信息隐藏是目的,而封装则是藉以达到这个目的的技术

    java和C++有 private关键字作为支持,Python有’__‘的命名约定,js中一般用闭包来模拟

接口实现

用注释来模拟

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/*
interface Composite {
    function add(child);
    function remove(child);
    ...
}

interface FormItem{
    function save();
}

var CompositeForm = function(id, method, action) {
    //implements Composite, FromItem
    ...
};

CompositeForm.prototype.add = function(child) {
    ...
}
CompositeForm.prototype.remove= function(child) {
    ...
}
CompositeForm.prototype.save= function() {
    ...
}

  • 优点:简单明了,代码体积小
  • 缺点:无法错误检查

用属性检查模仿接口

约定所有类明确声明实现了那些接口,和这些类打交道的对象可以针对这些声明做检查。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/*
interface Composite {
    function add(child);
    function remove(child);
    ...
}

interface FormItem{
    function save();
}

var CompositeForm = function(id, method, action) {
    //implements Composite, FromItem
    this.implementsInterfaces = ['Composite', 'FromItem'];
    ...
};
    ...

function addForm(formInstance) {
    if (!implements(formInstance, 'Composite', 'FormItem')) {
        throw new Error("object does not implement a required interface");
    }
}

function implements(objects) {
    for (var i = 1; i < arguments.length; i++) {
        var interfaceName = arguments[i];
        var interfaceFound = false;
        for (var j = 0; j < object.implementsInterfaces[j] == interfaceName) {
            interfaceFound = true;
            break;
        }
        if (!interfaceFound) {
            return false;
        }
    }
    return true;
}

  • 优点:有错误检查
  • 缺点:每次调用都要检查,啰嗦,另外防不住有说了实现但没有干活的

鸭式辨型模仿接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
var Composite = new Intreface('Composite', ['add', 'remove']);
var FormItem = new Interface('FormItem', ['save']);
var CompositeForm = function(id, method, action) {
    ...
};

function addForm(formInstance) {
    ensureImplements(formInstance, Composite, FormItem);
    ......
}

var Interface = function(name, methods) {
    if (arguments.length != 2) {
        throw new Error("Interface constructor called with " + arguments.length + "arguments, but expected exactly 2.");
    }
    this.name = name;
    this.methods = [];
    for (var i = 0; len = methods.length; i < len; i++) {
        if (typeof methods[i] !== 'string')  {
            throw new Error("Interface contructor expects method names to be " +
                            "passed in as string");
        }
    }
    this.methods.push(methods[i]);
}

Interface.ensureImplements = function(object) {
    if (arguments.length < 2) {
        throw Error("Functino Interface.ensureImplements called with " + arguments.length +
                    "arguments, but expected at leaset 2.");
    }

    for (var i = 1, len = arguments.length; i < len; i++) {
        if (interface.constructor !== Interface) {
            throw new Error("Function Interface.ensureImplements expects arguments" +
                            "two and above to be instances of Interface.");
        } 
    }

    for (var j = 1, methodsLen = interface.methods.length; j < methodsLen; j++) {
        var method = interface.methods[j];
        if(!object[method] || typeof object[method] !== 'function') {
            throw new Error("Function Interface.ensureImplements: object " +
                            "does not implement the " + interface.name)  +
                            "interface.Method " + method + " was not found.");
        }
    }
}

  • 优点:进一步加强了错误检查
  • 缺点:增大了调试难度

Macvtap Ethernet Support Tcpdump

| Comments

用macvtap模拟网卡时,用tcpdump抓包是抓不到的,后来发现3.14版本以上的内核修正了这一点。

参考这个提交:

https://github.com/torvalds/linux/commit/6acf54f1cf0a6747bac9fea26f34cfc5a9029523