Top 10 Algorithms for Coding Interview

transferred from: http://www.programcreek.com/2012/11/top-10-algorithms-for-coding-interview/

The following are top 10 algorithms related topics for coding interviews. As understanding those concepts requires much more effort, this list below only serves as an introduction. They are viewed from a Java perspective and the following topics will be covered: String/Array/Matrix, Linked List, Tree, Heap, Graph, Sorting, Recursion vs. Iteration, Dynamic Programming, Bit Manipulation, Probability, Combinations and Permutations, and other problems that need us to find patterns.

1. String/Array/Matrix

First of all, String in Java is a class that contains a char array and other fields and methods. Without code auto-completion of any IDE, the following methods should be remembered.

toCharArray() //get char array of a String
Arrays.sort()  //sort an array
Arrays.toString(char[] a) //convert to string
charAt(int x) //get a char at the specific index
length() //string length
length //array size 
substring(int beginIndex) 
substring(int beginIndex, int endIndex)
Integer.valueOf()//string to integer
String.valueOf()/integer to string

Strings/arrays are easy to understand, but questions related to them often require advanced algorithm to solve, such as dynamic programming, recursion, etc.

Classic problems:
1) Evaluate Reverse Polish Notation
2) Longest Palindromic Substring
3) Word Break
4) Word Ladder
5) Median of Two Sorted Arrays
6) Regular Expression Matching
7) Merge Intervals
8) Insert Interval
9) Two Sum
9) 3Sum
9) 4Sum
10) 3Sum Closest
11) String to Integer
12) Merge Sorted Array
13) Valid Parentheses
14) Implement strStr()
15) Set Matrix Zeroes
16) Search Insert Position
17) Longest Consecutive Sequence
18) Valid Palindrome
19) Spiral Matrix
20) Search a 2D Matrix
21) Rotate Image
22) Triangle
23) Distinct Subsequences Total
24) Maximum Subarray
25) Remove Duplicates from Sorted Array
26) Remove Duplicates from Sorted Array II
27) Longest Substring Without Repeating Characters
28) Longest Substring that contains 2 unique characters
29) Palindrome Partitioning

2. Linked List

The implementation of a linked list is pretty simple in Java. Each node has a value and a link to next node.

class Node {
	int val;
	Node next;   Node(int x) {
		val = x;
		next = null;
	}
}

Two popular applications of linked list are stack and queue.

Stack

class Stack{
	Node top;   public Node peek(){
		if(top != null){
			return top;
		}   return null;
	}   public Node pop(){
		if(top == null){
			return null;
		}else{
			Node temp = new Node(top.val);
			top = top.next;
			return temp;	
		}
	}   public void push(Node n){
		if(n != null){
			n.next = top;
			top = n;
		}
	}
}

Queue

class Queue{
	Node first, last;   public void enqueue(Node n){
		if(first == null){
			first = n;
			last = first;
		}else{
			last.next = n;
			last = n;
		}
	}   public Node dequeue(){
		if(first == null){
			return null;
		}else{
			Node temp = new Node(first.val);
			first = first.next;
			return temp;
		}	
	}
}

It is worth to mention that Java standard library already contains a class called “Stack“, and LinkedListcan be used as a Queue (add() and remove()). (LinkedList implements the Queue interface) If you need a stack or queue to solve problems during your interview, you can directly use them.

Classic Problems:
1) Add Two Numbers
2) Reorder List
3) Linked List Cycle
4) Copy List with Random Pointer
5) Merge Two Sorted Lists
6) Merge k Sorted Lists *
7) Remove Duplicates from Sorted List
8) Partition List
9) LRU Cache

3. Tree & Heap

Tree here is normally binary tree. Each node contains a left node and right node like the following:

class TreeNode{
	int value;
	TreeNode left;
	TreeNode right;
}

Here are some concepts related with trees:

  1. Binary Search Tree: for all nodes, left children <= current node <= right children
  2. Balanced vs. Unbalanced: In a balanced tree, the depth of the left and right subtrees of every node differ by 1 or less.
  3. Full Binary Tree: every node other than the leaves has two children.
  4. Perfect Binary Tree: a full binary tree in which all leaves are at the same depth or same level, and in which every parent has two children.
  5. Complete Binary Tree: a binary tree in which every level, except possibly the last, is completely filled, and all nodes are as far left as possible

Heap is a specialized tree-based data structure that satisfies the heap property. The time complexity of its operations are important (e.g., find-min, delete-min, insert, etc). In Java, PriorityQueue is important to know.

Classic problems:
1) Binary Tree Preorder Traversal
2) Binary Tree Inorder Traversal
3) Binary Tree Postorder Traversal
4) Word Ladder
5) Validate Binary Search Tree
6) Flatten Binary Tree to Linked List
7) Path Sum
8) Construct Binary Tree from Inorder and Postorder Traversal
9) Convert Sorted Array to Binary Search Tree
10) Convert Sorted List to Binary Search Tree
11) Minimum Depth of Binary Tree
12) Binary Tree Maximum Path Sum *
13) Balanced Binary Tree

4. Graph

Graph related questions mainly focus on depth first search and breath first search. Depth first search is straightforward, you can just loop through neighbors starting from the root node.

Below is a simple implementation of a graph and breath first search. The key is using a queue to store nodes.

breath-first-search

1) Define a GraphNode

class GraphNode{ 
	int val;
	GraphNode next;
	GraphNode[] neighbors;
	boolean visited;   GraphNode(int x) {
		val = x;
	}   GraphNode(int x, GraphNode[] n){
		val = x;
		neighbors = n;
	}   public String toString(){
		return "value: "+ this.val; 
	}
}

2) Define a Queue

class Queue{
	GraphNode first, last;   public void enqueue(GraphNode n){
		if(first == null){
			first = n;
			last = first;
		}else{
			last.next = n;
			last = n;
		}
	}   public GraphNode dequeue(){
		if(first == null){
			return null;
		}else{
			GraphNode temp = new GraphNode(first.val, first.neighbors);
			first = first.next;
			return temp;
		}	
	}
}

3) Breath First Search uses a Queue

public class GraphTest {   public static void main(String[] args) {
		GraphNode n1 = new GraphNode(1); 
		GraphNode n2 = new GraphNode(2); 
		GraphNode n3 = new GraphNode(3); 
		GraphNode n4 = new GraphNode(4); 
		GraphNode n5 = new GraphNode(5);   n1.neighbors = new GraphNode[]{n2,n3,n5};
		n2.neighbors = new GraphNode[]{n1,n4};
		n3.neighbors = new GraphNode[]{n1,n4,n5};
		n4.neighbors = new GraphNode[]{n2,n3,n5};
		n5.neighbors = new GraphNode[]{n1,n3,n4};   breathFirstSearch(n1, 5);
	}   public static void breathFirstSearch(GraphNode root, int x){
		if(root.val == x)
			System.out.println("find in root");   Queue queue = new Queue();
		root.visited = true;
		queue.enqueue(root);   while(queue.first != null){
			GraphNode c = (GraphNode) queue.dequeue();
			for(GraphNode n: c.neighbors){   if(!n.visited){
					System.out.print(n + " ");
					n.visited = true;
					if(n.val == x)
						System.out.println("Find "+n);
					queue.enqueue(n);
				}
			}
		}
	}
}

Output:

value: 2 value: 3 value: 5 Find value: 5
value: 4

Classic Problems:
1) Clone Graph

5. Sorting

Time complexity of different sorting algorithms. You can go to wiki to see basic idea of them.

Algorithm
Average Time
Worst Time
Space

Bubble sort
n^2
n^2
1

Selection sort
n^2
n^2
1

Insertion sort
n^2
n^2

Quick sort
n log(n)
n^2

Merge sort
n log(n)
n log(n)
depends

* BinSort, Radix Sort and CountSort use different set of assumptions than the rest, and so they are not “general” sorting methods. (Thanks to Fidel for pointing this out)

Here are some implementations/demos, and in addition, you may want to check out how Java developers sort in practice.
1) Mergesort
2) Quicksort
3) InsertionSort.

6. Recursion vs. Iteration

Recursion should be a built-in thought for programmers. It can be demonstrated by a simple example.

Question:

there are n stairs, each time one can climb 1 or 2. How many different ways to climb the stairs?

Step 1: Finding the relationship before n and n-1.

To get n, there are only two ways, one 1-stair from n-1 or 2-stairs from n-2. If f(n) is the number of ways to climb to n, then f(n) = f(n-1) + f(n-2)

Step 2: Make sure the start condition is correct.

f(0) = 0;
f(1) = 1;

public static int f(int n){
	if(n <= 2) return n;
	int x = f(n-1) + f(n-2);
	return x;
}

The time complexity of the recursive method is exponential to n. There are a lot of redundant computations.

f(5)
f(4) + f(3)
f(3) + f(2) + f(2) + f(1)
f(2) + f(1) + f(2) + f(2) + f(1)

It should be straightforward to convert the recursion to iteration.

public static int f(int n) {   if (n <= 2){
		return n;
	}   int first = 1, second = 2;
	int third = 0;   for (int i = 3; i <= n; i++) {
		third = first + second;
		first = second;
		second = third;
	}   return third;
}

For this example, iteration takes less time. You may also want to check out Recursion vs Iteration.

7. Dynamic Programming

Dynamic programming is a technique for solving problems with the following properties:

  1. An instance is solved using the solutions for smaller instances.
  2. The solution for a smaller instance might be needed multiple times.
  3. The solutions to smaller instances are stored in a table, so that each smaller instance is solved only once.
  4. Additional space is used to save time.

The problem of climbing steps perfectly fit those 4 properties. Therefore, it can be solve by using dynamic programming.

public static int[] A = new int[100];   public static int f3(int n) {
	if (n <= 2)
		A[n]= n;   if(A[n] > 0)
		return A[n];
	else
		A[n] = f3(n-1) + f3(n-2);//store results so only calculate once!
	return A[n];
}

Classic problems:
1) Edit Distance
2) Longest Palindromic Substring
3) Word Break
4) Maximum Subarray

8. Bit Manipulation

Bit operators:

OR (|)
AND (&)
XOR (^)
Left Shift (<<)
Right Shift (>>)
Not (~)

1|0=1
1&0=0
1^0=1
0010<<2=1000
1100>>2=0011
~1=0

Get bit i for a give number n. (i count from 0 and starts from right)

public static boolean getBit(int num, int i){
	int result = num & (1<<i);   if(result == 0){
		return false;
	}else{
		return true;
	}
}

For example, get second bit of number 10.

i=1, n=10
1<<1= 10
1010&10=10
10 is not 0, so return true;

Classic Problems:
1) Find Single Number
2) Maximum Binary Gap

9. Probability

Solving probability related questions normally requires formatting the problem well. Here is just a simple example of such kind of problems.

There are 50 people in a room, what’s the probability that two people have the same birthday? (Ignoring the fact of leap year, i.e., 365 day every year)

Very often calculating probability of something can be converted to calculate the opposite. In this example, we can calculate the probability that all people have unique birthdays. That is: 365/365 * 364/365 * 363/365 * … * 365-n/365 * … * 365-49/365. And the probability that at least two people have the same birthday would be 1 – this value.

public static double caculateProbability(int n){
	double x = 1;   for(int i=0; i<n; i++){
		x *=  (365.0-i)/365.0;
	}   double pro = Math.round((1-x) * 100);
	return pro/100;
}

calculateProbability(50) = 0.97

10. Combinations and Permutations

The difference between combination and permutation is whether order matters.

Example 1:

Given 5 numbers – 1, 2, 3, 4 and 5, print out different sequence of the 5 numbers. 4 can not be the third one, 3 and 5 can not be adjacent. How many different combinations?

Example 2:

Given 5 banaba, 4 pear, and 3 apple, assuming one kind of fruit are the same, how many different combinations?

Class Problems:
1) Permutations
2) Permutations II
3) Permutation Sequence

Some other problems need us to use observations to form rules to solve them:

1) Reverse Integer
2) Palindrome Number
3) Pow(x,n)
4) Subsets
5) Subsets II

You may also like …
  1. 面试10大算法汇总+常见题目解答
  2. LeetCode – Merge k Sorted Lists (Java)
  3. How to answer coding questions for your interview?
  4. Leetcode Solution of Iterative Binary Tree Postorder Traversal in Java

Composer PHP依赖管理的新时代

对于现代语言而言,包管理器基本上是标配。Java有Maven,Python有pip,Ruby有gem,Nodejs有npm。PHP的则是PEAR,不过PEAR坑不少:

  • 依赖处理容易出问题
  • 配置非常复杂
  • 难用的命令行接口

好在我们有Composer,PHP依赖管理的利器。它是开源的,使用起来也很简单,提交自己的包也很容易。

 

安装Composer

Composer需要PHP 5.3.2+才能运行。

$ curl -sS https://getcomposer.org/installer | php

这个命令会将composer.phar下载到当前目录。PHAR(PHP 压缩包)是一个压缩格式,可以在命令行下直接运行。

你可以使用--install-dir选项将Composer安装到指定的目录,例如:

$ curl -sS https://getcomposer.org/installer | php -- --install-dir=bin

当然也可以进行全局安装:

$ curl -sS https://getcomposer.org/installer | php
$ mv composer.phar /usr/local/bin/composer

在Mac OS X下也可以使用homebrew安装:

brew tap josegonzalez/homebrew-php
brew install josegonzalez/php/composer

不过通常情况下只需将composer.phar的位置加入到PATH就可以,不一定要全局安装。

声明依赖

在项目目录下创建一个composer.json文件,指明依赖,比如,你的项目依赖 monolog

{
    "require": {
        "monolog/monolog": "1.2.*"
    }
}

安装依赖

安装依赖非常简单,只需在项目目录下运行:

composer install

如果没有全局安装的话,则运行:

php composer.phar install

自动加载

Composer提供了自动加载的特性,只需在你的代码的初始化部分中加入下面一行:

require 'vendor/autoload.php';

模块仓库

packagist.org是Composer的仓库,很多著名的PHP库都能在其中找到。你也可以提交你自己的作品

高级特性

以上介绍了Composer 的基本用法。Composer还有一些高级特性,虽然不是必需的,但是往往能给PHP开发带来方便。

项目主页

更多信息请访问 Composer 的主页

 

1. 仅更新单个库

只想更新某个特定的库,不想更新它的所有依赖,很简单:

composer update foo/bar

此外,这个技巧还可以用来解决“警告信息问题”。你一定见过这样的警告信息:

Warning: The lock file is not up to date with the latest changes in composer.json, you may be getting outdated dependencies, run update to update them.

擦,哪里出问题了?别惊慌!如果你编辑了composer.json,你应该会看到这样的信息。比如,如果你增加或更新了细节信息,比如库的描述、作者、更多参数,甚至仅仅增加了一个空格,都会改变文件的md5sum。然后Composer就会警告你哈希值和composer.lock中记载的不同。

那么我们该怎么办呢?update命令可以更新lock文件,但是如果仅仅增加了一些描述,应该是不打算更新任何库。这种情况下,只需update nothing

$ composer update nothing
Loading composer repositories with package information
Updating dependencies
Nothing to install or update
Writing lock file
Generating autoload files

这样一来,Composer不会更新库,但是会更新composer.lock。注意nothing并不是update命令的关键字。只是没有nothing 这个包导致的结果。如果你输入foobar,结果也一样。

如果你用的Composer版本足够新,那么你可以直接使用--lock选项:

composer update --lock

2. 不编辑composer.json的情况下安装库

你可能会觉得每安装一个库都需要修改composer.json太麻烦,那么你可以直接使用require命令。

composer require "foo/bar:1.0.0"

这个方法也可以用来快速地新开一个项目。init命令有--require选项,可以自动编写composer.json:(注意我们使用-n,这样就不用回答问题)

$ composer init --require=foo/bar:1.0.0 -n
$ cat composer.json
{
    "require": {
        "foo/bar": "1.0.0"
    }
}

3. 派生很容易

初始化的时候,你试过create-project命令么?

composer create-project doctrine/orm path 2.2.0

这会自动克隆仓库,并检出指定的版本。克隆库的时候用这个命令很方便,不需要搜寻原始的URI了。

4. 考虑缓存,dist包优先

最近一年以来的Composer会自动存档你下载的dist包。默认设置下,dist包用于加了tag的版本,例如"symfony/symfony": "v2.1.4",或者是通配符或版本区间,"2.1.*"">=2.2,<2.3-dev"(如果你使用stable作为你的minimum-stability

dist包也可以用于诸如dev-master之类的分支,Github允许你下载某个git引用的压缩包。为了强制使用压缩包,而不是克隆源代码,你可以使用installupdate--prefer-dist选项。

下面是一个例子(我使用了--profile选项来显示执行时间):

$ composer init --require="twig/twig:1.*" -n --profile
Memory usage: 3.94MB (peak: 4.08MB), time: 0s

$ composer install --profile
Loading composer repositories with package information
Installing dependencies
  - Installing twig/twig (v1.12.2)
    Downloading: 100%

Writing lock file
Generating autoload files
Memory usage: 10.13MB (peak: 12.65MB), time: 4.71s

$ rm -rf vendor

$ composer install --profile
Loading composer repositories with package information
Installing dependencies from lock file
  - Installing twig/twig (v1.12.2)
    Loading from cache

Generating autoload files
Memory usage: 4.96MB (peak: 5.57MB), time: 0.45s

这里,twig/twig:1.12.2的压缩包被保存在~/.composer/cache/files/twig/twig/1.12.2.0-v1.12.2.zip。重新安装包时直接使用。

5. 考虑修改,源代码优先

当你需要修改库的时候,克隆源代码就比下载包方便了。你可以使用--prefer-source来强制选择克隆源代码。

composer update symfony/yaml --prefer-source

接下来你可以修改文件:

composer status -v
You have changes in the following dependencies:
/path/to/app/vendor/symfony/yaml/Symfony/Component/Yaml:
    M Dumper.php

当你试图更新一个修改过的库的时候,Composer会提醒你,询问是否放弃修改:

$ composer update
Loading composer repositories with package information
Updating dependencies
  - Updating symfony/symfony v2.2.0 (v2.2.0- => v2.2.0)
    The package has modified files:
    M Dumper.php
    Discard changes [y,n,v,s,?]?

为生产环境作准备

最后提醒一下,在部署代码到生产环境的时候,别忘了优化一下自动加载:

composer dump-autoload --optimize

安装包的时候可以同样使用--optimize-autoloader。不加这一选项,你可能会发现20%到25%的性能损失

如果你需要帮助,或者想要了解某个命令的细节,你可以阅读官方文档,或者查看JoliCode做的这个交互式备忘单

ElasticSearch入门笔记

ElasticSearch 是构建在Apache Lucene之上的的搜索引擎服务,开源(Apache2协议),分布式,RESTful。安装方便,使用简单。

官方站点:http://www.elasticsearch.com/
中文站点:http://es-cn.medcl.net/

1.安装

必须先安装Java环境,并设置 JAVA_HOME => C:\Program Files\Java\jdk1.6.0_18

elasticsearch-rtf 中文入门集成包 https://github.com/medcl/elasticsearch-rtf
使用git签出,下载到本地。windows下,执行bin下面的elasticsearch.bat。linux下,执行bin下面或者service下面elasticsearch。

Pyes https://github.com/aparo/pyes 更多客户端

Bottle http://bottlepy.org/docs/dev/

2.角色关系对照

elasticsearch 跟 MySQL 中定义资料格式的角色关系对照表如下

MySQL             elasticsearch
database                 index
table                         type

table schema mapping
row                          document
field                         field

3.索引映射

#创建索引
$ curl -XPUT http://localhost:9200/test-index
#创建Mapping
$ curl -XPUT http://localhost:9200/test-index/test-type/_mapping -d ‘{
    “properties” : {
        “name” : { “type” : “string” }
    }
}’

@route(‘/indexsetting/’)
def indexmapping():
“””索引映射”””
    conn = ES(‘127.0.0.1:9200’)
    conn.debug_dump = True
try:
#删除索引
        conn.delete_index(“test-index”)
except:
pass
#创建索引
    conn.create_index(“test-index”)
    mapping = {
           u’id’: {‘store’: ‘yes’,
‘type’: u’integer’},
           u’author’: {‘boost’: 1.0,
‘index’: ‘not_analyzed’,
‘store’: ‘yes’,
‘type’: u’string’},
           u’published’: {‘boost’: 1.0,
‘index’: ‘not_analyzed’,
‘store’: ‘yes’,
‘type’: u’datetime’},
           u’url’: {‘store’: ‘yes’,
‘type’: u’string’},
           u’title’: {‘boost’: 1.0,
‘index’: ‘analyzed’,
‘store’: ‘yes’,
‘type’: u’string’},
           u’content’: {‘boost’: 1.0,
‘index’: ‘analyzed’,
‘store’: ‘yes’,
‘type’: u’string’,
“term_vector” :”with_positions_offsets”}
}
#索引映射
    conn.put_mapping(“test-type”, {‘properties’:mapping},[“test-index”])
return “索引映射”

4.索引

#索引
$ curl -XPUT http://localhost:9200/test-index/test-type/1 -d ‘{
    “user”: “kimchy”,
    “post_date”: “2009-11-15T13:12:00”,
    “message”: “Trying out elasticsearch, so far so good?”
}’
#获取
$ curl -XGET http://localhost:9200/test-index/test-type/1
#删除
$ curl -XDELETE ‘http://localhost:9200/test-index/test-type/1&#8217;

@route(‘/indextest/’)
def indexTest():
“””索引测试”””
    conn = ES(‘127.0.0.1:9200’)
for item in Data().getData():
#添加索引
        conn.index(item,”test-index”, “test-type”,item[‘id’])
#索引优化
    conn.optimize([“test-index”])
#删除索引内容
    conn.delete(“test-index”, “test-type”, 2668090)
#更新索引内容
    model = conn.get(“test-index”, “test-type”, 2667371)
    model[“title”]=”标题修改测试”
    conn.update(model,”test-index”, “test-type”,2667371)
#刷新索引
    conn.refresh([“test-index”])
    q = MatchAllQuery()
    results = conn.search(query = q,indices=”test-index”,doc_types=”test-type”)
#    for r in results:
#        print r
return template(‘default.tpl’,list=results,count=len(results))

5.搜索

#lucene语法方式的查询
$ curl -XGET http://localhost:9200/test-index/test-type/_search?q=user:kimchy
#query DSL方式查询
$ curl -XGET http://localhost:9200/test-index/test-type/_search-d ‘{
    “query” : {
        “term” : { “user”: “kimchy” }
    }
}’
#query DSL方式查询
$ curl -XGET http://localhost:9200/test-index/_search?pretty=true -d ‘{
    “query” : {
        “range” : {
            “post_date” : {
                “from” : “2009-11-15T13:00:00”,
                “to” : “2009-11-15T14:30:00”
            }
        }
    }
}’
#查找全部索引内容
$ curl -XGET http://localhost:9200/test-index/test-type/_search?pretty=true

@route(‘/search/’)
@route(‘/search/<searchkey>’)
def search(searchkey=u”关键算法”):
“””索引搜索”””
    conn = ES(‘127.0.0.1:9200’)
#TextQuery会对searchkey进行分词
    qtitle = TextQuery(“title”, searchkey)
    qcontent = TextQuery(“content”, searchkey)
#发布时间大于”2012-9-2 22:00:00″
    qpublished=RangeQuery(ESRangeOp(“published”, “gt”,datetime(2012, 9, 2, 22, 0, 0)))
    h = HighLighter([‘<b>’], [‘</b>’], fragment_size=500)
#多字段搜索(must=>and,should=>or),高亮,结果截取(分页),排序
    q = Search(BoolQuery(must=[qpublished],should=[qtitle,qcontent]), highlight=h, start=0, size=3, sort={‘id’:{‘order’: ‘asc’}})
    q.add_highlight(“title”)
    q.add_highlight(“content”)
    results = conn.search(query = q,indices=”test-index”,doc_types=”test-type”)
list=[]
for r in results:
if(r._meta.highlight.has_key(“title”)):
            r[‘title’]=r._meta.highlight[u”title”][0]
if(r._meta.highlight.has_key(“content”)):
            r[‘content’]=r._meta.highlight[u”content”][0]
list.append(r)
return template(‘search.tpl’,list=list,count=results.total)
</searchkey>

6.设置

#创建索引,并设置分片和副本参数
$ curl -XPUT http://localhost:9200/elasticsearch/ -d ‘{
    “settings” : {
        “number_of_shards” : 2,
        “number_of_replicas” : 3
    }
}’

7.其他

#分词
curl -XGET ‘http://localhost:9200/test-index/_analyze?text=中华人民共和国&#8217;

例子程序下载

程序员/设计师能用上的 75 份速查表

75 份速查表,由 vikas 收集整理,包括:jQuery、HTML、HTML5、CSS、CSS3、JavaScript、Photoshop 、git、Linux、Java、Perl、PHP、Python、Ruby、Ruby on Rails、Scala、C#、SQLite、C++、C语言、Ubuntu、WordPress、Node.js、Oracle、NMAP、Mac OS X、Haskell、Unicode、PostgreSQL、Lisp、Matlab 等。

  速查表可能是图片,也可能是 PDF 文件,希望你能在这个列表中找到你所需要的。

  1) Cheat Sheets – jQuery

  2) Cheat Sheets – HTML

  3) Cheat Sheets – HTML5 Cheat Sheet

  4) Cheat Sheets – CSS

  5) Cheat Sheets – CSS2

  6) Cheat Sheets – CSS3

  7) Cheat Sheets – JavaScript

  8) Cheat Sheets – Linux

  9) Cheat Sheets –  Java

  10) Cheat Sheets – Java 8

  11) Cheat Sheets – Perl

  12) Cheat Sheets – PHP

  13) Cheat Sheets – Python

  14) Cheat Sheets – Ruby on Rails

  15) Cheat Sheets – Ruby

  16) Cheat Sheets – Scala Cheat sheets

  17) Cheat Sheets – SQL

  18) Cheat Sheets – My SQL

  19) Cheat Sheets – C#

  20) Cheat Sheets – SQLite

  21) Cheat Sheets – C++

  22) Cheat Sheets – Javascript and AJAX

  23) Cheat Sheets – C

  24) Cheat Sheets – Unix

  25) Cheat Sheets – Ubuntu

  26) Cheat Sheets – WordPress

  27) Cheat Sheets – Nodejs

  28) Cheat Sheets –  HTML and XHTML

  29) Cheat Sheets – XML

  30) Cheat Sheets – Oracle

  31) Cheat Sheets – NMAP

  32) Cheat Sheets – Mac OS X

  33) Cheat Sheets – Haskell

  34) Cheat Sheets – DOM

  35) Cheat Sheets – Drupal

  36) Cheat Sheets – Oracle DB

  37) Cheat Sheets – FireFox

  38) Cheat Sheets – Apache Ant

  39) Cheat Sheets – Apache hadoop

  40)  Cheat Sheets –Git

  41)  Cheat Sheets –Mathematica

  42) Cheat Sheets – RedHat Fedora

  43) Cheat Sheets – Unicode

  44) Cheat Sheets – PostgreSQL

  45) Cheat Sheets – MATLAB

  46) Cheat Sheets – Design Patterns
  47) Cheat Sheets – Django Reference Sheet
  48) Cheat Sheets – Flash Quick Reference
  49) Cheat Sheets – Fortran
  50) Cheat Sheets – Adobe flex 3 cheat sheet
  51) Cheat Sheets – Google Chrome Plus
  52) Cheat Sheets – HTTP
  53) Cheat Sheets – Internet Explorer
  54) Cheat Sheets – JavaEE6 Reference Sheet
  55) Cheat Sheets – LISP
  56) Cheat Sheets – Mootools 1.3 cheat sheets
  57) Cheat Sheets – Photoshop
  58) Cheat Sheets – Prototype
  59) Cheat Sheets – QT
  60) Cheat Sheets – Shell Scrip Cheat Sheet
  61) Cheat Sheets – Server Side Includes Qucik Reference
  62) Cheat Sheets – VBasic Quick Ref
  63) Cheat Sheets – ASPCheat Sheet
  64) Cheat Sheets – WebGL Reference
  65) Cheat Sheets – Microsoft NET
  66) Cheat Sheets – Flash ActionScript Quick Ref
  67)  Cheat Sheets – BlueprintCSS
  68) Cheat Sheets – Computer Science
  69) Cheat Sheets – Erlang
  70) Cheat Sheets – Java Server faces
  71) Cheat Sheets – JDBC Best Practices
  72) Cheat Sheets – Core-ASPNet
  73) Cheat Sheets – HTML5 IndexedDB
  74) Cheat Sheets – Using XML Java
  75) Cheat Sheets – HTML5 Canvas Web Standard

Linux操作系统Redhat系列与Debian系列

Linux操作系统的发行版本可以大体分为两类Redhat应该说是在国内使用人群最多的Linux版本,甚至有人将Redhat等同于Linux操作系统,而有些老鸟更是只用这一个版本的Linux操作系统。Debian,或者称Debian系列,包括Debian和Ubuntu等。Debian是社区类Linux操作系统的典范,是迄今为止最遵循GNU规范的Linux系统。

AD:51CTO学院:IT精品课程在线看!

想知道到Linux操作系统的真相么,想知道Linux操作系统中藏有的内在奥义么,只有我来给大家全面讲解介绍Linux操作系统。Linux操作系统的发行版本可以大体分为两类,一类是商业公司维护的发行版本,一类是社区组织维护的发行版本,前者以著名的Redhat(RHEL)为代表,后者以Debian为代表。

Redhat,应该称为Redhat系列,包括RHEL(Redhat Enterprise Linux,也就是所谓的Redhat Advance Server,收费版本)、Fedora Core(由原来的Redhat桌面版本发展而来,免费版本)、CentOS(RHEL的社区克隆版本,免费)。

Redhat应该说是在国内使用人群最多的Linux版本,甚至有人将Redhat等同于Linux操作系统,而有些老鸟更是只用这一个版本的Linux操作系统。所以这个版本的特点就是使用人群数量大,资料非常多,言下之意就是如果你有什么不明白的地方,很容易找到人来问,而且网上的一般Linux教程都是以Redhat为例来讲解的。

Redhat系列的包管理方式采用的是基于RPM包的YUM包管理方式,包分发方式是编译好的二进制文件。稳定 *** 方面RHEL和CentOS的稳定 *** 非常好,适合于服务器使用,但是Fedora Core的稳定 *** 较差,最好只用于桌面应用。

Debian,或者称Debian系列,包括Debian和Ubuntu等。Debian是社区类Linux操作系统的典范,是迄今为止最遵循GNU规范的Linux系统。Debian最早由Ian Murdock于1993年创建,分为三个版本分支(branch): stable, testing 和 unstable。

其中,unstable为最新的测试版本,其中包括最新的软件包,但是也有相对较多的bug,适合桌面用户。testing的版本都经过unstable中的测试,相对较为稳定,也支持了不少新技术(比如SMP等)。而stable一般只用于服务器,上面的软件包大部分都比较过时,但是稳定和安全 *** 都非常的高。

Debian最具特色的是apt-get / dpkg包管理方式,其实Redhat的YUM也是在模仿Debian的APT方式,但在二进制文件发行方式中,APT应该是最好的了。Debian的资料也很丰富,有很多支持的社区,有问题求教也有地方可去

Ubuntu严格来说不能算一个独立的发行版本,Ubuntu是基于Debian的unstable版本加强而来,可以这么说,Ubuntu就是一个拥有Debian所有的优点,以及自己所加强的优点的近乎完美的 Linux桌面系统。

根据选择的桌面系统不同,有三个版本可供选择,基于Gnome的Ubuntu,基于KDE的Kubuntu以及基于Xfc的Xubuntu。特点是界面非常友好,容易上手,对硬件的支持非常全面,是最适合做桌面系统的Linux发行版本。

Gentoo,伟大的Gentoo是Linux世界最年轻的发行版本,正因为年轻,所以能吸取在她之前的所有发行版本的优点,这也是Gentoo被称为最完美的Linux操作系统发行版本的原因之一。Gentoo最初由Daniel Robbins(FreeBSD的开发者之一)创建。

首个稳定版本发布于2002年。由于开发者对FreeBSD的熟识,所以Gentoo拥有媲美FreeBSD的广受美誉的ports系统 ——Portage包管理系统。不同于APT和YUM等二进制文件分发的包管理系统,Portage是基于源代 *** 分发的,必须编译后才能运行,对于大型软件而言比较慢。

不过正因为所有软件都是在本地机器编译的,在经过各种定制的编译参数优化后,能将机器的硬件 *** 能发挥到极致。Gentoo是所有Linux发行版本里安装最复杂的,但是又是安装完成后最便于管理的版本,也是在相同硬件环境下运行最快的版本。

最后,介绍一下FreeBSD,需要强调的是:FreeBSD并不是一个Linux系统!但FreeBSD与Linux的用户群有相当一部分是重合的,二者支持的硬件环境也比较一致,所采用的软件也比较类似,所以可以将FreeBSD视为一个Linux版本来比较。

FreeBSD拥有两个分支:stable和current。顾名思义,stable是稳定版,而 current则是添加了新技术的测试版。FreeBSD采用Ports包管理系统,与Gentoo类似,基于源代 *** 分发,必须在本地机器编后后才能运行,但是Ports系统没有Portage系统使用简便,使用起来稍微复杂一些。

FreeBSD的最大特点就是稳定和高效,是作为服务器 *** 作系统的最佳选择,但对硬件的支持没有Linux操作系统完备,所以并不适合作为桌面系统。如果只是需要一个桌面系统,而且既不想使用盗版,又不想花大量的钱购买商业软件。

那么就需要一款适合桌面使用的Linux发行版本了,如果不想自己定制任何东西,不想在系统上浪费太多时间,那么很简单,你就根据自己的爱好在ubuntu、kubuntu以及xubuntu中选一款吧,三者的区别仅仅是桌面程序的不一样。

如果需要一个桌面系统,而且还想非常灵活的定制自己的Linux系统,想让自己的机器跑得更欢,不介意在Linux操作系统安装方面浪费一点时间,那么选择就是Gentoo,尽情享受Gentoo带来的自由快感吧!

如果需要的是一个服务器系统,而且非常厌烦各种Linux操作系统的配置,只是想要一个比较稳定的服务器系统而已,那么你最好的选择就是CentOS了,安装完成后,经过简单的配置就能提供非常稳定的服务了。

如果需要的是一个坚如磐石的非常稳定的服务器系统,那么选择就是FreeBSD。如果需要一个稳定的服务器系统,而且想深入摸索一下Linux操作系统的各个方面的知识,想自己定制许多内容,那么推荐你使用Gentoo。

UML学习入门就这一篇文章

http://www.uml.org.cn/oobject/201309023.asp

 

1.1 UML基础知识扫盲

UML这三个字母的全称是Unified Modeling Language,直接翻译就是统一建模语言,简单地说就是一种有特殊用途的语言。

你可能会问:这明明是一种图形,为什么说是语言呢?伟大的汉字还不是从图形(象形文字)开始的吗?语言是包括文字和图形的!其实有很多内容文字是无法表达的,你见过建筑设计图纸吗?里面还不是很多图形,光用文字能表达清楚建筑设计吗?在建筑界,有一套标准来描述设计,同样道理,在软件开发界,我们也需要一套标准来帮助我们做好软件开发的工作。UML就是其中的一种标准,注意这可不是唯一标准,只是UML是大家比较推崇的一种标准而已,说不定以后有一个更好的标准可能会取代她呢!UML并不是强制性标准,没有法律规定你在软件开发中一定要用UML,不能用其它的,我们的目标是善用包括UML在内的各种标准,来提高我们软件开发的水平。

UML由1.0版发展到1.1、1.2、…,到现在的2.0、2.x,本书将会以2.x版本为基础开展讨论。网络上、书籍、还有各种UML工具软件,各自基于的UML版本可能会不一样,大家在学习过程中可能会有一些困惑,不过没关系,本课程在某些关键地方会描述1.x与2.x的差异。

UML有什么用?

有很多人认为,UML的主要用途就是软件设计!也有人认为,如果你不是开发人员,是难以理解UML的。

然而我第一次在实际工作中应用UML的却不是软件设计,而是软件需求分析!当时我们和客户面对面沟通调研需求的时候,直接用类图、顺序图、活动图、用例图等UML。我们并没有因此和客户无法沟通,反而是沟通得更加顺畅。客户在我们的引导下,很快就会读懂这些UML图,因为UML图,让我们和客户的沟通效率和效果更好!你可能觉得很神奇,在后续章节中,我将会为你逐一揭开神奇背后的“秘密”。

UML可帮助我们做软件需求分析和软件设计的工作,在我工作中大概各占了50%的比例,当然在你的实际工作中不一定是这样的比例。UML会让你的需求分析或者软件设计工作更上一层楼,本书将会介绍UML在需求分析方面的最佳实践。

告诉你一个秘密,UML应用于软件需求分析时,其学习门槛将会大大降低!语法复杂度会降低,而且你基本不需要掌握软件开发的知识。只要你对软件需求分析感兴趣,认真学习和应用UML,就很有机会成为软件需求分析高手。

UML的分类

结构型的图(Structure Diagram)

类图(Class Diagram)

对象图(Object Diagram)

构件图(Component Diagram)

部署图(Deployment Diagram)

包图(Package Diagram)

行为型的图(Behavior Diagram)

活动图(Activity Diagram)

状态机图(State Machine Diagram)

顺序图(Sequence Diagram)

通信图(Communication Diagram)

用例图(Use Case Diagram)

时序图(Timing Diagram)

本书所描述的UML的各种图的名字,以上述的为准。

UML各种图的中文译名,因为翻译的原因可能会有所不一样,如:Sequence Diagram和Timing Diagram有时候都会被译成“时序图”,这是最让人困扰的地方!Sequence Diagram 除了被译为顺序图,还有序列图的译法。

中国软件行业协会(CSIA)与日本UML建模推进协会(UMTP)共同在中国推动的UML专家认证,两个协会共同颁发认证证书、两国互认,CSIA与UMTP共同推出了UML中文术语标准,该标准全称为:CSIA-UMTP UML中文术语标准v1.0(本书后文将会简称为UML中文术语标准)。本书将会遵循UML中文术语标准,并且我们会同时给出中文译名和英文原名,大家要留意看英文名字噢,这样能帮助你不会被众多的中文译名混淆。

UML图为什么会分为结构型和行为型两种呢?

顾名思义,结构型的图描述的是某种结构,这种结构在某段时间内应该是稳定的,“静态”的;而结构型的图描述的是某种行为,是“动态”的。

分析系统需求时,我们会面对很多业务概念,它们之间会有某些关系,这些内容可以看成是“静态”的,我们可以利用UML的结构性的图来分析。同时,业务会涉及大量的流程、过程等,这些内容是“动态”的,我们可以用行为型的UML图来分析。

在我们软件设计时,我们需要考虑需要那些类、哪些构件、系统最后怎样部署等,这些内容可以看成是“静态”的,我们可以利用UML的结构型的图来设计。同时,我们也需要考虑软件如何和用户交互,类、构件、模块之间如何联系等“动态”内容,我们可以利用行为型的图来设计。

所谓“静态”和“动态”不是绝对的,下文我们将会进一步介绍结构型的UML和行为型的UML。通过下面的学习,你将会初步认识UML的各种图,你可能还会有很多问题,本章的主要目的是让你对UML有一个宏观的认识,带着你的问题继续阅读后面的章节吧!

1.2 结构型的UML(Structure Diagram)

类图(Class Diagram)

请看下面这个类图:

图 1.1 某模具系统类图

此图截取自某模具管理系统的业务概念分析图,图中一个一个的矩形就是类,这些类之间有各种线条连接,这些线条表示类之间的关系。类图是分析业务概念的首选,类图可能是使用率最高的UML图。

再看下面这个Person类图,这时软件设计时用到的一个图:

图 1.2 Person类图

该Person类有以下属性(Attribute):Name(姓名),Sex(性别),Department(部门)等,有以下操作(Operation):Work(工作)等。类有属性和操作,但用类图分析业务模型时,往往不需要使用操作,如图1.1中的类就只有属性。

Attribute有特性、特征等译法,Operation也称作方法,但本书遵循UML中文术语标准,即Attribute为属性,Operation为操作。

对象图(Object Diagram)

一般情况下只有在软件开发中才会使用到对象图,下面的内容以开发的角度来说明对象图,如果你没有开发经验,阅读起来可能有一点难度。

图1.2中的Person类,用代码实例化如下:

Person person = new Person();

……

类(Class)实例化后就是对象(Object),对象person是类Person的实例,上述代码可以用对象图表示如下:

图 1.3 Person类的对象图

对象图和类图的样子很相似,对象是类的实例化,“person : Person”表示对象person是类Person的实例。对象图往往只在需要描述复杂算法时才会使用,画出来的对象图往往不会只有一个对象,该图只画了一个对象,其目的是尽量简化以便读者的理解什么是对象图。

在需求分析工作中基本上不需要使用对象图,从严谨的角度来看某些情况下应该使用对象图,但我往往还是会用类图来处理,这样更加简便而且容易理解。我们将在类图一章再次讲解对象图。

构件图(Component Diagram)

构件图也叫组件图,两个名字均符合UML中文术语标准。

一辆汽车由轮子、发动机等物理部件组成,一个软件往往也是由很多“物理部件”(如:控件、重用构件等)组成的,构件图就是用来描述软件内部物理组成的一种图。下图是某权限构件设计图:

图 1.4 某权限构件设计图

图1.4右上方有这样标志 的矩形表示一个构件,构件可以再包含构件。

软件需求分析工作中,需要用到构件图的情况不是很多,以下情况除外:

1. 待开发的系统需要与第三方的系统、原有系统、某些老系统等交互,这时可用构件图描述交互要求。

2. 客户对软件设计有某些特殊要求,这时可用构件图来描述要求。

构件图有时不会单独使用,还会和部署图一起结合使用。

部署图(Deployment Diagram)

部署图是用来描述系统如何部署、本系统与其他系统是怎样的关系的一种图,如下图:

图 1.5 某24小时便利店的管理系统部署图

图中一个个立体的矩形是部署图的“节点”,一个节点表示一个物理的设备,节点之间的线条表示节点间的物理连接关系。

大部分客户都会具备一定的IT基础环境(如具备局域网、一些服务器、某些软件平台等),软件系统需要基于当前的IT基础环境来规划,这时我们可以使用部署图来做这个规划。

分析系统的需求,不能忽略系统架构、部署、IT架构等方面的要求,我们要基于客户当前的IT基础环境,做一个最符合客户利益的规划。

要活用构件图、部署图来分析需求,需要具备一定的IT基础架构知识和软件设计知识,如果你还不具备相关知识,那么可以考虑抓紧补充相关知识。不过需求分析工作更多的还是分析业务,提炼功能性需求,这部分工作能做好是相当不容易的事情。对于技术方面的非功能性需求分析,可交由有技术背景的专业人士负责。

包图(Package Diagram)

Package有“打包”的意思,包图的主要用途是“打包”类图。用类图描述业务概念时,很多时候会因为业务类太多,而导致类图非常庞大,不利于阅读,这时可以将某些类放入“包”中,通过包图来组织业务概念图。

下图是包图的一个示例:

图 1.6 包图

图中好像文件夹样子的就是一个“包”,包之间的线条表示包之间的关系。

1.3 行为型的UML(Behavior Diagram)

活动图、状态机图、顺序图处于三种不同的角度来描述流程,是分析业务流程的三种不同利器,下面将会逐一说明。

活动图(Activity Diagram)

我们将起床到出门上班这个过程画成活动图,可能是这样的:

图 1.7 起床到出门上班的活动图

活动图中的一个圆边框框表示一个“活动”,多个活动之间的带箭头线条表示活动的先后顺序,该图只是表达了一个顺序流程,活动图还可以表达分支结构。如果你以前曾学过流程图的话,你会发现活动图和流程图很相似。活动图可能是三种能表示流程的UML图中最接近我们思维习惯的一种,下面来学习另外两种能表达流程的图。

状态机图(State Machine Diagram)

状态机图又叫状态图,但状态图这个译名并没有译出Machine的意思。

状态机图从某个物品的状态是如何变化的角度来展示流程,下图某请假条审批流程:

图 1.8 请假处理流程

整个请假审批流程是围绕“请假条”这个物体进行的,随着不同的审批阶段,请假条具备不同的状态。我们分析业务流程时会发现很多流程其实是围绕某个物品进行的,这时可考虑使用状态机图。

顺序图(Sequence Diagram)

你去餐厅吃饭,向服务员点餐到服务员送菜上来,这个过程用顺序图可表示如下:

图 1.9 点菜的顺序图

该图有三个“小人”,每个“小人”下面的文字说明(如:顾客)表示其代表的角色。角色与角色之间有一些线条链接,表示角色之间是如何交互的。该图表示的意思是:顾客向服务员点菜后,服务员将点菜信息传递给厨师,然后厨师做菜,最后再由服务员送菜给你。

点菜过程涉及几个环节,每个环节均由不同的角色来负责,如果遇到类似的情况,你可以考虑使用顺序图来分析。用顺序图来分析的好处是能清晰表达整个过程所参与的角色,角色与角色之间的关系,各角色是如何被卷入这个过程当中的。

通信图(Communication Diagram)

UML1.1时,该图英文名为Collaboration Diagram;UML2.x时,英文名为Communication Diagram。将英文名字直接翻译,原来的英文名字可译为协作图,而新的英文名字译为通信图。

通信图是顺序图的另外一种画法,点菜的顺序图,如果用通信图来画可表示如下:

图 1.10 点菜的通信图

三个“小人”分表表示三种角色:顾客、服务员、厨师;角色之间有直线联系表示他们之间有关系;带序号的文字和箭头,表示角色之间传递的信息。

顺序图更强调先后顺序,通信图更强调相互之间的关系。我觉得顺序图实用性更好一点,比通信图能表达更多的信息,更容易读懂,在需求分析工作中我基本不会使用通信图。

用例图(Use Case Diagram)

下图是用例图的示意图:

图 1.11 用例图

用例图表达的是什么角色通过软件系统能做什么事情,我们可以使用用例图系统地表达软件系统的绝大部分需求。

时序图(Timing Diagram)

时序图也叫时间图,时序图是UML中文术语标准的说法,而时间图不是标准的说法。

时序图是表示某东西的状态随时间变化而变化的一种图,参见下图:

图 1.12 灯的开关状态随时间变化图

此图表示在0秒到30秒,灯的状态是关的,30-60秒灯的状态为开,60秒后状态为关。

在实际工作中我基本上没有试用过时间图。

下面通过这个表格来总结一下我在需求分析工作中应用各种UML图的情况:

表 1.1 各种UML图实际应用情况

上表是根据我的工作经验总结的,相信会适用于很多情况。但每个人的工作经历、情况、环境等不太一样,上表仅作参考。

1.4 如何学好UML?

UML的认识误区

误区一:认为UML主要用于软件设计。

前面的文章你可以看到,UML除了用于软件设计,还能用于需求分析,而本书就是专门来说明如何在需求分析工作中活用UML的。

误区二:客户无法理解UML,在需求分析中应用UML实际意义不大。

我还不熟悉UML时,确实也有这样的怀疑,而实际工作中发现UML恰恰成为与客户沟通的良好桥梁!UML其实不难读懂,只要稍加解释客户马上就能读懂。我在所有的项目需求分析工作中,都直接使用UML图与客户沟通,并且给客户签署的需求规格说明书中含有大量的UML图。

UML能直观、形象、严谨地描述出业务概念、业务流程、客户的期望和需求,只要稍加引导客户,客户将会很容易读懂UML,甚至会主动使用UML与项目组交流。我曾经遇到过客户向我们索要画UML图的工具,客户见识过UML的威力后,也想在自己实际工作中使用。

误区三:认为UML语法繁杂,难以学习和应用。

某些UML资料和书籍可能将UML说得过于复杂了,官方的UML标准资料也确实是枯燥难懂、人见人晕。我刚开始学习UML时,也看过一些UML书籍,觉得UML的语法太多、太复杂、太容易混淆了!

在实际工作中,其实经常需要用到的UML语法并不多,而且很容易掌握。当我们在需求分析方面应用UML时,需要掌握的语法更少(在软件设计方面应用UML时需要掌握稍多一点的语法)。“二八原则”在这里完全适用,我们经常用到的UML语法,其实只占全部语法的20%,而本书将会重点介绍实用性强的UML语法。

误区四:UML用途不大。

很多人推崇UML,但也有不少人士不太认可UML。不认可的原因主要是因为一些人士学习UML后,发现在实际工作中发挥的作用并不是很大,有时候不用UML效果更好。

我不敢说UML能帮助我们解决所有问题,至少从我的多年使用经验上来说,UML对于提升我的需求分析能力帮助还是很大的。有人之所以感觉UML不太好用,我觉得原因还是只掌握了UML的形而没有领会UML的神。UML的常用语法可能几天就能学会了,而要真正做到“thinking in UML”却没有这么容易,需要长期的锻炼。

我的学习经历

我读大学时没有听说过UML,出来工作两三年后才开始接触UML,当时的感觉就好像找到了新大陆,很想好好发掘一番!而我当时的运气还是相当不错的,我的上司是UML达人,他带领我参加了项目的需求分析工作。我很快就见识了UML威力,在他的言传身教之下,迅速掌握了UML。

在那个项目以后,我便独立担当了多个项目管理及需求分析工作,没有一个项目不应用UML,而且我毫不保留地传授UML知识给项目组的其他成员。多年的工作进一步磨练了自己,对UML在实际工作中的应用有了更深刻的认识,形成自己的一套方法。

我的UML知识绝大部分来自于工作实践,期间虽然也看过一些书籍,但对我的帮助很少。当然我最大的得益还是来自我的UML启蒙老师,他在实际工作中教会了我UML,帮助我踏上自我成长的道路。

我的UML学习最大体会就是:实践太重要了!如果有名师指导则会让你事半功倍!希望本书能成为你在实际工作中学习和应用UML的好帮手!

UML学习难点

学UML之难,不在于学习语法,关键是要改变思维习惯。UML是一种新的工具,但同时也是代表了一种新的先进的思考方法,如果不能掌握这样的方法,只能学到了UML的形,而没有掌握其神髓。

要用好UML,你需要在平时多多培养下面的能力:

1. 书面表达能力。

2. 归纳总结能力。

3. “面向对象”的思维能力和抽象能力。

平时你可以利用各种机会来提升第1和第2种能力,如多写写项目文档、写写日记或博客等,多思考和总结平时自己的工作得失等。

第3种能力说起来有点虚,大家在大学中可能也学过相关知识。训练这种能力的最好方法就是多应用类图,我们将会在类图的章节再重点介绍,通过实例来体会什么才叫“面向对象”!

本书将会重点培养你的这三种能力,只要你有进步之心,多练习、多实践、多思考、多总结,一定会取得长足进步!

1.5 小结

本章的主要目标是让你不需要阅读全书的情况下,就可以了解到UML的全貌,大概知道UML各种图的用途,同时给你说明学习UML的难点,为最终活用UML做好准备。下面我们一起来复习一下本章的主要内容:

UML是Unified Modeling Language的简称,是软件开发界的一套标准,UML不仅可用于软件设计,也可以用于软件需求分析。但UML并不是强制标准,我们应该善用包括UML在内的各种标准来提高我们的水平。

UML可分为两类:结构型、行为型,结构性的UML有:类图、对象图、构件图、部署图、包图,行为型的图有活动图、状态机图、顺序图、通信图、用例图、时间图。

类图是业务概念模型分析的有利武器,也是面向对象分析能力的强有力训练工具。

对象图在需求分析工作中并不常用。

构件图、部署图是分析IT基础架构、软件架构等方面需求的有利分析工具,但需要你具备IT基础架构、软件设计方面的知识和经验。

包图可用来组织类图,在需求分析工作中应用的机会不是很大。

活动图、状态机图、顺序图是分析业务流程的强力武器。活动图的表达思路与流程图很类似,很容易掌握,而且大部分情况下都可以使用活动图来分析业务流程;某流程如果是围绕某个物品进行,该物品在流程中转换多种状态,那么使用状态机图来分析是首选;用顺序图来分析的好处是能清晰表达整个过程所参与的角色,角色与角色之间的关系,各角色是如何被卷入这个过程当中的。

通信图可以看作是顺序图的另外一种表达形式,顺序图更强调先后顺序,通信图更强调相互之间的关系。而从我的工作经验看,顺序图更加实用一点。

有人会将用例图称作“公仔图”,用例图表达的是什么角色通过软件系统能做什么事情,我们可以使用用例图系统地表达软件系统的绝大部分需求。

时间图是表示某东西的状态随时间变化而变化的一种图,我在实际工作中很少有机会能用到这种图。

学UML之难,不在于学习语法,避免陷入UML的认识误区,多练习、多实践,培养良好的“think in UML”思想,锻炼面向对象分析的能力,成为活用UML的需求分析高手不远矣!

23种设计模式(超级简洁)

这个设计模式参考提供四人帮23种模式的快速参考,像原书《设计模式-可复用面向对象基础》中所陈述的,每个模式包括,类图,解释,使用说明和实际例子。

创建模式:用来构建对象以便能从实现系统解耦。

结构模式:用不同的对象组成大规模的对象结构。

行为模式:用来在对象中管理算法,关系,和责任。

对象层面:处理对象之间的关系,决定于运行期。

类层面:处理类的关系,决定于在编译期。

Chain of Responsibility责任链    对象行为

image

目的:

通过把多个接受请求的对象链接起来,给多个对象去执行请求的机会。

使用:

  • 允许多个对象处理一个请求,处理者不必是特定的对象。
  • 存在一组对象可以去处理请求,动态决定到底是哪一个处理者。
  • 可以接受潜在的情形是请求最终没有被任何Handler处理。

例子:

某些语言的异常处理使用此模式,当一个异常被方法抛出时,运行时检查那个方法有没有机制去处理异常。或者它应该沿调用堆栈向上传递。向上传递的过程持续,直到处理这个异常的代码找到,或者没有更外层的父类对象来处理异常。

Command命令   对象行为 

image

目的:

封装一个请求,允许它作为一个对象来对待, 这样允许请求被作为队列(Queuing)或回调(Callbacks)中的传统对象来处理

使用:

  • 你需要回调(Callbacks)功能
  • 多个请求需要在不同时间或不同顺序被处理
  • 请求日志需要记录
  • 发起者需要和处理命令的对象(Receiver)解耦。

例子:

工作队列广泛应用于异步处理的算法。通过命令模式,要执行的功能可以发给一个工作队列,而工作队列无需知道发起的请求的实际实现,排队的命令对象用自己的算法实现了队列期望的静态接口。

Interpreter解释器    类型行为

image

目的:

定义一个语法表示,和一种理解处理语法的机制

使用:

语法可以被解释成一颗大的语法树。

语法简单。

效率不重要。

语法和底层的表达式解耦。

例子:

文字类的冒险游戏,1980年代相当流行,提供了一个很好的例子。 很多都有简单的命令,比如“向下走”允许对游戏的遍历。这些命令可以嵌套,如此得以改变他们的意思。例如,“go in”将导致和”go up”不同的结果。基于命令和修饰符(终结和非终结表达式)创建出的命令等级系统,应用可以很容易映射很多不同命令到一个行为树。

Mediator调停者    对象行为  

image

目的:

通过封装不同的对象集之间的交互和通讯实现松耦合, 允许每个对象集的行为相对于其他对象独立的演化。

使用:

不同对象集之间通信事先被定义,并且复杂。

通信和控制的节点之间存在太多的关系。

例子:

邮件列表软件记录登记到邮件列表的用户,并提供一个入口,通过这个入口,任何人都可以给整个邮件列表发邮件。 没有调停者模式实现的话,那个人必须随时关注谁订阅了列表,而谁又没有订阅。通过实现调停者模式,系统可以从任何入口接受邮件,然后决定哪些接收者需要传送,而不需要发送者担心真正的接收名单。

Iterator迭代子    对象行为  

image

目的:

允许得到一个聚集的元素,而不使用聚集本身的底层实现。

使用:

只需要得到聚集的元素,而不用知道整个聚集的内部表现。

需要对所有元素的多个或并行遍历。

需要对不同聚集的一个统一接口。

不同迭代子的实现细节有细微差异存在。

例子:

Java对迭代子模式的实现允许用户可以遍历不同类型的数据集合,而不用担心集合的底层实现。由于客户端只和迭代子接口打交道,各种集合可以留出余地定义一个适合他们的接口。有些允许存取所有集合的数据,有些禁止了某些功能,比如移除一些元素。

Memento备忘录    对象行为  

image

目的:

允许存储和外化一个对象的内部状态以便以后可以恢复,而这一切并不破坏封装。

使用:

对象的内部状态必须存储以备以后某个时间恢复。

一个接口的内部状态不通过暴露实现就不能被使用。

封装边界必须被保留。

例子:

Undo功能可以通过备忘录模式得到很好实现。 在状态变化之前,通过序列化和反序列化一个对象的状态可以保留它的一个快照,如果用户需要undo操作的话,可以用这个快照恢复。

Observer观察者    对象行为  

image

目的:

当系统中的主题对象状态发生变化时,让一个或多个观察者对象得到通知。

使用:

一个或多个对象的状态发生变化应该激发其他对象的行为。

需要广播(消息)的能力。

一个须知是,对象无视得到通知的代价。

例子:

这个模式几乎可以在任何界面环境中见到,当应用中出现按钮,文字,和其他变量,应用一般登记为这些控件的观察者(Listener),当一个用户激发一个事件,比如点击一个按钮, 控件遍历所有它登记的观察者(Observers)并对每个观察者一一发送通知。

Strategy策略    对象行为  

image

目的:

定义一个系列的封装好的算法,可以互换以用来实现特定行为。

使用:

类之间的唯一区别是他们的行为。

需要一个算法的不同版本或演化。

算法存取或使用的数据不应该被暴露给调用方。

一个类的行为应该在运行时决定。

条件语句复杂而且难以维护,需要重构

例子:

当数据被导入到新系统的时候,不同的验证算法需要运行于数据之上。通过配置,导入和使用策略,决定哪些验证需要被运行的条件逻辑可以移除掉,(数据)导入可以和真实验证代码解耦。 这会允许我们导入数据时动态调用一个或多个策略。

State状态    对象行为  

image

目的:

对象的情景绑定到它的行为上,允许对象根据内部状态表现不同行为。

使用:

对象的行为受它的状态影响。

把对象行为绑定到状态的条件复杂。

状态间需要显式转换。

例子:

一个邮件对象可以有不同状态,这些状态会影响对象的功能, 如果状态是“待发”,那么调用send()将会发出邮件,而调用recallMessage()会抛出错误或什么也不干,然而,如果状态时“已发出”状态,那么调用send()会抛出错误或什么也不干,而调用recallMessage()会尝试发送提醒通知给接受者。为避免方法中出现条件转移语句, 会使用多个状态对象处理相对应于特定状态的行为实现。邮件对象里的调用方法会委派给适当的状态对像来处理。

Template Method模板方法    类行为  

image

目的:

标识识别一个算法的框架,允许实现类顶一个真正的行为。

使用:

需要算法的一个抽象的是实现。

所有子类的共同行为需要放置到共同类中。

父类应该统一调用所有子类中的行为。

大部分或所有子类需要实现一个共同的行为。

例子:

一个父类,InstantMessage,需要所有的方法去处理发送消息,但是要发送的序列化数据可能根据不同实现而不同,一个视频消息和一个文字消息需要不同的算法以使数据正确序列化。InstantMessage的子类可以产生他们各自的序列化实现方法。允许父类可以和他们一起作用,而无需知道各自的实现细节。

Visitor访问者    对象行为  

image

这个图漏了ConcreteVisitor到Visitor的继承关系,读者心中有数。

目的:

允许运行时对一组对象进行一种或几种操作,而把对象结构和操作解耦。

使用:

一个对象结构须有多个不相关的操作作用其上。

一个对象结构不能改变,当时作用其上的操作可以变。

操作必须要作用在对象结构的具体类上。

可以接受对象结构暴露出来的内部状态和操作。

操作需要能够作用在继承统一的接口集合的对象结构之上。

例子:

对不同地区的一组发票计算税收需要很多不同的计算逻辑。实现访问者模式允许计算税收逻辑可以和发票详细项目解耦(我的理解是发票有很多不同的部分,每个部分需要不同的计税方式)。访问发票项目等级结构的计算逻辑可以和地区的合适税率解耦。改变地区就是简单改变一个不同的访问者具体类。

Bridge桥梁    对象结构  

image_thumb[1]

目的:

定义一个抽象化角色对象结构 和一个独立的实现化角色对象结构,抽象化角色的对象通过委派到实现化角色的对象来实现自己的功能,同时又能灵活的改变实现化角色对象的引用,因此达到解耦。

使用:

抽象化角色和实现化角色不应该在编译期绑定。

例子:

JVM, Java虚拟机有他自己的本地方法,抽象了视窗,系统日志,和字节码执行的使用,这些方法的具体实现委派到JVM运行的操作系统之上。当一个应用指示JVM去渲染窗口,他委派这个渲染的调用去JVM的具体实现,JVM知道怎么样和操作系统通信,以便渲染这个窗口。

Adapter适配器    类和对象结构  

image

这个图中的Adapter接口是源角色,所以又叫Target

目的:

通过这个具体适配器对象,使原本接口不匹配而无法一起工作的类能够一起工作。

使用:

一个要使用的类不满足接口需要。

复杂的条件把对象的行为绑定到他的状态。

状态需要显式转换。

例子:

一个账单应用需要人事应用的接口,以便交换雇员数据。但是他们都有自己对雇员数据接口的实现, 另一个要命的地方他们的社会安全保障号码使用不同的格式,通过在两个应用之间创建一个适配器类, 允许他们使用本地的对象来通信,也可以在这个过程中转换SSN格式。

Composite组合    对象结构  

image

目的:

合成模式使客户端将单纯元素和复合元素同等看待,方便创建对象的树结构。

使用:

需要对象的等级结构时,

单纯对象和复合对象可以统一对待。

例子:

有时候购物车的显示内容是一个单独项目或多个项目的集合。将项目(item)实现为一个Composite接口的子类,我们就可以把集合项目和单纯项目统一对待,允许我们遍历对象树,调用每个项目的方法。通过调用一个节点的getCost方法,我们可以得到那个项目的金额,和所有子项目的金额。无论他们是单纯项目还是一组项目都可以统一被看待。

Decorator装饰    对象结构  

image

目的:

允许动态包装对象以便修改它们现有的责任和行为。

使用:

对象责任和行为应该可以被动态地修改。

具体实现应该和责任和行为解耦。

用继承的方式去修改变得不实际和不可能。

特定功能不应驻留在对象层次的上层。

一大堆小对象包裹在具体实现周围是可以接受的。

例子:

很多公司利用装饰模式搭建邮件系统,当一个邮件从公司内部发送到外部邮件地址时,邮件服务器自动在原来的内容加上版权和机密信息,如果是发往内部地址,就不添加。这种装饰允许邮件本身不发生改变,添加额外信息的判断在运行时被执行。

Flyweight享元    对象结构  

image

目的:

方便重用某些细粒度的对象,使对大量对象的使用更有效率。

使用:

很多相同的对象被使用而且存储代价比较高。

每个对象的主要状态可以外部存放。

有些共享对象可以代替很多不共享的对象。

每个对象的标识不重要。

例子:

系统允许用户定义他们自己的应用流程和布局经常需要跟踪记录很多数量的变量,页面和其他项目,而这些东西几乎没有差别,通过把这些东西做成享元对象,所有对象的实例可以共享内部状态,而把外部状态分开存放。内部状态将存储共享属性,比如一个对话框的视觉效果,可以装多少长度的数据,暴露出什么样的事件,外部状态会存储不共享的属性,比如项目的位置,怎样对用户的点击做出反应,怎么样处理事件。

Facade门面    对象结构  

image

目的:

为一个子系统里面的一组接口提供一个单独的接口

使用:

需要一个简单的接口提供对一个复杂系统的访问

在系统实现和客户之间存在过多的依赖

系统和子系统需要被分层

例子:

通过Web服务暴露一组功能,客户代码只需要关心通过web服务暴露给他们的一个简单接口,而不是隐藏在web服务层后面存在的可能有的复杂关系。用新数据通过一个单独的web服务调用去更新系统可能涉及和几个数据库和系统的通信,然而这一切细节门面模式隐藏起来。

Proxy代理    对象结构  

image

目的:

允许对象层的存取控制,像一个Pass through entity或placeholder object。

使用:

被代表的对象存在于系统的外部。

对象可以在需要时创建。

对真正要访问的对象提供存储控制。

当一个对象被存取时需要增加功能。

例子:

账本应用提供一个方式顾客可以用他们的银行对账单和银行记录进行对账。 使很多过程自动化,和第三方通信的真正的操作十分耗费资源,应该被限制执行次数。通过使用代理来表示通信对象,我们可以限制通信时间和间隔,更进一步的是,我们可以把复杂的通信对象的初始化操作隐藏在代理类里面,是调用代码和是实现细节解耦。

Abstract Factory抽象工厂    对象创建  

image

目的:

提供一个接口,为了创建某些特定对象,把创建调用委派给某些具体创建类。

使用:

对象的创建应独立于是使用它们的系统。

系统应该能够使用多个对象族。

一个族的对象应该被集中使用。

程序库发布的时候不会暴露出其实现细节。

具体类应该和客户端解耦。

例子:

邮件编辑器应该允许操作多种格式,包括纯文字,富格式文字,和HTML。 根据被使用的格式,不同的对象需要被创建。如果邮件是纯文字,那么主体对象表示的就是纯文字和一个附件对象只是把附件简单编码成Base64。如果邮件是HTML,那么主体对象表示HTML 编码文字,附件对象允许inline表示和标准附件。通过利用抽象工厂来创建,我们可以保证合适的对象族根据邮件的风格来创建。

Factory Method工厂方法    对象创建  

image

目的:

暴露一个方法来创建对象,允许子类来控制真正的创建过程。

使用:

一个类不知道他要创建的是什么对象。

子类可以指定需要创建的对象。

父类希望将创建的操作放在它的子类。

例子:

很多应用有一种user 和 group结构来做登录,当应用需要创建一个user,它将通常把创建的过程委派给多个工厂方法子类实现。父类User对象将处理每种User的大部分操作,但是子类将定义工厂方法处理不同种类的User创建的差别。系统可能有AdminUser和StandardUser对象,每一个继承了User,AdminUser对象执行某些任务保证使用权,而StandardUser却要限制使用权。

Builder建造者    对象创建  

image

目的:

允许根据一些容易替换的算法动态创建对象。

使用:

对象创建算法应该和系统解耦。

需要创建算法的多种表示。

添加新的创建行为而不改变主要代码。

需要创建过程的运行时控制。

例子:

一个文件传输应用应该使用很多不同的协议来发送文件,真正的传输对象要根据选择的传输协议。通过使用一个Builder,我们可以决定使用正确的Builder来实例化正确的对象。 如果设置里是FTP那么FTP Builder将被使用。

Prototype原型    对象创建  

image

目的:

根据一个已经存在的对象作为模板通过克隆来创建对象。

使用:

组合,创建和对象的表示应该和系统解耦。

要创建的类在运行时才决定。

一个对象内部存在有限数目的状态。

对象或需要的对象结构需要一致或非常接近其他已经存在的对象和对象结构。

一开始对每个对象的创建是一个很耗费的操作。

例子:

价格处理引擎常常需要寻找很多不同的配置参数,是初始化这个引擎来变得相对昂贵,当有几个这样的引擎实例需要初始化,使用多线程来导入数据。很多引擎的初始化耗费就会很高。通过使用原型模式,我们可以保证只有一个引擎的拷贝需要初始化,然后克隆这个实例来复制它。附加的好处是克隆可以流程化。

Singleton单例    对象创建

image

目的:

保证系统里只有一个类的实例。

使用:

只需要类的一个实例。

必须控制使用一个单独对象。

例子:

很多语言提供系统或环境对象,允许语言和本地操作系统交互。因为物理上在一个操作系统上运行的应用,只需要一个系统对象。单例模式被语言运行时来保证只有一个系统对象的拷贝被创建,只有合适的进程才可以访问它。

从php的缓冲区说起

先从php buffer开始讲起。
php buffer
php运行的结果先放入缓冲区(buffer),只有当缓冲区满了或者php运行完毕,才将数据输出去。

缓冲区是通过php.ini中的output_buffering变量控制。output_buffering的默认值是off,可以设置大于0的数值来打开buffer。
但是这里需要注意的是:
1)使用ini_set是无法修改buffer的设置。
2)不管php.ini中output_buffering设置,cli模式下的php始终默认是output buffering为关闭的。但是你可以通过ob_start()将buffer打开。

大家都说:ob_start()是将php buffer打开,ob_end_flush()是将php buffer关闭。但是php.ini中php buffer是关闭的,再次调用ob_end_flush()会报warning。

另外,ob_*系列的函数是操作php本身的输出缓冲区。可以使用ob_flush()将php 缓冲区的内容强制输出。

web server buffer
这里主要将apache和nginx的缓冲区。
1、apache buffer
当php的输出数据给apache服务器时,它也会做一层buffer(也将数据放入它的缓冲区,当缓冲区数据满或执行完毕时,才输出数据)。

若想关闭缓冲区,可以在php层使用flush()来强制将缓冲区数据输出。
fulsh() 的工作原理:在apache module的sapi下, flush会通过调用sapi_module的flush成员函数指针, 间接的调用apache的api: ap_rflush刷新apache的输出缓冲区, 当然手册中也说了, 有一些apache的其他模块, 可能会改变这个动作的结果.例如mod_gzip,可能自己进行输出缓冲区,这将导致flush()函数产生的结果不会立即被发送到客户端浏览器。

2、nginx buffer
nginx使用fastcgi缓冲区来缓冲数据。很遗憾的是,fastcgi是强制将buffer打开的,无法关闭缓冲区。

有人有可能会想,无法关闭可以将buffer设置的足够小,来使缓冲数据输出,达到无缓冲的效果。但是这个想法无法实现。
原因一:fastcgi buffer无法识别小于1k的数值。
原因二:受参数之间大小关系的影响。

具体可以看看fastcgi的一些buffer设置。
fastcgi_buffer_size是用来存储response的header数据。
fastcgi_buffers是用来存储response的内容数据.
fastcgi_busy_buffers_size是用来控制同时传输到客户端的buffer数量。一旦fastcgi_buffers设置的 buffer被写入,直到buffer里面的数据被完整的传输完(传输到客户端),这些buffer将会一直处在busy状态,我们不能对这些 buffer进行任何别的操作。所有处在busy状态的buffer size加起来不能超过fastcgi_busy_buffers_size。

参数之间大小关系:
fastcgi_busy_buffers_size<(all fastcgi_buffers – one buffer) 并且fastcgi_busy_buffers_size>=max (fastcgi_buffer_size, one fastcgi _buffers)。
例如,在nginx.conf配置中有:
fastcgi_buffers 4 128k
fastcgi_buffer_size 256k
那么fastcgi_busy_buffers_size<(4*128k – 4k) 并且fastcgi_busy_buffers_size>=max(256k, 128k)
其中,4k(one buffer的大小)是linux系统默认的缓存大小,即一个内存页。

若fastcgi_buffer_size设置的很小,会导致header过小的错误。你也同样无法保证设置的值会满足所有的情况。

要注意的是:
1)flush, 严格来讲, 这个只有在PHP做为apache的Module(handler或者filter)安装的时候, 才有实际作用. 它是刷新WebServer(可以认为特指apache)的缓冲区.所以在nginx下,flush()函数是无法起作用的。

browser buffer
在 php端无法关闭浏览器buffer(至少目前我没有研究出来,若有哪位高人研究出来了,请一定要记得分享)。为了使得数据及时输出,可以在发送真正内容 数据前,发送一些空格来填满浏览器的buffer。浏览器的buffer一满,就会将其他新输出的数据输出。但是不同的浏览器会设置不同的buffer大 小。为了保险期间,可以发送4096个空格,因为目前比较流行的浏览器的buffer还没有超过4k(一个内页大小)。

深入理解ob_flush和flush的区别

ob_flush/flush在手册中的描述, 都是刷新输出缓冲区, 并且还需要配套使用, 所以会导致很多人迷惑…

其实, 他们俩的操作对象不同, 有些情况下, flush根本不做什么事情..

ob_*系列函数, 是操作PHP本身的输出缓冲区.

所以, ob_flush是刷新PHP自身的缓冲区.

而flush, 严格来讲, 这个只有在PHP做为apache的Module(handler或者filter)安装的时候, 才有实际作用. 它是刷新WebServer(可以认为特指apache)的缓冲区.

在apache module的sapi下, flush会通过调用sapi_module的flush成员函数指针, 间接的调用apache的api: ap_rflush刷新apache的输出缓冲区, 当然手册中也说了, 有一些apache的其他模块, 可能会改变这个动作的结果..

  1. 有些Apache的模块,比如mod_gzip,可能自己进行输出缓存,
  2. 这将导致flush()函数产生的结果不会立即被发送到客户端浏览器。
  3.  
  4. 甚至浏览器也会在显示之前,缓存接收到的内容。例如 Netscape
  5. 浏览器会在接受到换行或 html 标记的开头之前缓存内容,并且在
  6. 接受到 </table> 标记之前,不会显示出整个表格。
  7.  
  8. 一些版本的 Microsoft Internet Explorer 只有当接受到的256个
  9. 字节以后才开始显示该页面,所以必须发送一些额外的空格来让这
  10. 些浏览器显示页面内容。

所以, 正确使用俩者的顺序是. 先ob_flush, 然后flush,

当然, 在其他sapi下, 不调用flush也可以, 只不过为了保证你代码的可移植性, 建议配套使用.

All the ways to perform HTTP requests in PHP

http://css.dzone.com/articles/all-ways-perform-http-requests

In the PHP world, HTTP is very important: not only for receiving requests, which is the job of PHP application, but also for performing them. ROT web services are probably the most popular way to interface with external systems nowadays.

We’ll make no assumptions on the payload of HTTP requests (which may be binary, text, JSON, XML), so you’ll have to deal with that yourself. In some cases, you may choose a more specific solution such as the SoapClient class in the SOAP extension, or the XML-RPC extension.

Native

There are several primitive for performing HTTP requests from PHP code:

  • the cURL extension relies on the most famous library and CLI tool for performing application-level requests like HTTP and FTP ones.
  • streams (stream_socket_client() or stream_context_create() as the main primitives) are an abstraction over several application protocols, like curl.
  • fsockopen() and other file-based functons like fwrite() work directly at the TCP level.
  • socket_*() primitives work with TCP too, and they are essentially a different API but do the same job as fsockopen().
  • The HTTP PECL extension is even object-oriented, but it is less commonly available than the cURL one.

The main difference between the various solutions is their availability. Streams have been part of PHP for 10 years, while cURL is an extension that might not be present on your server. cURL is actually pretty common and even included by default in Windows versions of PHP, but the point stands with regard to the other extension.

In short, it is possible you are not in the condition to rely on cURL because you are writing a portable library, or you do not have control on the server configuration.

However, it’s probably not worth the hassle to use fsockopen() if you’re talking over HTTP, as it would be handy for lower-level textual or binary protocols. So as an alternative to cURL, many libraries (and people) stick to streams.

Libraries

The problem with PHP libraries present on PEAR, SourceForge or GitHub is that there are too many of them (a picture worse than for PHP frameworks). If you choose the wrong one, you may be stuck with an old, not supported library: that’s vendor lock-in, a form of technical debt.

Look for activity in the library you choose: the commit log, number of watch and forks on Github, or tweets about the library.

These are the two libraries I’ve seen cited the most:

  • Buzz by Kris Wallsmith of Symfony fame.
  • Guzzle is a bit heavy in lines of code but features much documentation.

The second one contains some external dependency (Symfony components), but in any case both are managed with Composer (so you’ll need that in addition.)

Every PHP library will use one of the primitive methods we listed above, so check what backends are available before picking one.

As a good news, consider that a standard interface for HTTP clients has been proposed; this interface would make conforming libraries less riskier to use as you would always been able to switch to another implementing one in case your current dependency becomes abandoned or obsolete.

Frameworks

A simple way to avoid picking up new technical debt is to leverage some debt you already have. So if you’re using a framework, look at what it offers you.

For example, my last projects have involved Zend Framework as a dependency I found already inside the user space: lots of Zend_* classes at a require_once() distance for me. So I could just rely on these classes:

  • Zend Framework 1: Zend_Http_Client, with back ends targeting curl and streams. So you will be portable at a price of configuring the adapter.
  • Zend Framework 2: Zend\Http\Client, essentially the port of the same component with a new unified interface on top.

Conclusions

HTTP clients are a commodity: they have no killer feature, and they should perform their jobs at the least cost to us. So we should choose them according to non-functional metrics: project activity, API ease of use, and occasionally performance.

The testability aspect is taken care by creating a level of abstraction over these libraries, at least until a unified interface that allows for mocking of requests will be available. Note that in case of primitive functions like curl() you already have to build a little object-oriented layer of abstraction.

But you do not always have to use a library: curl() and stream_context_create() are very easy to wrap and if you only have to make a couple of types of HTTP requests, just stick to them. I do this myself in PHPUnit_Selenium, for calling the Selenium Server RC and WebDriver APIs: two Driver classes wrap the curl_*() primitive functions, which are hidden inside them and never referred upon in the rest of the codebase.

If instead the HTTP client role of your project results in dozens of different requests made by different components, check out a framework’s or a library solution to prevent the introduction of duplication derived from multiple wrapping. In this case, the only thing preventing you from including a library is your project’s policy (for example PHPUnit_Selenium ships as a portable package and as such cannot depend on non-PEAR projects without including their sources directly.)