Leetao's Blog

Talk is cheap, show me the code

0%

下载 go-pear.phar 文件

可以直接访问 http://pear.php.net/go-pear.phar 网站,然后右键保存为 go-pear.phar 文件,将该文件保存在 php.exe 所在文件目录下

安装 pear

运行如下命令:(在此之前确保, php 已经配置到环境变量当中)

以管理员身份运行

1
php go-pear.phar

然后提示安装完成

导入注册表

安装完成之后,最后控制台会输出下述内容:

1
2
3
4
5
* WINDOWS ENVIRONMENT VARIABLES *
For convenience, a REG file is available under E:\wamp\bin\php\php5.6.19PEAR_ENV.reg .
This file creates ENV variables for the current user.

Double-click this file to add it to the current user registry.

上述内容的意思,在当前目录下有一个名为PEAR_ENV.reg的注册表,双击导入注册表,到此为止,安装完成

安装 Pear

可以参考 Windows下PHP安装PEAR

安装 PHPunit

准备工作

在命令行下输入以下命令:

1
pear list

获得如下结果:

如果没有以上安装包,请依次安装,安装命令如下:

1
pear install xxxx

安装 PHPunit

上述准备工作完成之后,就开始重头戏,安装 PHPunit, 输入如下命令行:

1
2
pear channel-discover pear.phpunit.de
pear install phpunit/PHPUnit

很遗憾的是,我通过该方法安装失败了,改用下列方法成功安装

  1. 下载 https://phar.phpunit.de/phpunit.phar 并将文件保存到 php.exe 所在文件目录下
  2. 在当前目录下打开命令行
  3. 建立外包覆批处理脚本
1
echo @php "%~dp0phpunit.phar" %* > phpunit.cmd

然后测试,是否安装成功:

1
phpunit --version

出现上图就说明我们已经大功告成了!

单行注释

单行注释以两个斜线开始,以行尾结束.单行注释有三种使用方法:

  1. 独占一行的注释,用来解释下一行代码。这行注释之前总有一个空行,且缩进层级和下一行代码保持一致.
  2. 在代码行的尾部的注释。代码结束到注释之间至少有一个缩进。注释(包括之前的代码部分)不应该超过单行最大字符数限制,如果超过了,就将这条注释放置于当前代码行的上方。
  3. 被注释的打断代码

多行注释

多行注释可以包裹跨行文本。

使用注释

添加注释的一般原则是,在需要让代码变得更清晰时添加注释。

1
2
3
4
5
6
7
8
9
10

// 不好的写法

// 初始化 count
var count = 10;

// 好的写法

// 改变这个值可能会让它变成青蛙
var count = 10;

使用注释的场景

  1. 难于理解的代码通常都应当加注释。
  2. 当代码看上去有错误的时应当加注释。
  3. 应当对方法、期望的从桉树和可能的返回值添加注释描述。
  4. 应当对自定义类型和期望的参数添加注释描述。
  5. 应当对包含文档花方法的对象添加注释描述。

查看 CentOS 7的 yum 是否有 mysql-server 文件

打开控制台,输入如下命令行:

1
yum list | grep mysql

注意:确保能上网, YUM 源配置到了 Internet 上,如果 Linux 无法上网则无法正确使用 yum 命令.

下载与安装

CentOS 7的yum源中没有正常安装MySQL时的mysql-sever文件,需要去官网上下载,官网地址
然后从下列文件选择其中一个文件下载:

然后在文件所在文件下,打开命令行使用如下命令(这里以 mysql57-community-release-el7-9.noarch.rpm 为例):

1
2
rpm -ivh mysql57-community-release-el7-9.noarch.rpm
yum install install mysql-server

然后一路 yes 就 ok 了~
安装之后重启 mysql 服务

1
service mysqld restart

数据库安装成功了,接下来就是配置数据库了.

配置数据库

获取临时密码

为了加强安全性, MySQL 为 root 用户随机生成了密码,用于初次登录,使用如下命令获取临时密码:

1
grep "password" /var/log/mysqld.log


然后将密码复制下来~,接着进入数据库:

1
mysql -u root -p

然后安装命令行提示输入密码,成功进入数据库,然后尝试 show databases,你会发现提示你修改密码

修改密码

修改密码的命令行如下:

1
SET PASSWORD FOR 'root'@'localhost' = PASSWORD('Your new password')

注意:如果你的密码设置的过于简单就会出现下列错误:

1
ERROR 1819 (HY000): Your password does not satisfy the current policy requirements

之所以会出现这个问题,跟 validate_password_policy 的值有关
validate_password_policy 有以下取值:

1
Policy Tests Performed 0 or LOW Length 1 or MEDIUM Length; numeric, lowercase/uppercase, and special characters 2 or STRONG Length; numeric, lowercase/uppercase, and special characters; dictionary file

默认是1,即MEDIUM,所以刚开始设置的密码必须符合长度,且必须含有数字,小写或大写字母,特殊字符.老实说平时数据库密码都很简单,方便记忆,所以这里同样为了可以设置简单的密码,需要修改两个全局参数.

修改 validate_password_policy 值

1
2
mysql> set global validate_password_policy=0;
Query OK, 0 rows affected (0.00 sec)

修改完这个之后,判断密码的标准就基于密码的长度了.这个由 validate_password_length 来决定.

修改 valitdate_password_length

validate_password_length 参数默认为8,它也有最小值的限制,最小值为:

1
2
3
validate_password_number_count 
+ validate_password_special_char_count
+ (2 * validate_password_mixed_case_count)

其中,validate_password_number_count 指定了密码中数据的长度,alidate_password_special_char_count 指定了密码中特殊字符的长度,validate_password_mixed_case_count 指定了密码中大小字母的长度,由于这些参数默认值均为1,所以 validate_password_length 最小值为4.

1
2
mysql> set global validate_password_length= 4;
Query OK, 0 rows affected (0.00 sec)

如果将值设置为 < 4 ,valiadate_password_length 将仍为4.将上述两项设置完成之后,就可以设置一个长度为4位的简单 mysql 密码了.

缩进层级

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

语句结尾

每行代码结尾使用分号.

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接口

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

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

各种类关系示意图