Leetao's Blog

Talk is cheap, show me the code

0%

缩进层级

保持一定的缩进,使用制表符进行缩进或者使用空格进行缩进.

语句结尾

每行代码结尾使用分号.

1
2
3
4
5
6
7
8
9
10
11
//合法代码
var name = "Nicholas";
function sayName() {
alert(name);
}

//合法代码,不推荐写法
var name = "Nicholas";
function sayName() {
alert(name)
}

上述两种代码合法的原因有赖于分析器的自动分号插入(Automatic Semicolon Insertion, ASI) 机制,JavaScript 代码省略分号也是可以的.但是存在由于该机制导致的错误.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//原始代码
function getData() {
return
{
"name": "leetao",
"age": "unknown"
}
}
//分析器会将它理解成
funcition getData() {
return;
{
"name": "leetao",
"age": "unknown"
};
}
//通过将左括号移至于return同一行修复该问题
function getData() {
return {
"name": "leetao",
"age": "unknown"
}
}

行的长度

每行的保持一定的长度,推荐80个字符.

换行

当每行到达单行最大胆字符数字数限制时,在运算符后换行,下一行需要增加那个层级的缩进.

1
2
3
4
5
6
7
8
9
10
11
// 好的做法: 在运算符后换行,第二行追加两个缩进
callAFunction(document, element, windoow, "some string value", true, 123,
navigator);

// 不好的做法: 第二行只有一个缩进
callAFunction(document, element, windoow, "some string value", true, 123,
navigator);

// 不好的做法: 在运算符前换行
callAFunction(document, element, windoow, "some string value", true, 123
, navigator);

总是将一个运算符置于行尾, ASI 不会自作主张地插入分号.对于语句同样遵循上述规则.不过对于语句,主体部分换行后未一个缩进.该规则存在一个特例: 当给变量赋值时,第二行位置应当和赋值运算符的位置保持对齐.

空行

建议在下述位置添加空行:

  1. 在方法之间.
  2. 在方法中的局部变量和第一条语句之间.
  3. 在多行或单行注释之前.
  4. 在方法内的逻辑片段之间插入空行,提高可读性.

命名

  1. 变量名遵循驼峰大小写命名法 (Camel Case),并且命名前缀应当是名词.
  2. 函数名遵循驼峰大小写命名法 (Camel Case),并且命名前缀应当是动词.
  3. 常量使用大小写字母和下划线来命名,下划线用以分割单词.
  4. 构造函数的命名遵循大驼峰命名法 (Pascal Case) ,即以大写字母开始.

直接量

字符串

  1. 多行字符串,使用字符串连接符 (+) 将字符串分成多份.
  2. 字符串用单行引号括起来均可,但是一个项目应当保持一种风格.

数字

为了避免歧义,不要省略小数点之前或者之后的数字.

null

建议使用 null 的场景:

  1. 用来初始化一个变量,该变量可能赋值为一个对象.
  2. 用来和一个已经初始化的变量比较,该变量是不是对象均可.
  3. 当函数的参数期望是对象时,用作参数传入.
  4. 当函数的返回值期望是对象时,用作返回值传出.

下述场景不应当使用 null:

  1. 不要使用 null 来检测是否传入某个参数.
  2. 不要用 null 来检测一个未初始化的变量.

undefined

不管是值是 undefined 的变量还是未声明的变量, typeof 运算结果都是 “undefined”.通过禁止使用特殊值 undefined,可以有效地确保只在一种情况下 typeof 才会返回 “undefined”: 当变量未声明时.如果你使用了一个可能(或者可能不会)赋值为一个对象的变量时,则将其赋值未 null.

对象直接量

创建对象使用对象直接量,在直接量中直接写出所有属性,这种方式可以取代先显式创建地创建 Object 的实例然后添加属性的这种做法.

1
2
3
4
// 不好的写法
var book = new Object();
book.title = "title";
bookl.author = "author";

当定义对象直接量时,常常在第一行包含左花括号,每一个属性的名值对都独占一行,并保持一个缩进.最后右花括号也独占一行.

1
2
3
4
5
 //好的写法
var book = {
"title": "title",
"author": "author"
}

数组直接量

不赞成显式地使用 Array 构造函数来创建数组

1
2
3
4
5
6
7
// 不好的写法
var colors = new Array("red", "green", "blue");
var numbers = new Array(1, 2, 3, 4);

// 好的写法
var colors = [ "red", "green", "blue" ];
var numbers = [ 1, 2, 3, 4 ];

文件读完了,我们就得开始尝试写文件了.关于读文件这里不再重复,可以参考Python读写文件之读文件

写文件

read() 相对应的就是 write(string) 了,将内容写进文件当中,返回值为 None.这里我们的测试文件同样是 test.txt, 只不过我们将其内容清空了.

不管读文件还是写文件,第一步都是需要打开文件的,同样关于访问模式这里也不再多说.

1
2
>>> f = open("test.txt","r+")
>>> f.write("1\n")

是不是这样子就将内容写入到文件中了呢?不急,让我们打开文件看一看:

为了和最初文件区分,特意将顶部部分截图没有截上.通过文件我们发现,我们似乎并没有将内容写到文本中,这是为什么呢?当我们操作做文件的时候,需要使用 f.close() 去关闭文件对象,然后释放由打开文件占用的系统资源,然后在关闭之前会将内容写到文本中去.

一旦关闭后,在尝试使用文件对象,则会失败.

如果留心看上面的 write()* 函数,会发现括号里面的内容是 String,所以如果你要想向文件写入非字符串的内容的话,第一步你需要做的是就是将它转换为 String 类型.

1
2
3
4
5
>>> f=open("test.txt","r+")
>>> value = ('the answer',42)
>>> s = str(value)
>>> f.write(s)
>>> f.close()


关于文件写的操作大概就是这样了,最后再介绍函数.

tell()

返回一个数字,文件指针当前在文件中的位置(当前位置到文件开头位置的字节数)
实例如下(在操作前我们已经清空文本):

1
2
3
4
5
6
7
>>> f=open("test.txt","r+")
>>> f.write("0123456789abcdef")
>>> f.seek(5) # 将指针移动到文件中的第6字节处
>>> f.read(1)
'5'
>>> f.tell()
6L

上述中我们还是用了一个函数 seek(),这是我们要提到的第二个额外的函数

seek(offset,from_what)

第一个参数 offset,顾名思义是偏移量,第二个参数这是指针从什么位置开始,0代表从头开始,1代表当前位置,2代表文件最末尾位置,默认值为0
实例如下(在操作前我们已经清空文本):

1
2
3
4
5
6
7
8
>>> f=open("test.txt","r+")
>>> f.write("0123456789abcdef")
>>> f.seek(5)
>>> f.read(1)
'5'
>>> f.seek(-3,2)
>>> f.read(1)
'd'

最后

推荐关于文件操作的时候使用 with 关键词.使用这个关键词的好处是,哪怕在处理过程中发生异常,文件也可以正常关闭.除此之外,它也比 try-finally 写起来要短.

读文件

打开文件

读写文件,自然第一步是打开文件,使用open( ),其返回一个文件对象,通常有两个参数

1
open(filena,mode)

第一个参数是文件名,第二个参数则是文件对象的访问模式.
访问模式可参照下图:

使用实例

测试文件名为 test.txt ,内容如下:

1
2
3
>>> f = open('test.txt', 'r')
>>> print f
<open file 'test.txt', mode 'r' at 80a0960>

文件打开之后自然就是开始重头戏,读文件了.读文件有很多方式.

读取文件方式

接上例,这里 f 为上述的文件对象

read()

1
2
3
4
>>> f.read()
'1\n2\n3\n4\n5\n6'
>>> f.read()
''

这里我们通过 f.read(size) 的方式去读取文件,其将读取的内容作为字符串返回.

size 是一个可选的参数,当 size 缺省或者为负数的时候,返回全部内容.

1
2
3
>>> f = open("test.txt","r")
>>> f.read(-1)
'1\n2\n3\n4\n5\n6'

size 值合法则返回指定字节的内容,前提是文件大小不超过你的内存,换句话说 read( ) 这种方式是将全部内容加载到内存中去.

1
2
3
4
5
6
7
>>> f = open("test.txt","r")
>>> f.read(1)
'1'
>>> f.read(2)
'\n2'
>>> f.read(4)
'\n3\n4'

当读到文件末尾的时候,f.read( ) 将会返回空字符串 (“”)

1
2
3
4
5
6
7
8
9
10
11
>>> f = open("test.txt","r")
>>> f.read(1)
'1'
>>> f.read(2)
'\n2'
>>> f.read(4)
'\n3\n4'
>>> f.read(4)
'\n5\n6'
>>> f.read(1)
''

readline()

f.read( ) 返回文件的单独一行.以 “\n” 作为每行的结束,最后一行的使不使用换行符则无所谓.
首先对最初的测试文件 test.txt 测试,其最初最后一行没有换行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> f = open("test.txt","r")
>>> f.readline()
'1\n'
>>> f.readline()
'2\n'
>>> f.readline()
'3\n'
>>> f.readline()
'4\n'
>>> f.readline()
'5\n'
>>> f.readline()
'6'
>>> f.readline()
''
>>>

然后这里我们将测试文件 test.txt ,在最后一行换行,如下图:

然后通过 readline( ) 测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> f = open("test.txt","r")
>>> f.readline()
'1\n'
>>> f.readline()
'2\n'
>>> f.readline()
'3\n'
>>> f.readline()
'4\n'
>>> f.readline()
'5\n'
>>> f.readline()
'6\n'
>>> f.readline()
''

通过上述的两个用例,我们可以发现和 read( ) 一样,如果返回空字符串,则说明到达文件末尾.当空白行被换行符分割,它将作为一个新的一行,这里就不测试了.

最后

通过循环去读取一个文件,是最快最有效率的方式,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> f = open("test.txt","r")
>>> for line in f:
print line


1

2

3

4

5

6

如果你想一次性将所有内容作为 list 返回,可以使用 list(f) 或者 f.readlines()

翻译自 MVP for Android: how to organize the presentation layer
原文地址

MVP(Model View Presenter) 模式是从著名的 MVC(Model View Controller)中衍生出来的.MVC 有段时间哪怕是现在在 Android 应用开发中也是有着举足轻重的作用.现在有越来越多的人们讨论它(MVP),但是却少有可靠的和结构清晰的信息.这就是为什么我想通过这篇博客来以带动讨论从而使得我们开动脑筋以期可以将它以最好的方式应用到我们项目当中去.

什么是 MVP

MVP 模式允许从逻辑上将展示层分离开,那就意味着接口是如何工作将会从如何展示中分离出来.理想状态下如果 MVP 模式实现这些逻辑,它将拥有完全不同的和可交互的视图.

首先需要澄清的是 MVP 不是一个结构化的模式,它只对展示层有效.无论如何在你的架构中使用它总归会更好一点.

为什么使用 MVP

在 Android 中我们有一个问题,问题来源于 Android 的活动与接口和数据访问机制紧密耦合.我们可以找一些极端的例子,比如说 CursorAdapter,其在使用光标的情况下混用了多个适配器,按道理说光标应该归入到深层的数据访问层.

对于一个应用来说如果想要易拓展和易维护的话,我们需要很好的定义各个分离的层.将来的趋势是什么,从一个 Web 服务器的数据库检索相同数据?我们需要重新装饰我们所有的视图.

MVP 模式可以让视图和我们的数据源独立开来.我们将应用分成不少于三个不同的图层(layers)来测试他们的独立性.
使用 MVP 我们可以将绝多数的逻辑从活动分离出来,那样的话,我们可以在没有使用任何仪器下测试.

如何在 Android 中实现 MVP

这是就是低耦合的开始.这里有许多不同种类的 MVP 并且每个人都可以调整模式去适应他们自己的需求,以便更舒服的使用它.模式种类基本上取决于我们给 presenter 的责任多少.

视图有责任去启用和禁用进度条,或者它由 presenter 去完成吗?又由谁决定在 Action Bar 展示什么行为?这就是最艰难的决定开始的地方.我将会介绍通常我是如何工作的.但是我更希望这篇文章成为更严格的应用 MVP 模式的指导方针的讨论的地方,因为我们都知道这里没有一个标准的方法去实现它.

The Presenter

presenter负责充当视图和模型之间的中间人.它从模型中检索数据,然后将它格式化的返回到视图中去.但是与典型的 MVC 模式不同的是,它同样决定当你与视图交互的时候发生什么.

The View

视图通常由一个活动(它可能是一个 Fragment,一个 View…取决于这个 app 是如何架构的)继承实现(implemented),其中包含一个 presenter 的引用.Presenter 将会提供一个独立的注入就像Dagger.但是如果你不使用这样的东西,它将负责创建一个 presenter 对象.视图需要做的唯一一件事情就是当一个接口执行的时候,调用 presenter 其中的方法.

The model

在拥有一个良好的分层架构的应用程序中,这种模式只会通往域层或业务逻辑.如果我们正在使用Uncle Bob clean architecture ,模型可能是一个实现了一个用例的交互器.但这是另一个话题,我想在以后的文章中讨论.现在,我们可以意识到它可以作为我们希望显示在视图中的那些数据的提供者.

例子

由于它解释起来有点冗长的,因此我在 Github 上创建了一个 MVP 样例,样例包含一个登陆页面验证数据,验证成功后访问包含从模型中检索的一系列表数据的主页.本文并没有对代码进行任何解释,因为代码相当简单.当然如果你觉得理解起来比较困难 的话,我将再写一篇文章详细的解释它.

结论

将界面与逻辑分离开在 Android 并不容易,但是 Model-View-Presenter 模式使得我们的活动,包含上百行甚至上千行的耦合的类得以解耦.在大型应用程序中组织好我们的代码至关重要.如果没有做好这点,维护和拓展就无从谈起.

此处的“类”指代所有的类、接口以及可复用代码块(traits)

4.1. 常量

类的常量中所有字母都必须大写,词间以下划线分隔。
参照以下代码:

1
2
3
4
5
6
7
8
<?php
namespace Vendor\Model;

class Foo
{
const VERSION = '1.0';
const DATE_APPROVED = '2012-06-01';
}

4.2. 属性

类的属性命名可以遵循 大写开头的驼峰式 ($StudlyCaps)、小写开头的驼峰式 ($camelCase) 又或者是 下划线分隔式 ($under_score),本规范不做强制要求,但无论遵循哪种命名方式,都应该在一定的范围内保持一致。这个范围可以是整个团队、整个包、整个类或整个方法。

4.3. 方法

方法名称必须符合 camelCase() 式的小写开头驼峰命名规范。

原文链接

关于Visio的UML的符号使用说明

  1. 继承(extends):用空心三角形+实线来表示。
  2. 实现接口(implements):用空心三角形+虚线来表示。(Visio中小圆形加实线)
  3. 关联(Association):用实线箭头来表示(Visio中实线,二元关联和关联类)
  4. 聚合(Aggregation):用空心的菱形+实线箭头来表示
  5. 组合(Composition):用实心的菱形+实线箭头来表示(Visio中没有)
  6. 依赖(Dependency):用虚线箭头来表示。(Visio中有调用、导入、实例化等7种依赖关系)。
  7. 基数:连线两端的数字表明这一端的类可以有几个实例,比如:一个鸟应该有两只翅膀。如果一个类可能有无数个实例,则就用’n’来表示。关联、聚合、组合是有基数的。
    此外Visio中还包括链接、约束、跟踪、精化、用法关系,包括信号、异常等符号。

各种类间的关系

种类间的关系:

  1. 关联:类A与类B的实例之间存在特定的对应关系
  2. 依赖:类A访问类B提供的服务
  3. 聚集:类A为整体类,类B为局部类,类A的对象由类B的对象组合而成
  4. 泛化:类A继承类B
  5. 实现:类A实现了B接口

关联可以分为三种:一对一关联、一对多关联、多对多关联,注意:关联还要以分为单向关联和双向关联。

聚合包括普通聚合和强聚合,强聚合不允许拆缷。

各种类关系示意图

创建测试表

这里创建两张测试表

创建 test_provinceid 表

1
2
3
4
5
6
7
8
CREATE TABLE `test_province` (
`id` CHAR(2) NOT NULL,
`pname` CHAR(6) NOT NULL,
PRIMARY KEY (`id`)
)
COMMENT='省份'
COLLATE='utf8_general_ci'
ENGINE=InnoDB;

创建 test_cityid 表

1
2
3
4
5
6
7
8
9
CREATE TABLE `test_city` (
`id` CHAR(2) NOT NULL,
`pid` CHAR(10) NOT NULL,
`cname` CHAR(10) NOT NULL COLLATE 'latin1_swedish_ci',
PRIMARY KEY (`id`)
)
COMMENT='城市'
COLLATE='utf8_general_ci'
ENGINE=InnoDB;

插入测试数据

1
2
3
4
5
6
7
INSERT INTO `test`.`test_province` (`id`, `pname`) VALUES ('AH', '安徽省');
INSERT INTO `test`.`test_province` (`id`, `pname`) VALUES ('ZJ', '浙江');
INSERT INTO `test`.`test_province` (`id`, `pname`) VALUES ('HN', '河南');
INSERT INTO `test`.`test_city` (`id`, `pid`, `cname`) VALUES ('01', 'AH', '合肥');
INSERT INTO `test`.`test_city` (`id`, `pid`, `cname`) VALUES ('04', 'AH', '六安');
INSERT INTO `test`.`test_city` (`id`, `pid`, `cname`) VALUES ('02', 'HN', '郑州');
INSERT INTO `test`.`test_city` (`id`, `pid`, `cname`) VALUES ('03', 'HB', '石家庄');

数据库中 test_province 数据现在有如下这些:

数据库中 test_city 数据现在有如下这些:

左连接

语法

1
2
3
SELECT *
FROM a LEFT JOIN b
ON a.key = b.key

左连接以左边的表作为主表,这里我们以 test_province 为主表,查看一个省份在 test_city 中有哪些城市

测试

1
select * from test_province a LEFT JOIN (select * from test_city) b on a.id = b.pid

执行结果


注意:这里将省份存在,城市不存在的数据也显示出来了

右连接

语法

1
2
3
SELECT *
FROM a RIGHT JOIN b
ON a.key = b.key

有上述的左连接的认知,我们不难得出,右连接是以右边的表作为主表的认识,这里我们将上面的语句简单修改一下

测试

1
select * from test_province a RIGHT JOIN (select * from test_city) b on a.id = b.pid

执行结果


注意:这里将城市存在,省份不存在的数据也显示出来了

内连接

语法

1
2
3
SELECT *
FROM a INNER JOIN b
ON a.key = b.key

左连接以左边的表作为主表,有连接以右边的表作为主表,还差一个以双方作为主表的连接,那就是内连接啦,内连接的想法很简单,你有我有大家都有才是真的有,同样简单修改一下语句

测试

1
select * from test_province a INNER JOIN (select * from test_city) b on a.id = b.pid

执行结果


注意: 这里只显示了城市和省份想匹配的数据

单例模式的定义

维基百科的定义

In software engineering, the singleton pattern is a design pattern that restricts the instantiation of a class to one object. This is useful when exactly one object is needed to coordinate actions across the system. The concept is sometimes generalized to systems that operate more efficiently when only one object exists, or that restrict the instantiation to a certain number of objects.

翻译后的意思

在软件工程中,单例模式是一种设计模式,它限制了一个类的只能实例化出唯一一个对象。这是十分有用的尤其一个对象需要协调整个系统运作.这个概念在有的时候会被广义的认知当且仅当只有一个对象存在是,系统将运行的更加有效率或者将对象的实例化限制在一定的数量下.

单例模式的 UML 类图

从上面的类图可以看出,单例模式中有一个构造函数 Singleton,但是这个构造函数是私有的 (‘-‘ 符号代表私有变量),然后有一个公有的方法 getInstance()
看完 UML 类图,接下来让我们实现一个简单的单例模式

单例模式的实现

java 的单例模式

1
2
3
4
5
6
7
pubic class Singleton{
private static final Singleton instance = new Singletion();

public static Singletion getInstance() {
return instance;
}
}

java的单例模式有七种写法,这里就不一一列举了,可以参考java 单例模式的七种写法

单例模式的特点以及应用场景

从上述 UML 类图以及代码不难总结出单例模式有以下特点:

  1. 单例模式下该类有且只有一个实例
  2. 单例模式下该类自己创建自己的唯一实例
  3. 单例模式下该类必须给其他对象提供获取该实例的方法

单例模式的应用可以从单例模式的优点说起,由于单例模式在系统运行过程中只存在唯一一个实例对象,可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能,在实际应用中 数据库通常使用单例模式较多,避免重复连接数据库造成资源的浪费

virtualenv 的安装

Ubuntu 下 virtualenv 的安装

Ubuntu 下的安装 python 的第三方依赖库就不得不提 python 的三大神器之 pip.默认情况下 Ubuntu 是没有安装的,可以在命令行下使用pip xxx 进行简单测试.

pip 的安装

pip 安装有很多方法,最简单的就是命令行输入下列命令,然后回车

1
sudo apt-get install python-pip

当然还有其他的安装方式,可以参考How to install pip

pip 安装完成之后,接下来就是重头戏了.

virtualenv 的安装

在命令行下输入如下命令,然后回车

1
sudo pip install virtualenv

同样安装方法有很多种,可以参考How to install virtualenv

virtualenv 的简单入门

创建虚拟环境

1
virtualenv ENV

创建一个名为 ENV的文件夹,在创建环境时会依赖系统环境的site-packages,如果想完全不依赖系统的site-packages,可以加上参数–no-site-packages来建立虚拟环境.
然后进入到ENV的目录下,目录下有如下文件夹:

1
2
$:~/ENV$ ls
bin include lib local pip-selfcheck.json

激活虚拟环境

1
source bin/activate

激活成功后,命令行前面会出现**(ENV)**的符号,如下图所示:

注意:

  1. ENV/libENV/include 包含了该环境下所支持的库.在当前环境下安装的包将会保存在 ENV/lib/pythonX.X/site-packages
  2. ENV/bin 则是 编译器所在之处了,在当前环境下你的 python 脚本文件的头部写法应该是这样的 #!/path/to/ENV/bin/python
  3. 在 virtualenv 环境下 pip 和 setuptools 是默认安装的,然后你可以很容易的安装其他的三方库

退出虚拟环境

1
deactivate

Windows 下 virtualenv 的安装

windows 下安装 virtualenv 跟Ubuntu下安装大同小异,实现安装好 pip,然后将其路径添加到系统环境变量,然后紧接着安装 virtualenv ,然后完成之后尝试在任意路径新建新的虚拟环境了.

为什么要使用 virtualenv

老实说最初我并不怎么喜欢使用 virtualenv,但是随着后来写的代码越来越多,项目所依赖的第三库也各不相同,需要别人去协助的我的时候,由于没有使用virtualenv,导致本地的 python 中的三方库十分的多,这样导致还需要人工去判断当前项目使用哪些库何其麻烦.而 virtualenv 的作用就是用来建立一个虚拟的python环境,一个专属于项目的python环境,保持一个相当干净的环境
使用 virtualenv 后导出已安装的包,只需要如下命令:

1
pip freeze > <目录>/requirements.txt

然后别人安装你的依赖,也只需要一个简单的命令:

1
pip install -r requirements.txt

使用的PHP版本:5.5.12

PHP中关于 比较运算符有很多个,这里就不一一列举,不过今天在程序中使用了类似下面的代码,然后出现的错误

1
2
3
if($variable1 <= $variable2 <= $variable3){
//coding ...
}

错误信息

1
Parse error: syntax error, unexpected '<=' (T_IS_SMALLER_OR_EQUAL)

然后尝试将中间的比较运算符,换成其他的比较运算符,仍然出现相同错误,最后将代码修改为

1
2
3
if(($variable1 <= $variable2) && ($variable2 <= $variable3)) {
//coding...
}