安全加密认证基础知识

安全概述

在典型的场景中,安全主要用于解决4类需求:

  • 保密(Security/Confidentiality)
  • 鉴别/认证(Authentication)
  • 完整性(Integrity)
  • 不可否认性(non-repudiation)

对称加密概述

密钥:分为加密密钥和解密密钥
明文:没有进行加密,能够直接代表原文含义的信息
密文:经过加密处理之后,隐藏原文含义的信息
加密:将明文转换成密文的实施过程
解密:将密文转换成明文的实施过程
Mark-Down

对称还是非对称,只是K与K’是否相等

加密的方式

  • 置换加密
  • 转置加密
  • 乘积加密
    置换加密:原文的字母替换成一个另一个字母, 如:原文:abc 密文:xyz
    转置加密:字母还是那些字母,位置交换,如:abc 密文:cab
    乘积加密:置换加密和转置加密的乘积:如:abc 密文:zxy

非对称加密(公钥密码)概述

公钥和密钥都有一个产生,公钥公开,私钥保密。
公开密钥的算法的最重要两大数学基础:

  • 建立在分解大数的困难度
  • 建立在以大素数为模来计算离散对数的困难度

RSA

建立在分解大数的困难度
公钥/私钥长度 至少1024bit

密钥交换概述

对称加密的特点

  • 高效
  • 密钥交换的问题
  • 不如RSA的加密安全程度高,但是当选择256bit的AES,仍然能够胜任绝大多数的安全领域

非对称加密的特点

  • 安全性足够高
  • 没有密钥交换的问题
  • 效率低,对于大数据加密很慢

实际的保密会话应用场景

  • 基于高效的对称加密算法对会话进行加密(AES,256Bit Shared Key)
  • 会话密钥实时产生且周期性变化
  • 基于其他足够安全的方式进行会话密钥的传输和交换

利用公钥密码来交换会话密钥

  • 实时随机的会话密钥产生
  • 使用对端的公钥对产生的会话密钥加密并传递给对端
  • 对端使用私钥解密获取会话密钥
  • 双方开始基于共享多线程会话密钥进行对称加密的保密会话同性

Diffie-Hellman密钥交换协议

  • 基于以大素数为模计算离散对数的困难度
  • 双方各自选定Key,然后以一定算法变换(使得key不以明文传输)后传输给对方
  • 双方利用对方交换来的数据和自己选定的key做变换,获得一个一致的结果,作为会话密钥

Hash算法

Hash(散列)函数(算法)的定义是:变长的输入变换成定长的输出。
常见的Hash算法:MD5(128bit) 、 SHA1(160bit)
Hash的特点:

  • 易变性:即便原始信息发生1bit的变化,Hash的输出将会有不可预知的巨大变化
  • 不可逆:通过hash结果构造出满足的输入信息是不可能的或者及其困难的。
    实例:
    *文件下载时的MD5
  • 消息传送时尾部额外传MD
  • CRC校验的作用和不足
  • MD检验消息恶劣环境传输的完整性和未受损坏
  • 应用程序中对于核心文件/数据库读写的鲁棒性保护,防止掉电和Crash

HMAC (Hash-based Message Authentication Code)

遇到的问题:
直接尾部附带消息摘要的问题(篡改内容的同时篡改摘要)
直接对密码做Hash传输的认证的问题(重放攻击)
HMAC如何解决这个问题:
HMAC就是使用Key对原始消息变换后再进行HASH

电子签名

RSA利用私钥签名

HASH+公钥密码术:成就电子签名

  • RSA的低效率特性,导致即便是签名也不适合直接对原始信息进行签名
  • 利用HASH先完成消息摘要和完整性鉴别的作用
  • 而后对简单的消息摘要进行基于公钥密码术的签名
  • 签名一般附着于原始消息尾部或者头部一起发送

数字证书和PKI

公钥作为一个字段存储于数字证书中
证书的交换和传输即可传输/交换公钥
利用签名来保护数字证书本身
数字时代的信任关系: 一个受信任的证书列表

证书链和PKI

人的信任链: 孔子->孔子的徒弟->孔子徒弟的徒弟
数字时代的信任链:证书链
证书签名的不同点:根证书自签名,非根证书父签名
证书的限制:

  • 约束
  • 用途
  • 有效期

基于可信任证书的认证方式被广泛的应用在现代安全领域,比如:WIFI、HTTPS
在HTTPS中,典型的Client对Server的认证和鉴别基于可信任列表

RX前传

原型

根据给定的查询请求搜索到整个互联网上的猫的图片。每个图片包含可爱指数的参数。
我们的任务将会下载到一个猫的列表集合,选择最可爱的那个,然后把它保存到本地。

模型和API

猫的结构数据

1
2
3
4
5
6
7
8
9
public class Cat implements Comparable<Cat>{
Bitmap images;
int cuteness;

@Override
public int compareTo(Cat another){
return Integer.compare(cuteness, another.cuteness);
}
}

阻塞式API

1
2
3
4
public interface Api{
List<Cat> queryCats(String query);
Uri store(Cat cat);
}

业务逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class CatHelper{
Api api;

public Uri saveTheCutestCat(String query){
List<cat> cats = api.queryCats(query);
Cat cutest = findCutest(cats);
Uri savedUri = api.store(cutest);
return savedUri;
}

private Cat findCutest(List<Cat> cats){
return Collections.max(cats);
}
}

主方法savetheCutestCat只包含了3个其他方法, 提供了输入参数然后就能得到结果返回了,
在这个方法工作的时候我们需要等待它的完成。

优势:

组合:根据其他3个方法而新创建一个方法(saveTheCutestCat),因此组合了它们。
错误的传递:另外一个好处就是我们处理错误的方式,任何一个方法都可能因执行时发生错误而被终止,这个错误能在任何层次上被处理掉,Java中我们叫它抛异常。我们不需要为组合方法里的每个方法都做异常处理,仅需要对这些组合起来的方法做统一处理
1
2
3
4
5
6
7
8
try{
List<Cat> cats = api.queryCats(query);
Cat cutest = findCutest(cats);
Uri savedUri = api.store(cutest);
}catch(Exception e){
e.printStackTrace();
return someDefaultValue;
}




走向异步

异步的网络调用

1
2
3
4
5
6
7
8
9
public interface Api{
interface CatsQueryCallback{
void onCatListReceived(List<Cat> cats);
void onError(Exception e);
}

void queryCats(String query,CatsQueryCallback catsQueryCallback);
Uri store(Cat cat);
}

异步的获取猫的信息集合列表,返回正确或错误的结果时都会通过CatsQueryCallback回调接口。

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
public calss CatsHelper{
public interface CutestCatCallback{
void onCutestCatSaved(Uri uri);
void onQueryFailed(Exception e);
}

Api api;

public void saveTheCutestCat(String query, CutestCatCallback cutestCatCallback){
api.queryCats(query, new Api.CatsQueryCallback(){
@Override
public void onCatListReceived(List<Cat> cats){
Cat cutest = findCutest(cats);
Uri savedUri = api.store(cutest);
cutestCatCallback.onCutestCatSaved(savedUri);
}
@Override
public void onQueryFailed(Exception e){
cutestCatCallback.onError(e);
}
});
}

private Cat findCutest(List<Cat> cats){
return Collections.max(cats);
}
}

两个Api方法都调用异步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface Api{
interface CatsQueryCallback{
void onCatListReceived(List<Cat> cats);
void onQueryFailed(Exception e);
}

interface StoreCallback{
void onCatStored(Uri uri);
void onStoreFailed(Exception e);
}

void queryCats(String query, CatsQueryCallback catsQueryCallback);
void store(Cat cat, StoreCallback storeCallback);
}

helper

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
public class CatsHelper{
public interface CutestCatCallback{
void onCutestCatSaved(Uri uri);
void onError(Exception e);
}

Api api;

public void saveTheCutestCat(String query, CutestCatCallback cutestCatCallback){
api.queryCats(query, new Api.CatsQueryCallback(){
@Override
public void onCatListReceived(List<Cat> cats){
Cat cutest = findCutest(cats);
api.store(cutest, new Api.StoreCallback(){
@Override
public void onCatStored(Uri uri){
cutestCatCallback.onCutestCatSaved(uri);
}

@Override
public void onStoreFailed(Exception e){
cutestCatCallback.onCutestCatSaved(uri);
}
});
}

@Override
public void onQueryFailed(Exception e){
cutestCatCallback.onError(e);
}
});
}

private Cat findCutest(List<Cat> cats){
return Collections.max(cats);
}

}




泛型回调

从回调接口中找到共同的模式:

  • 都有一个分发结果的方法(onCutestCatSaved, onCatListReceived, onCatStored)
  • 它们中大多数有一个用于错误处理的方法(onError, onQueryFailed, onStoreFailed)
    所以可以创建一个泛型回调接口去代替原来所有的接口。
    但是不能去改变API的调用方法的签名,必须用创建包装类来间接调用。
    1
    2
    3
    4
    public interface Callback<T>{
    void onResult(T result);
    void onError(Exception e);
    }

ApiWrapper

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
public class ApiWrapper{
Api api;

public void queryCats(String query, Callback<List<Cat>> catsCallback){
api.queryCats(query, new Api.CatsQueryCallback(){
@Override
public void onCatListReceived(List<Cat> cats){
catsCallback.onResult(cats);
}

@Override
public void onQueryFailed(Exception e){
catsCallback.onError(e);
}
}
}

public void store(Cat cat, Callback<Uri> uriCallback){
api.store(cat, new Api.StoreCallback(){
@Override
public void onCatStored(Uri uri){
uriCallback.onResult(uri);
}

@Override
public void onStoreFailed(Exception e){
uriCallback.onError(e);
}
});
}
}

CatsHelper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class CatsHelper{
ApiWrapper apiWrapper;

public void saveTheCutestCat(String query, Callback<Uri> cutestCatCallback){
apiWrapper.queryCats(query, new Callback<List<Cat>> ()){
@Override
public void onResult(List<Cat> cats){
Cat cutest = findCutest(cats);
apiWrapper.store(cutest, cutestCatCallback);
}

@Override
public void onError(Exception e){
cutestCatCallback.onError(e);
}
});
}
}




做的更好

如果在异步操作中返回一些临时对象,需要定义一个出来。
这样的一个对象需要包括常见的行为(以回调为单一参数),定义这样的类给所有的异步操作使用,这个类叫它AsyncJob。

1
2
3
public abstract class AsyncJob<T>{
public abstract void start(Callback<T> callback);
}

更改包装API的调用

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
public class ApiWrapper{
Api api;

public AsyncJob<List<Cat>> queryCats(String query){
return new AsyncJob<List<Cat>>(){
@Override
public void start(Callback<List<Cat>> catsCallback){
api.queryCats(query, new Api.CatsQueryCallback(){
@Override
public void onCatListReceived(List<Cat> cats){
catsCallback.onResult(cats);
}

@Override
public void onQueryFailed(Exception e){
catsCallback.onError(e);
}
});
}
};
}

public AsyncJob<Uri> store(Cat cat){
return new AsyncJob<Uri>(){
@Override
public void start(Callback<Uri> uriCallback){
api.store(cat, new Api.StoreCallback(){
@Override
public void onCatStored(Uri uri){
uriCallback.onResult(uri);
}

@Override
public void onStoreFailed(Exception e){
uriCallback.onError(e);
}
});
}
};
}

}

CatsHelper

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
public class CatsHelper{
ApiWrapper apiWrapper;

public AsyncJob<Uri> saveTheCutestCat(String query){
return new AsyncJob<Uri>(){
@Override
public void start(Callback<Uri> cutestCatCallback){
apiWrapper.queryCats(query)
.start(new Callback<List<Cat>>()){
@Override
public void onResult(List<Cat> cats){
Cat cutest = findCutest(cats);
apiWrapper.store(cutest)
.start(new Callback<Uri>(){
@Override
public void onResult(Uri result){
cutestCatCallback.onResult(result);
}

@Override
public void onError(Exception e){
cutestCatCallback.onError(e);
}
});
}

@Override
public void onError(Exception e){
cutestCatCallback.onError(e);
}
});
}
};
}

private Cat findCutest(List<Cat> cats){
return Collections.max(cats);
}
}

现在可以给客户端返回“组合”操作的AsyncJob

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
50
51
52
53
54
55
56
57
58
59
60
public class CatsHelper{
ApiWrapper apiWrapper;

public AsyncJob<Uri> saveTheCutestCat(String query){
AsyncJob<List<Cat>> catsListAsyncJob = apiWrapper.queryCats(query);
AsyncJob<Cat> catsListAsyncJob = new AsyncJob<Cat>(){
@Override
public void start(Callback<Cat> callback){
catsListAsyncJob.start(new Callback<List<Cat>>()){
@Override
public void onResult(List<Cat> result){
callback.onResult(findCutest(result));
}

@Override
public void onError(Exception e){
callback.onError(e);
}
});


}

};

AsyncJob<Uri> storedUriAsyncJob = new AsyncJob<Uri>(){
@Override
public void start(Callback<Uri> cutestCatCallback){
cutestCatAsyncJob.start(new Callback<Cat>(){
@Override
public void onResult(Cat cutest){
apiWrapper.store(cutest)
.start(new Callback<Uri>(){
@Override
public void onResult(Uri result){
cutestCatCallback.onResult(result);
}

@Override
public void onError(Exception e){
cutestCatCallback.onError(e);
}
});
}

@Override
public void onError(Exception e){
cutestCatCallback.onError(e);
}
});
}
};
return storedUriAsyncJob;
}

private Cat findCutest(List<Cat> cats){
return Collections.max(cats);
}

}

AsyncJob cutestCatAsyncJob

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
AsyncJob<Cat> cutestCatAsyncJob = new AsyncJob<Cat>(){
@Override
public void start(Callback<Cat> callback){
catsListAsyncJob.start(new Callback<List<Cat>>(){
@Override
public void onResult(List<Cat> result){
callback.onResult(findCutest(result));
}

@Override
public void onError(Exception e){
callback.onError(e);
}
});
}
};

这16行代码只有一行有用(对于逻辑来说)

1
findCutest(result);

剩下的仅仅是开启另外一个AsyncJob和传递结果与错误的样板代码。

该怎么写?必须做下面两件事情:

  • AsyncJob是我们转换的结果
  • 转换方法
    1
    2
    3
    public interface Func<T, R>{
    R call(T t);
    }

相当简单,Func接口有两个类型成员,T对应于参数类型而R对应于返回类型。
当从一个AsyncJob中装换处结果后就需要做一些值之间的映射,这样的方法叫map。
定义这个方法实例(Func类型)最好的地方就在AsyncJob类中,所以AsyncJob代码里看起来就是这样了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public abstract class AsyncJob<T>{
public abstract void start(Callback<T> callback);

public <R> AsyncJob<R> map(Func<T, R> func){
final AsyncJob<T> source = this;
return new AsyncJob<R>(){
@Override
public void start(Callback<R> callback){
source.start(new Callback<T>(){
@Override
public void onResult(T result){
R mapped = func.call(result);
callback.onResult(mapped);
}

@Override
public void onError(Exception e){
callback.onError(e);
}
});
}
};
}
}

这时CatsHelper就是下面这样了:

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
public class CatsHelper{
ApiWrapper apiWrapper;

public AsyncJob<Uri> saveTheCutestCat(String query){
AsyncJob<List<Cat>> catsListAsyncJob = apiWrapper.queryCats(query);
AsyncJob<Cat> cutestCatAsyncJob = catsListAsyncJob.map(new Func<List<Cat>, Cat>(){
@Override
public Cat call(List<Cat> cats){
return findCutest(cats);
}
});

AsyncJob<Uri> storedUriAsyncJob = cutestCatAsyncJob.flatMap(new Func<Cat, AsyncJob<Uri>(){
@Override
public void start(Callback<Uri> cutestCatCallback){
cutestCatAsyncJob.start(new Callback<Cat>(){
@Override
public void onResult(Cat cutest){
apiWrapper.store(cutest)
.start(new Callback<Uri>(){
@Override
public void onResult(Uri result){
cutestCatCallback.onResult(result);
}

@Override
public void onError(Exception e){
cutestCatCallback.onError(e);
}
});
}

@Override
public void onError(Exception e){
cutestCatCallback.onError(e);
}
});
}
};

return storedUriAsyncJob;
}
}

private Cat findCutest(List<Cat> cats){
return Collections.max(cats);
}
}




高级映射

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
public class CatsHelper {

ApiWrapper apiWrapper;

public AsyncJob<Uri> saveTheCutestCat(String query) {
AsyncJob<List<Cat>> catsListAsyncJob = apiWrapper.queryCats(query);
AsyncJob<Cat> cutestCatAsyncJob = catsListAsyncJob.map(new Func<List<Cat>, Cat>() {
@Override
public Cat call(List<Cat> cats) {
return findCutest(cats);
}
});

AsyncJob<Uri> storedUriAsyncJob = cutestCatAsyncJob.map(new Func<Cat, Uri>() {
@Override
public Uri call(Cat cat) {
return apiWrapper.store(cat);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ will not compile
// Incompatible types:
// Required: Uri
// Found: AsyncJob<Uri>
}
});
return storedUriAsyncJob;
}

private Cat findCutest(List<Cat> cats) {
return Collections.max(cats);
}
}
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
public class CatsHelper {

ApiWrapper apiWrapper;

public AsyncJob<Uri> saveTheCutestCat(String query) {
AsyncJob<List<Cat>> catsListAsyncJob = apiWrapper.queryCats(query);
AsyncJob<Cat> cutestCatAsyncJob = catsListAsyncJob.map(new Func<List<Cat>, Cat>() {
@Override
public Cat call(List<Cat> cats) {
return findCutest(cats);
}
});

AsyncJob<AsyncJob<Uri>> storedUriAsyncJob = cutestCatAsyncJob.map(new Func<Cat, AsyncJob<Uri>>() {
@Override
public AsyncJob<Uri> call(Cat cat) {
return apiWrapper.store(cat);
}
});
return storedUriAsyncJob;
//^^^^^^^^^^^^^^^^^^^^^^^ will not compile
// Incompatible types:
// Required: AsyncJob<Uri>
// Found: AsyncJob<AsyncJob<Uri>>
}

private Cat findCutest(List<Cat> cats) {
return Collections.max(cats);
}
}

在目前这点上我们只能有AsyncJob>。我们需要往更深处挖吗?我们希望的是,去把AsyncJob在一个级别上的两个异步操作扁平化成一个单一的异步操作。

现在我们需要的是得到能使方法返回映射成R类型也是AsyncJob类型的操作。这个操作应该像map,但在最后应该flatten我们嵌套的AsyncJob。我们叫它为flatMap

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
50
51
52
53
54
55
56
57
58
public abstract class AsyncJob<T>{
public abstract void start(Callback<T> callback);

public <R> AsyncJob<R> map(Func<T,R> func){
final AysncJob<T> source = this;
return new AsyncJob<R>(){
@Override
public void start(Callback<R> callback){
@Override
public void start(Callback<R> callback){
source.start(new Callback<T>(){
@Override
public void onResult(T result){
R mapped = func.call(result);
callback.onResult(mapped);
}

@Override
public void onError(Exception e){
callback.onError(e);
}
});
}
}
};
}

public <R> AsyncJob<R> flatMap(Func<T,AsyncJob<R>> func){
final AsyncJob<T> source = this;
return new AsyncJob<R>(){
@Override
public void start(Callback<R> callback){
source.start(new Callback<T>(){
@Override
public void onResult(T result){
AsyncJob<R> mapped = func.call(result);
mapped.start(new Callback<R>(){
@Override
public void onResult(R result){
callback.onResult(result);
}

@Override
public void onError(Exception e){
callback.onError(e);
}
});
}

@Override
public void onError(Exception e){
callback.onError(e);
}
});
}
};
}
}

修复CatsHelper

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
public class CatsHelper{
ApiWrapper apiWrapper;

public AsyncJob<Uri> saveTheCutestCat(String query){
AsyncJob<List<Cat>> catsListAsyncJob = apiWrapper.queryCats(query);
AsyncJob<Cat> cutestCatAsyncJob = catsListAsyncJob.map(new Func<List<Cat>, Cat>(){
@Override
public Cat call(List<Cat> cats){
return findCutest(cats);
}
});

AsyncJob<Uri> storeUriAsyncJob = cutestCatAsyncJob.flatMap(new Func<Cat, AsyncJob<Uri>>(){
@Override
public AsyncJob<Uri> call(Cat cat){
return apiWrapper.store(cat);
}
});

return storedUriAsyncJob;
}

private Cat findCutest(List<Cat> cats){
return Collections.max(cats);
}
}




最后的要点

如果使用Java 8的lambda表达式,代码会更加简洁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class CatsHelper{
ApiWrapper apiWrapper;

public AsyncJob<Uri> saveTheCutestCat(String query){
AsyncJob<List<Cat>> catsListAsyncJob = apiWrapper.queryCats(query);
AsyncJob<Cat> cutestCatAsyncJob = catsListAsyncJob.map(cats->findCutest(cats));
AsyncJob<Uri> storedUriAsyncJob = cutestCatAsyncJob.flatMap(cat->apiWrapper.store(cat));
return storedUriAsyncJob;
}

private Cat findCutest(List<Cat> cats){
return Collections.max(cats);
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class CatsHelper {

Api api;

public Uri saveTheCutestCat(String query){
List<Cat> cats = api.queryCats(query);
Cat cutest = findCutest(cats);
Uri savedUri = api.store(cutest);
return savedUri;
}

private Cat findCutest(List<Cat> cats) {
return Collections.max(cats);
}
}

源码分析OKHttp

OkHttp3特点

OkHttp是一个高效的Http客户端,有如下的特点:

  • 支持HTTP2/SPDY
  • socket自动选择最好路线,并支持自动重连
  • 拥有自动维护的socket连接池,减少握手次数
  • 拥有队列线程池,轻松写并发
  • 拥有Interceptors轻松处理请求与响应(比如透明GZIP压缩,LOGGING)
  • 基于Headers的缓存策略

主要对象

  • Connections:对JDK中socket进行了引用计数封装,用来控制socket连接
  • Streams:维护HTTP的流,用来对Request/Response进行IO操作
  • Calls:HTTP请求任务封装
  • StreamAllocation:用来控制Connections/Streams的资源分配与释放

工作流程的概述

当我们OkHttpClient.newCall(request)进行execute/enenqueue时,实际是将请求Call放到了Dispatcher中,
OkHttp使用Dispatcher进行线程分发,它有两种方法,
一个是普通的同步单线程;
另一个是使用了队列进行并发任务的分发(Dispatch)与回调。

1.Dispatcher的结构

Dispatcher维护了如下变量,用于控制并发的请求:

  • maxRequests = 64:最大并发请求数为64
  • maxRequestsPerHost = 5:每个主机最大请求数为5
  • Dispatcher:分发者,也就是生产者(默认在主线程)
  • AsyncCall:队列中需要处理的Runnable(包装了异步回调接口)
  • ExecutorService: 消费者池(也就是线程池)
  • Deque: 缓存(用数组实现,可自动扩容,无大小限制)
  • Deque: 正在运行的任务,仅仅是用来引用正在运行的任务以判断并发量,注意它并不是消费者缓存

根据生产者消费者模型的理论,当入队(enqueue)请求时,如果满足(runningRequests < 64 && runningRequestsPerHost < 5),
那么就直接把AsyncCall直接加到runningCalls的队列中,并在线程池中执行。
如果消费者缓存满了,就放入readyAsyncCalls进行缓存等待。

1
2
3
4
5
6
7
8
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}

当任务执行完成后,调用finished的promoteCalls()函数,手动移动缓存区(可以看出这里是主动清理的,因此不会发生死锁)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private void promoteCalls() {
if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.

for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall call = i.next();

if (runningCallsForHost(call) < maxRequestsPerHost) {
i.remove();
runningAsyncCalls.add(call);
executorService().execute(call);
}

if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
}
}

Mark-Down

线程池基础

线程池好处都有啥

线程池的关键在于线程复用以减少非核心任务的损耗。
线程池技术正是关注如何缩短或调整T1,T3时间的技术,从而提高服务器程序性能的。
通过对线程进行缓存,减少了创建销毁的时间损失。
通过控制线程数量的阈值,减少了当线程过少时带来的CPU闲置与线程过多时对JVM的内存与线程切换时系统调用的压力。

OkHttp配置的线程池

在OkHttp,使用如下构造了单例线程例:

1
2
3
4
5
6
public synchronized ExecutorService executorService(){
if(executorService == null){
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60 , TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), Util.thread("OkHttp Dispatcher", false));
}
return executorService;
}}

参数说明如下:

  • int corePoolSize :最小并发线程数,这里并发同时包括空闲与活动的线程,如果是0的话,空闲一段时间后所有线程将全部被销毁。
  • int maximumPoolSize:最大线程数,当任务进来时可以扩充的线程最大数,当大于了这个值就会根据丢弃处理机制来处理。
    long keepAliveTime:当线程数大于corePoolSize时,多余的空闲线程的最大存活时间,类似于HTTP中的keep-alive
    TimeUnit unit:时间单位,一般用秒
    BlockingQueue workQueue:工作队列
    ThreadFactory threadFactory:单个线程的工厂,可以打log,设置Dameo(即当JVM退出时,线程自动结束)等。

可以看出,在OkHttp中,构建了一个阈值为[0, Integer.MAX_VALUE]的线程池,它不保留任何最小线程数,随时创建更多的线程数,当线程空闲时只能存活60秒,它使用了一个不存储元素的阻塞工作队列,一个叫做“OkHttp Disptcher”的线程工厂。
也就是说,在实际运行中,当收到10个并发请求时,线程池会创建10个线程,当工作完成后,线程池会在60s后相继关闭所有线程。

反向代理模型

在OkHttp中,使用了与Nginx类似的反向代理与分发技术,这是典型的单生产多消费者问题。
我们知道在Nginx站,用户通过HTTP(SOCKET)访问前置的服务器,服务器会添加Header并自动转发给请求给后端群,接着返回数据结果给用户。
通过将工作分配给多个后台服务器,可以提高服务的负载均衡能力,实现非阻塞、高并发连接,避免资源全部放到一台服务器而带来的负载、速度、在线率等影响。
而在OkHttp中,非常类似于上述场景,它使用Dispatcher作为任务的转发器,线程池对应多台后置服务器,用AsyncCall对应Socket请求,用Deque对应Nginx的内部缓存。

OkHttp的任务调度

当我们希望使用OkHttp的异步请求时,一般进行如下构造

1
2
3
4
5
6
7
8
9
10
11
12
OkHttpClient client = new OkHttpClient.Builder().build();
Request request = new Request.Builder()
.url("http://qq.com").get().build();
Client.newCall(request).enqueue(new Callback(){
@Override
public void onFailure(Call call, IOException e){
}

@Override
public void onResponse(Call call,Response response) throws IOException{
}
});

当HttpClient的请求入队是,根据代码,我们可以发现实际上是Dispatcher进行了入队操作

1
2
3
4
5
6
7
8
9
10
11
synchronized void enqueue(AsyncCall call){
if(runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxReqeustsPerHost){
//添加正在运行的请求
runningAsyncCalls.add(call);
//线程池执行请求
executorService().execute(call);
}else{
//添加到缓存队列排队等待
readyAsyncCalls.add(call);
}
}

如果满足条件,那么就直接把AsyncCall直接加到runningCalls的队列中,并在线程池中执行(线程池会根据当前负载自动创建、销毁、缓存相应的线程)。反之就放入readyAsyncCalls进行缓存等待。
我们再分析请求元素AsyncCall(它实现了Runnable接口),它内部实现的execute方法如下:

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
@Override 
protected void execute(){
boolean signalledCallback = false;
try{
//执行耗时IO任务
Response response = getResponseWithInterceptorChain(forWebSocket);
if(canceled){
signalledCallback = true;
//回调,注意这里回调在线程池中,而不是想当然的在主线程回调
resposneCallback.onFailure(RealCall.this, new IOException("Cancelled"));
}else{
signalledCallback = true;
//回到,同上
responseCallback.onResponse(RealCall.this, response);
}
}catch(IOException e){
if(signalledCallback){
logger.log(Level.INFO, "Callback failure for " + toLoggableString(),e):
}else{
responseCallback.onFailure(RealCall.this, e);
}
}finally{
//最关键的代码
client.dispatcher().finished(this);
}
}

当任务执行完成后,无论是否有异常,finally代码段总会执行,也就调用Dispatcher的finished函数,打开源码,发现它将正在运行的任务Call从队列runningAsyncCalls中移除后,接着执行promoteCalls()函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private void promoteCalls(){
//如果目前是最大负荷运转,接着等
if(runningAsyncCalls.size() >= maxRequests) return;
//如果缓存等待区是空的,接着等
if(readyAsyncCall.isEmpty()) return;
for(Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext();){
AsyncCall call = i.next();
if(runningCallsForHost(call) < maxRequestsPerHost){
//将缓存等待区最后一个移动到运行区中,并执行
i.remove();
runningAsyncCalls.add(call);
executorService().execute(call);
}
if(runningAsyncCalls.size() >= maxRequests) return;
}
}

这样,就主动的把缓存队列向前走了一步,而没有使用互斥锁等复杂编码。

总结

  • OkHttp采用了Dispatcher技术,类似于Nginx,与线程池配合实现了搞并发,低阻塞的运行
  • OkHttp采用Deque作为缓存,按照入队的顺序先进先出
  • OkHttp最出彩的地方就是try/finally中调用了finished函数,可以主动控制等待队列的移动,而不是采用锁,极大减少了编码复杂性。





    Socket管理(StreamAllocation)

    1.选择路线与自动重连(RouteSelector)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public Route next() throws IOException{
    if(!hasNextInetSocketAddress()){
    if(!hasNextProxy()){
    if(!hasNextPostponed()){
    throw new NoSuchElementException();
    }
    return nextPostponed();
    }
    lastProxy = nextProxy();
    }
    lastInetSocketAddress = nextInetSocketAddress();

    Route route = new Route(address, lastProxy, lastInetSocketAddress);
    if(routeDatabase.shouldPostpone(route)){
    postponsedRoutes.add(route);
    return next();
    }

    return route;
    }

如果Proxy为null:

  1. 在构造函数中设置代理为Proxy.NO_PROXY
  2. 如果缓存中的lastInetSocketAddress为空,就通过DNS(默认是Dns.SYSTEM,包装了jdk自带的lookup函数)查询,并保存结果,注意结果是数组,即一个域名有多个IP,这就是自动重连的来源。
  3. 如果还没有查询到就递归调用next查询,直到查询到为止
  4. 一切next都没有枚举到,抛出NoSuchElementException,退出


    如果Proxy为HTTP:
  5. 设置socket的ip为代理地址的ip
  6. 设置socket的端口为代理地址的端口
  7. 一切next都没有枚举到,抛出NoSuchElementException,退出

2.连接socket链路(RealConnection)

当地址,端口准备好了,就可以进TCP连接了(也就是三次握手),步骤如下:

  1. 如果连接池中已经存在连接,就从中取出(get)RealConnection,如果没有命中就进入下一步
  2. 根据选择的路线(Route),调用Platform.get().connectSokcet选择当前平台Runtime下最好的socket库进行握手
  3. 强建立成功的RealConnection放入(put)连接池缓存
  4. 如果存在TLS,就根据SSL版本与证书进行安全握手
  5. 构造HttpStream并维护刚刚的socket连接,管理建立完成。

3.释放socket链路(release)

如果不再需要(比如通信完成,连接失败)此链路,释放连接(也就是TCP断开)

  1. 尝试从缓存的连接池中删除(remove)
  2. 如果没有命中缓存,就直接调用jdk的socket关闭

HTTP请求序列化/反序列化

1.获得HTTP流(httpStream)

以下为无缓存,无多次302跳转,网络良好,HTTP/1.1下的GET访问实例分析。

1
httpStream = connect();

在connect()有非常重要的一步,它通过okio库与远程socket建立了I/O连接,为了更好的理解,我们可以把它看成管道:

1
2
3
4
//source 用于获取response
source = Okio.buffer(Okio.source(rawSocket));
//sink用于write buffer到server
sink = Okio.buffer(Okio.sink(rawSocket));

OkHttp的I/O使用的是Okio库:

  • Buffer:Buffer是可变字节,类似于byte[],相当于传输介质
  • source: source是Okio库中的输入组件,类似于inputStream,经常在下载中用到。它的重要方法是read(Buffer sink, long byteCount),从流中读取数据。
  • sink:sink是okio库中的io输入组件,类似于outputStream,经常用于写到file/socket,它的最重要方法是void write(Buffer source, long byteCount),写数据到Buffer中
    如果把连接看成管道,->为管道的方向,如下
    1
    2
    Sink -> Socket/File
    Source <- Socket/File

2.拼装Raw请求与Headers(writeRequestHeaders)

我们通过Rquest.Builder构建了简陋的请求后,可能需要进行一些修饰,这时需要使用interceptors对Request进行进一步的拼装了。
拦截器是OkHttp中强大的流程装置,它可以用来监控log,修改请求,修改结果,甚至是对用户透明的GZIP压缩。
在OkHttp中,内部维护了一个Interceptors的List,通过InterceptorChain进行多次拦截修改操作。
Mark-Down

请求代码如下,是自增递归(recursive)调用Chain.process(),直到interceptors().size()中的拦截器全部调用完。

  1. 递归调用interceptors,依次入栈对response进行处理
  2. 当全部递归出栈完成后,移交给网络模块(getResponse)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    if(index < client.interceptors().size()){
    Interceptor.Chain chain = new ApplicationInterceptorChain(index + 1, request,forWebSocket);
    Interceptor interceptor = client.interceptors().get(index);
    //递归调用Chain。process()
    Response interceptedResponse = interceptor.intercept(chain);
    if(interceptedResponse == null){
    throw new NullPointerException("application interceptor" + interceptor + " returned null");
    }
    return interceptedResponse;
    }
    return getResponse(request, forWebSocket);

接下来是正式的网络请求getResponse(),此步骤通过http协议规范将对象中的数据信息序列化为Raw文本:

  1. 在OkHttp中,通过RequestLine,Request,HttpEngine,Header等参数进行序列化操作,也就是拼装参数为socketRaw数据。拼装方法也比较暴力,直接按照RFC协议要求的格式进行concat输出就实现了
    2.通过sink写入write到socket连接

1.3 获得响应(readResponseHeaders/Bod)

此步骤根据获取到的Socket纯文本,解析为Response对象,我们可以看成是一个反序列(通过http协议将Raw文本转成对象)的过程:
拦截器的设计:

  1. 自定义网络拦截器请求进行递归入栈
  2. 在自定义网络拦截器的intercept中,调用NetworkInterceptorChain的proceed(request),进行真正的网络请求(readNetWorkResponse)

  3. 接自定义请求递归出栈
    网络读取(readNetworkResponse)分析:
    1.读取Raw的第一行,并反序列化为StatusLine对象

  4. 以Transfer-Encoding:chuncked的模式传输并组装Body
    伪代码如下:
    1
    2
    3
    4
    5
    6
    (RawData <- RemoteChannel(www.xx.com, 80) //读取远程的Raw
    map(func NetworkInterceptorChains())//云处理
    //这里的source应用了HttpEngine,并重写了read方法
    .map(func getTransferStream{})
    // 根据source拼装body对象
    .map(func RealResponseBody());

接下来进行释放socket连接。
现在我们就获得response对象,进行进一步Gson操作。

在线预览ppt doc

打造自己在在线文档浏览

Apache POI

对在线文档浏览的误会,刚开始我选择了POI项目
Apache Poi下载地址:http://poi.apache.org/download.html
我使用的版本是稳定版poi-bin-3.14.zip

ApachePoi组件

  • POIFS(较差混淆技术实现文件系统):此组件是所有其他POI元件的基本因素。它被用来明确地读取不同的文件。
  • HSSF(可怕的电子表格格式):它被用来读取和写入MS-Excel文件的xls格式。
  • XSSF(XML格式):它是用于MS-Excel中XLSX文件格式。
  • HPSF (可怕的属性设置格式):它用来提取MS-Office文件属性设置。
  • HWPF(可怕的字处理格式) :它是用来读取和写入MS-Word的文档扩展名的文件。
  • XWPF(XML字处理格式):它是用来读取和写入MS-Word的docx扩展名的文件。
  • HSLF(可怕的幻灯片版式格式):它是用于读取、创建和编辑PowerPoint演示文稿。
  • HDGF(可怕的图表格式):它包含类和方法为MS-Visio的二进制文件。
  • HPBF(可怕的出版商格式):它被用来读取和写人MS-Publisher文件。

Open Office

做浏览,其实是需要另一个OpenOffice的项目。
步骤:

  • 先将doc、ppt、xls转换成pdf
  • 再用js调用pdf在线查看
  • 主要借助的工具openoffice、jodconvert、viewjs

安装openoffice

http://www.openoffice.org/zh-cn/download/

JODConvert简介

JODConvert,是一个Java的OpenDocument文件转换器,可以进行许多文件格式的转换。
它依赖于OpenOffice提供的服务来进行转换,它能将MS Office文档转换为PDF格式。


可以将JODConverter内嵌在Java应用程序里,也可以单独作为命令行由脚本调用,更可以应用为网页程序或者WebService以供网络应用。

1
2
3
4
5
6
7
8
OfficeManager officeManager = new DefaultOfficeManagerConfiguration( ).buildOfficeManager( );
//启动OpenOffice服务
officeManager.start( );
//执行转换
OfficeDocumentConverter converter = new OfficeDocumentConverter(officeManager);
converter.convert(new File(“test.odt”), new File(“test.pdf”));
//停止服务
officeManager.stop( );

OfficeManager是一个接口,主要定义了三个方法:

  • public void start() 启动OpenOffice服务
  • public void stop() 停止OpenOffice服务
  • public void execute(OfficeTask task) 执行转换任务
    DefaultOfficeManagerConfiguration是一个实现了OfficeManager接口的实体类,其提供了相关方法配置OpenOffice.org,比如:
    1
    2
    3
    public DefaultOfficeManagerConfiguration setOfficeHome(String officeHome) //设置OpenOffice.org安装目录
    public DefaultOfficeManagerConfiguration setConnectionProtocol(OfficeConnectionProtocol conn) //设置连接协议,确定使用管道通信,还是socket通信
    public DefaultOfficeManagerConfiguration setTemplateProfileDir(File templateProfileDir)//设置临时目录

可以查看JODConvert API手册查看其它配置。
配置完之后,必须要执行方法buildOfficeManager(),实现真正的配置。


OfficeDocumentConverter中主要包含convert方法,该方法实际上调用的是实现OfficeManager接口的类中的execute方法。

算法设计

Mark-Down

代码实现

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package com.converter.pdfConverter;

import java.io.File;
import java.io.FileNotFoundException;

import org.artofsolving.jodconverter.OfficeDocumentConverter;
import org.artofsolving.jodconverter.office.DefaultOfficeManagerConfiguration;
import org.artofsolving.jodconverter.office.OfficeManager;

import com.converter.utils.FileUtils;

public class OpenOfficePDFConverter implements PDFConverter{

private static OfficeManager officeManager;
private static String OFFICE_HOME = "D:\\Program Files\\OpenOffice.org 3";
private static int port[] = {8100};



public void convert2PDF(String inputFile, String pdfFile) {

if(inputFile.endsWith(".txt")){
String odtFile = FileUtils.getFilePrefix(inputFile)+".odt";
if(new File(odtFile).exists()){
System.out.println("odt文件已存在!");
inputFile = odtFile;
}else{
try {
FileUtils.copyFile(inputFile,odtFile);
inputFile = odtFile;
} catch (FileNotFoundException e) {
System.out.println("文档不存在!");
e.printStackTrace();
}
}
}

startService();
System.out.println("进行文档转换转换:" + inputFile + " --> " + pdfFile);
OfficeDocumentConverter converter = new OfficeDocumentConverter(officeManager);
converter.convert(new File(inputFile),new File(pdfFile));
stopService();
System.out.println();
}


public void convert2PDF(String inputFile) {
String pdfFile = FileUtils.getFilePrefix(inputFile)+".pdf";
convert2PDF(inputFile,pdfFile);

}

public static void startService(){
DefaultOfficeManagerConfiguration configuration = new DefaultOfficeManagerConfiguration();
try {
System.out.println("准备启动服务....");
configuration.setOfficeHome(OFFICE_HOME);//设置OpenOffice.org安装目录
configuration.setPortNumbers(port); //设置转换端口,默认为8100
configuration.setTaskExecutionTimeout(1000 * 60 * 5L);//设置任务执行超时为5分钟
configuration.setTaskQueueTimeout(1000 * 60 * 60 * 24L);//设置任务队列超时为24小时

officeManager = configuration.buildOfficeManager();
officeManager.start(); //启动服务
System.out.println("office转换服务启动成功!");
} catch (Exception ce) {
System.out.println("office转换服务启动失败!详细信息:" + ce);
}
}

public static void stopService(){
System.out.println("关闭office转换服务....");
if (officeManager != null) {
officeManager.stop();
}
System.out.println("关闭office转换成功!");
}
}

渲染

下载viewerjs
http://viewerjs.org/releases/viewerjs-0.5.7.zip
解压,将ViewerJS下的所有内容放置项目webapp下,
在同级目录下放置一个pdf(test.pdf),
如项目名是xxx
在线预览该pdf为http://localhost:8080/xxx/test.pdf

Java注解

揭开Java注解的面纱

什么是注解?

从JDK1.5被引入到Java,它提供了一种机制,这种机制允许在编写代码的同时可以直接编写元数据。

介绍

注解就是代码的元数据,它们包含了代码自身的信息。
注解可以被用在包、类、方法、变量、参数上。
自Java8开始,有一种注解几乎可以被放在代码的任何位置,叫做类型注解。
被注解的代码并不会直接被注解影响。这只会向第三系统提供关于自己的信息以用于不同的需求。
注解会被编译至class文件中,而且在运行时被处理程序提取出来用于业务逻辑。
当然,创建在运行时不可用的注解也是可能的,甚至可以创建只在源文件中可用,在编译时不可用的注解。

消费器

理解注解的目的以及如何使用它都会带来困难,因为注解本身并不包含任何功能逻辑,它们也不会影响自己注解的代码,那么,它们到底为什么而存在呢?
注解消费器,它们是利用被注解代码并根据注解信息产生不同行为的系统或者应用程序。
例如,在Java自带的内建注解(元注解)中,消费器是执行被注解代码的JVM。
还有其他,如JUnit,消费器是读取,分析被注解代码的Junit处理程序,它还可以决定测试单元和方法执行顺序。
消费器使用Java中的反射机制来读取和分析被注解的源代码。

注解语法和元素

声明一个注解需要使用”@”作为前缀,这便向编译器说明,该元素为注解。

1
2
3
4
@Annotation
public void annotatedMethod(){
...
}

上述的注解名称为Annotation,它正在注解annotatedMethod方法。
编译器会处理它。注解可以以键值对的形式持有有很多元素,即注解的属性:

1
2
3
4
5
6
7
@Annoation(
info = "I am an annotation",
counter = "55"
)
public void annotatedMethod(){
...
}

如果注解只包含一个元素(或者只需要指定一个元素的值,其它使用默认),可以像这样声明:

1
2
3
4
@Annotation("I am annotation")
public void annotatedMethod(){
...
}

如果没有元素需要被指定,则不需要括号。多个注解可以使用在同一代码上,例如类:

1
2
3
@Annoation(info = "U a u O")
@Annoation2
class AnnotatedClass{...}

在什么地方使用

注解基本上可以在Java程序的每一个元素上使用:类、域、方法、包、变量,等待。
自Java8,诞生了通过类型注解的理念。
在此之前,注解是限于在前面讨论的元素的声明上使用。
从此,无论是类型哈市声明都可以使用注解,就像:

1
@MyAnnotation String str = "danibuiza";

使用案例

注解可以满足许多要求,最普遍的是:

  • 向编译器提供信息:注解可以被编译器用来根据不同的规则产生警告,甚至错误。一个例子是Java8中@FunctionalInterface注解,这个注解使得编译器校验被注解的类,检查它是否是一个正确的函数式接口。
  • 文档:注解可以被软件应用程序计算代码的质量例如:FindBugs、PMD或者自动生成报告,例如:用来Jenkins、Jira、Teamcity。
  • 代码生成:注解可以使用代码中展示的元数据信息来自动生成代码或者xml文件,一个不错的例子是JAXB。
  • 运行时处理:在运行时检查的注解可以用做不同的目的,像单元测试(Junit),依赖注入(Spring),校验,日志(Log4j),数据访问(Hibernate)等等。

内建注解

Java语言自带了一系列的注解。
这个清单只涉及了Java语言最核心的包,未包含标准JRE中所有包和库如JAXB或Servlet规范。
以下讨论到的注解中有一些被称之为Meta注解,它们的目的注解其他注解,并且包含关于其他注解的信息。
@Retention:这个注解在其他注解上,并用来说明如何存储已被标记的注解。这是一种元注解,用来标记注解并提供注解的信息。可能的值是:

  • SOURCE:表明这个注解会被编译器忽略,并只会保留在源代码中。
  • CLASS:表明这个注解会通过编译驻留在CLASS文件,但会被JVM在运行时忽略,正因为如此,其在运行时不可见。
  • RUNTIME:表示这个注解会被JVM获取,并在运行时通过反射获取。

@Target:这个注解用于限制某个元素可以被注解的类型。例如:

  • ANNOTATION_TYPE 表示该注解可以应用到其他注解上
  • CONSTRUCTOR 表示可以使用到构造器上
  • FIELD 表示可以使用到域或属性上
  • LOCAL_VARIABLE表示可以使用到局部变量上
  • METHOD可以使用到方法级别的注解上
  • PACKAGE可以使用到包声明上
  • PARAMETER可以使用到方法的参数上
  • TYPE可以使用到一个类的任何元素上

@Documented:被注解的元素将会作为Javadoc产生的文档中的内容。
注解都默认不会成为文档中的内容。
这个注解可以对其它注解使用。
@Inherited:在默认情况下,注解不会被子类继承。
被此注解会被所有子类继承。这个注解可以对类使用
@Deprecated:说明被标记的元素不应该再度使用。
这个注解会让编译器产生警告消息。
可以使用到方法,类和域上。
相应的解释和原因,包括另一个可取代的方法应该同时和这个注解使用。
@SuppressWarnings:说明编译器不会针对指定的一个或多个原因产生警告。
例如:如果我们不想因为存在尚未使用的私有方法而得到警告可以这样做。

1
2
3
4
@SuppressWarnings("unused")
private String myNotUsedMethod(){
...
}

通常,编译器会因为没调用该方法而产生警告;用了注解印制了这种行为。该注解需要一个或多个参数来指定抑制的警告类型。
@Override:向编译器说明被注解元素是重写的父类的一个元素。在重写父类元素的时候此注解并非强制性的,不过可以在重写错误时帮助编译器产生错误以提醒我们。
@SafeVarargs:断言方法或者构造器的代码不会对参数进行不安全的操作。在Java的后续版本中,使用这个注解时将会令编译器产生一个错误在编译期间防止潜在的不安全操作。

Java 8与注解

Java8带来了一些优势,同样注解框架的能力也得到了提升。
@Repeatable注解,关于类型注解的声明,函数式接口注解@FunctionInterface(与Lambda结合使用)。
*@Repeatable:说明该注解标识的注解可以多次使用到同一个元素的声明上。
看一个使用的例子。首先我们创造一个能容纳重复的注解的容器:

1
2
3
4
5
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
public @interface RepeatedValues{
CanBeRepeated[] value();
}

接着,创建注解本身,然后标记@Repeatable

1
2
3
4
5
6
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
@Repeatable(RepeatedValues.class)
public @interface CanBeRepeated{
String value();
}

最后,我们可以这样重复地使用:

1
2
3
4
5
6
7
@CanBeRepeated ("the color is green")
@CanBeRepeated ("the color is red")
@CanBeRepeated ("the color is blue")
public class RepeatableAnnotated
{


}

如果我们尝试去掉@Repeatable

1
2
3
4
5
6
7
8
9
10
11
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
public @interface CannotBeRepeated
{
String value();
}
@CanBeRepeated("info")
public class RepeatableAnnotatedWrong
{


}

自Java8开始,我们可以在类型上使用注解。
由于我们在任何地方都可以使用类型,包括new操作符,casting,implements,throw等等。
注解可以改善对Java代码的分析并且保证更加健壮的类型检查。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@SuppressWarnings("unused")
pubilc static void main(String[] args){
//type def
@TypeAnnotated
String cannotBeEmpty = null;

//type
List<@TypeAnnotated String> myList = new ArrayList<String>();

//values
String myString = new @TypeAnnotated String("this is annotated in java 8");
}

public void methodAnnotated(@TypeAnnotated int parameter){
System.out.println("do nothing");
}

@FunctionalInterfce:这个注解表示一个函数式接口元素。
函数式接口是一种只有一个抽象方法(非默认)的接口。
编译器会检查被注解元素,如果不符,就会产生错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@SuppressWarnings("unused")
MyCustomInterface myFuncInterface = new MyCustomInterface()
{
@Override
public int doSomething(int param){
return param * 10;
}
};

@SuppressWarnings("unused")
MyCustomInterface myFuncInterfceLambdas = (x) -> (x * 10);
}

@FunctionalInterface
interface MyCustomInterface
{

int doSomething(int param);
}

这个注解可以被使用到类、接口、枚举和注解本身。
它的被JVM保留并在runtime可见,这个是它的声明:

1
2
3
4
@Documented
@Retention(value = RUNTIME)
@Target(value = TYPE)
public @interface FunctionalInterface

自定义注解

首先,定义一个注解:

1
public @interface CustomAnnotationClass

这样创建了一个新的注解类型名为CustomAnnotationClass。
关键字@interface说明这是一个自定义注解的定义。

之后,你需要为此注解定义一对强制性的属性,保留策略和目标。
还有一些其他属性可以定义,不过两个是最基本和重要的。
所以,我们为自定义的注解设置属性:

1
2
3
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface CustomAnnotationClass implements CustomAnnotationMethod

在保留策略中RUNTIME告诉编译器这个注解应该被JVM保留,并且能通过反射在运行时分析。
通过TYPE我们又设置该注解可以被使用到任何类的元素上。
之后,我们定义两个注解的成员:

1
2
3
4
5
6
7
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface CustomAnnotationClass
{
public String author() default "xxxx";
public String date();
}

以上我们仅定义了默认值为’xxxx’的author属性和没有默认值的date属性。
我们应强调所有的方法声明都不能有参数和throw语句。
这个返回值的类型被限制为之前提过的字符串、类、枚举,注解和存储这些类型的数组。
现在我们可以像这样使用刚创建的自定义注解:

1
2
3
4
5
@CustomAnnotationClass(date = "2014-05-05")
public class AnnotatedClass
{

...
}

在另一种类似的用法中我们可以创建一种注解方法的注解,使用Target METHOD:

1
2
3
4
5
6
7
8
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CustomAnnotationMethod
{
public String author() default "xxxx";
public String date();
public String description();
}

这种注解可以使用在方法声明上:

1
2
3
4
5
6
7
8
9
@CustomAnnotationMethod(date = "2016-07-21", descripton = "annotated method")
public String annotatedMethod(){
return "nothing niente";
}
@CustomAnnotationMethod(author = "Jimmy", date = "2016-07-21", description = "annotated method")
public String annotatedMethodFromAFriend()
{

return "nothing niente";
}

有很多其他属性可以用在自定义注解上,但是目标(Target)和保留策略(Retention Policy)是最重要的两个。

获取注解

Java反射API包含了许多方法在运行时从类、方法或者其它元素获取注解。
接口AnnotatedElement包含了大部分重要的方法,如下:

  • getAnnotations(): 返回该元素的所有注解,包括没有显示定义该元素上的注解。
  • isAnnotationPresent(annotation):检查传入的注解是否存在于当前元素。
  • getAnnotation(class):按照传入的参数获取指定类型的注解。返回null说明当前元素不带有此注解。
    class通过java.lang.Class被实现,java.lang.reflect.Method和java.lang.reflect.Field,所以可以基本上被和任何Java元素使用。
    现在写一个程序,从一个类和它的方法中读取所有的存在的注解:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public static void main(String[] args) throws Exception{
    Class<AnnotatedClass> object = AnnotatedClass.class;
    Annotation[] annotations = object.getAnnotations();
    for(Annotation annotation : annotations){
    System.out.println(annotation);
    }
    if(object.isAnnotationPresent(CustomAnnotationClass.class)){
    Annotation annotation = object.getAnnotation(CustomAnnotationClass.class);
    System.out.println(annotation);
    }
    for( Method method : object.getDeclatredMethods()){
    if(method.isAnnotationPresent(CustomAnnnotationMethod.class)){
    Annotation annotation = method.getAnnotation(CustomAnnotionMethod.class);
    System.out.println(annotation);
    }
    }

    }

在这个程序中,可以看到getAnnotations()方法来获取所有某个对象(方法、类)上的所有注解的用法。
展示了怎样使用isAnnotationPresent()方法和getAnnotation()方法检查是否存在特定的注解,和如何获取它。

FFMpeg入门

概述

ffplay文件概览

Mark-Down

libavcodec
  • allcodecs.c 简单的注册类函数
  • avcodec.h 编解码相关结构定义和函数原型声明
  • dsputil.c 限幅数组初始化
  • dsputil.h 限幅数组声明
  • imgconvert.c 颜色空间转换相关函数实现
  • imgconvert_template.h 颜色空间转换相关结构体定义和函数实现
  • msrle.c 视频RLE行程长度压缩算法解码库
  • truespeech.c 语音TrueSpeech算法解码库
  • truespeech_data.h 语音TrueSpeech算法解码常量数组
  • utils_codec.c 一些解码相关的工具类函数的实现
libavformat
  • allformats.c 简单注册类函数
  • avformat.h 文件和媒体格式相关结构体定义和函数原型声明
  • avidec.c AVI文件解析类函数
  • avio.c 无缓冲数据IO相关函数实现
  • avio.h 无缓冲数据IO相关结构体定义和函数实现
  • aviobuf.c 有缓冲数据IO相关函数实现
  • cutils.c 两个简单的字符串操作函数
  • file.c 文件IO相关函数
  • utils_format.c 文件和媒体格式相关的工具类函数的实现
libavutil
  • avutil.h 简单的像素格式宏定义
  • bswap.h 简单的大小端转换函数的实现
  • common.h 公共的宏定义和简单函数的实现
  • mathematics.h 一个简单的数学运算函数(A*B/C)
  • rational.h 用两整数表示分数相关函数

berrno.h 错误码定义
ffplay.c 总控文件

播放器一般原理

七个模块按广度顺序:读文件模块、解复用模块、视频解码模块、音频解码音频、颜色空间转换模块、视频显示模块,音频播放模块。
Mark-Down

播放器有关的filter粗略的分为五类,分别是SourceFilter,DemuxFilter,DecoderFilter,ColorSpaceConverterFilter, RenderFilter。
SourceFilter源过滤器的作用是为下级DemuxFilter以包的形式源源不断的提供数据流。在通常情况下,我们有多种方式可以获得数据流,一种是从本地文件中读取,一种是从网上获取,SourceFilter另外一个作用就是屏蔽读本地文件和获取网络数据的差别,在下一级的DemuxFilter看来,本地文件和网络数据是一样的。
DemuxFilter解复用过滤器的作用是识别文件类型,媒体类型,分离出各媒体原始数据流,打上时钟信息后送给下级DecodeFilter。为识别出不同的文件类型和媒体类型,常规的做法是读取一部分数据,然后遍历解复用过滤器支持的文件格式和媒体数据格式,做匹配来确定是哪种文件类型,哪种媒体类型;有些媒体类型的原始数据外面还有其他的信息,比如时间、包大小、是否完整包等等。DemuxFilter解析数据包后取出原始数据,有些类型的媒体不管是否是完整包都立即送往下级DecodeFilter,有些类型的媒体要送完整数据包,此时可能有一些数据包拼接的动作;当然时钟信息的计算也是DemuxFilter的工作内容,这个时钟用于各媒体之间的同步。在本例中,AVISplitter是DemuxFilter。
DecoderFilter解码过滤器的作用就是解码数据包,并且把同步时钟信息传递下去。对视频媒体而言,通常是解码成YUV数据,然后利用显卡硬件直接支持YUV格式数据Overlay快速显示的特性让显卡极速显示。YUV格式是一个统称,常见的有YV12、YUY2,UYVY等等。有些非常古老的显卡和嵌入式系统不支持YUV数据显示,那就要转换成RGB格式的数据,每一帧的每一个像素点都要转换,分别计算RGB分量,并且因为转换是浮点运算,虽然有定点算法,还是要耗掉相当一部分CPU,总体上效率底下;对音频媒体而言,通常是解码成PCM数据,然后送给声卡直接输出。在本例中,AVI Decompress和ACM Warper是DecoderFilter。
ColorSpaceConverterFilter颜色空间转换过滤器的作用是把视频解码器解码出来的数据转换成当前显示系统支持的颜色格式。通常视频解码器解码出来的是YUV数据,PC系统是直接支持YUV格式的,也支持RGB格式,有些嵌入式系统只支持RGB格式的。在本例中,视频解码器出来的是RGB8格式的数据,ColorSpaceConverterFilter把RGB8转换成RGB32显示。
RenderFilter渲染过滤器的作用就是在适当的时间渲染相应的媒体,对视频媒体就是直接显示图像,对音频就是播放声音。视音频同步的策略方法有好几种,其中最简单的一种就是默认视频和音频基准时间相同,这时音频可以不打时钟信息,通过计算音频的采样频率,量化bit数,声道数等基本参数就知道音频PCM的数据速率,按照这个速率往前播放即可;视频必须要使用同步时钟信息来决定什么时候显示。DirectShow采用一个有序链表,把接收到的数据包放进有序链表中,启动一个定时器,每次定时器时间到就扫描链表,比较时钟信息,或者显示相应的帧,或者什么也不做,每次接收到新的数据帧,首先判断时钟信息,如果是历史数据帧就丢弃,如果是将来显示数据帧就进有序链表,如果当前时间帧就直接显示。如此这样,保持视频和音频在人体感觉误差范围内相对的动态同步。在本例中VideoRendeDirectSoundDevice是RenderFilter,同时也是SinkFilter。
GraphEdit应用程序可以看成是一个支撑平台,支撑框架。它容纳各种Filter,在Filter间的传递一些通讯消息,控制Filter的运行(启动暂停停止),维护Filter运行状态。GraphEdit就像超级大管家一样,既维护管理看得见的Filter,又维护管理看不见的运行支撑环境。

ffplay播放器原理

ffplay架构概述

ffplay主要改动

SDL显示视频

SDL播放音频

AVI文件格式简介

MS RLE压缩算法简介

True Speech压缩算法简介





libavutil剖析

函数式编程

在Java的世界里,第一要素是“类”,而在函数式编程里,第一要素则是“函数”。

何为函数式编程

函数式编程是一种编程范式(programming paradigm),也就是如何编写程序的方法论。
它属于“结构化编程”的一种,主要思想是想把运算过程尽量写成一系列嵌套的函数调用。
举例来说,现在有这样一个数学表达式:

1
(1+2) * 3 - 4

传统的过程式编写,可能这样写:

1
2
3
var a = 1 + 2;
var b = a * 3;
var c = b - 4;

函数式编程要求使用函数,可以把这一过程定义为不同的函数,然后写成下面这样:

1
var result = subtract(multiply(add(1,2), 3), 4);

这就是函数式编程

特点

  1. 函数是第一等公民
    函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值。

    1
    2
    var print = function(i){ console.log(i);};
    [1,2,3].forEach(print);
  2. 只用“表达式”,不用“语句”
    “表达式”(expression)是一个单纯的运算过程,总是有返回值;
    “语句”(statement)是执行某种操作,没有返回值。
    函数式编程要求,只使用表达式,不使用语句。
    也就是说,每一步都是单纯的运算,而且都有返回值。

  3. 没有“副作用”
    “副作用”是指,函数内部与外部互动(最典型的情况,就是修改全局变量的值),产生运算以外的其他结果。
    函数式编程强调没有“副作用”,意味着函数要保持独立,所有功能就是返回一个新的值,没有其他行为,尤其是不得修改外部变量的值。
  4. 不修改状态
    在其他类型的语言中,变量往往用来保存“状态”(state)。
    不修改变量,意味着状态不能保存在变量中。
    函数式编程使用参数保存状态,最好的例子就是递归。
  5. 引用透明
    引用透明(Referential transparency),指的是函数的运行不依赖于外部变量或“状态”,
    只依赖于输入的参数,任何时候只要参数相同,引用函数所得的返回值总是相同的。

意义

  1. 代码简洁,开发快速
  2. 接近自然语言,易于理解
  3. 更方便的代码管理
    函数式编程不依赖、也不会改变外界的状态,只要给定输入参数,返回的结果必定相同。
    因此,每一个函数都可以被看做独立单元,很有利于进行单元测试和除错,以及模块化组合。
  4. 易于“并发”
    函数式编程不需要考虑“死锁”,因为它不修改变量,所以根本不存在“锁”线程的问题。
    1
    2
    3
    var s1 = Op1();
    var s2 = Op2();
    var s3 = concat(s1, s2);

由于s1和s2互不干扰,不会修改变量,谁先执行是无所谓的,所以可以放心地增加线程,把它们分别放在两个线程上完成。

  1. 代码的热升级
    函数式编程没有副作用,只要保证接口不变,内部实现是外部无关的。
    所以,可以在运行状态下直接升级代码不需要重启。



    扫盲

    函数式编程是一种编程模型,他将计算机运算看做是数学中函数的计算,并且避免了状态以及变量的概念。
    在函数式编程中,我们要做的就是把函数传来穿去,而这个,说成术语,我们把他叫做高阶函数。
    f(x) = y ,那么这个函数无论在什么场景下,都会得到同样的结果,这个我们称之为函数的确定性。
    函数式编程的抽象本质则是将函数也作为一个抽象单位,而反映成代码形式,则是高阶函数。
    循环是在描述我们该如何地去解决问题。
    递归是在描述这个问题的定义。
    其实尾递归就是不要保持当前递归函数的状态,而把需要保持的东西全部用参数给传到下一个函数里,这样就可以自动清空本次调用的栈空间。

    Java8 Lambda表达式

    匿名内部类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public class CommandTest
    {

    public static void main(String[] args){
    ProcessArray pa = new ProcessArray();
    int[] target = {3, -4, 6, 4};
    //处理数组,具体处理行为取决于匿名内部类
    pa.process(target, new Command(){
    public void process(int[] target){
    int sum = 0;
    for(int tmp: target){
    sum += tmp;
    }
    System.out.println("数组元素的总和:" + sum);
    }
    });
    }
    }

Lambda表达式

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
public  class CommandTest2
{

public static void main(String[] args){
ProcessArray pa = new ProcessArray();
int[] array = [3, -4, 6, 4];
//处理数组,具体处理行为取决于匿名内部类
pa.process(array, (int[] target)->{
int sum = 0;
for(int tmp : target){
sum += tmp;
}
System.out.println("数组元素的总和:" + sum);
});
}
}
Lambda表达式的主要作用就是代替匿名内部类的繁琐语法。
它由三部分组成:
形参列表。形参列表允许省略形参类型。如果形参列表中只有一个参数,甚至连形参列表的圆括号也可以省略。
箭头(->)必须通过英文中画线号和大于符号组成。
代码块。如果代码块只包含一条语句,Lambda表达式允许省略代码块的花括号,那么这条语句就不要用花括号表示语句结束。
Lambda代码块只有一条return语句,甚至可以省略return关键字。
```java
interface Eatable
{

void taste();
}

interface Flyable
{

void fly(String weather);
}

interface Addable
{

int add(int a, int b);
}

public class LambdaQs{
//调用该方法需要Eatable对象
public void eat(Eatable e){
System.out.println(e);
e.taste();
}

//调用该方法需要Flyable对象
public void drive(Flyable f){
System.out.println("我正在驾驶:" + f);
f.fly("碧空如洗的晴日");
}

//调用该方法需要Addable对象
public void test(Addable add){
System.out.println("5与3的和为: " + add.add(5, 3));
}

public static void main(String[] args){
LambdaQs lq = new LambdaQs();
//Lambda表达式的代码只有一条语句,可以省略花括号
lq.eat(()->System.out.println("苹果的味道不错!"));
//Lambda表达式的形参列表只有一个形参,可以省略圆括号
lq.drive(weather->{
System.out.println("今天天气是:" + weather);
System.out.println("直升飞机飞行平稳");
});
//Lambda表达式的代码块只有一条语句,可以省略花括号
//代码块只有一条语句,即使该表达式需要返回值,也可以省略return关键字
lq.test((a, b)->a + b);
}
}

为了保证Lambda表达式的目标类型是一个明确的函数式接口,可以由如下三种常见的方法:

  • 将Lambda表达式赋值给函数式接口类型的变量
  • 将Lambda表达式作为函数式接口类型的参数传给某个方法
  • 使用函数式接口对Lambda表达式进行强制类型转换

买买买网

学习计划

  1. 电商行业的背景。买买买商城的介绍。搭建项目工程。SVN的使用。
  2. 框架的整合。后台管理商品列表的实现。分页插件。
  3. 后台管理。商品添加。商品类目的选择、图片上传、富文本编辑器的使用。
  4. 商品规格的实现。
  5. 商城前台系统的搭建。首页商品分类的展示。Jsonp。
  6. cms系统的实现。前台打广告位的展示。
  7. cms系统添加缓存。Redis。缓存同步。
  8. 搜索功能的实现。使用solr实现搜索。
  9. 商品详情页面的展示。
  10. 单点登录系统。Session共享。
  11. 购物车订单系统的实现。
  12. nginx。反向代理工具。
  13. redis集群的搭建、solr集群的搭建。系统的部署。
  14. 项目总结。

2016-06-23

电商行业技术特点

  • 技术新
  • 技术范围广
  • 分布式
  • 高并发、集群、负载均衡、高可用
  • 海量数据
  • 业务复杂
  • 系统安全

买买买商城的模式

买买买商城是一个综合性的B2C平台,类似京东商城、天猫商城。
会员可以在商城浏览商品、下订单、以及参加各种活动。
管理员、运营可以在平台后台管理系统中管理商品、订单、会员等。
客服可以在后台管理系统中处理用户的询问以及投诉。
Mark-Down

技术架构

Mark-Down

UiAutomator环境搭建

必备条件

JDK
Android SDK(API高于15)
Eclipse(安装ADT插件)
ANT(用于编译生成jar包)

配置环境

JAVA_HOME
ANDORID_HOME
ANT_HOME

创建JAVA工程

Mark-Down

Mark-Down

Mark-Down

Mark-Down

编写测试代码

创建TestClass类,继承自UiAutomatorTestCase
编写testDemo()方法,添加测试用例

生成AutoRunner的jar包

  • cmd命令 android list 查看API 大于15的SDK的ID值
  • android create uitest-project -n -t -p
    Mark-Down

运行命令后,将会在工程的根目录下生成build.xml文件
Mark-Down

用ant命令编译生成jar cmd ant build
Mark-Down

PUSH并运行jar

adb push data/local/tmp
Mark-Down

adb shell uiautomator runtest -c <包名.类名>
Mark-Down

Java设计模式

简单工厂

简单工厂模式属于类的创建型模式,又叫静态工厂方法模式。
通过专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
1)工厂(Create)角色
简单工厂模式的核心,它负责实现创建所有实例的内部逻辑。
工厂类可以被外界直接调用,创建所需的产品对象。
2)抽象(Product)角色
简单工厂模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。
3)具体产品(Concrete Product)角色
简单工厂模式所创建的具体实例对象

简单工厂模式的优缺点

在这个模式中,工厂类是整个模式的关键所在。
它包含必要的判断逻辑,能够根据外界给定的信息,决定究竟应该创建哪个具体类的对象。
用户在使用时可以直接根据工厂类去创建所需的实例,而无需了解这些对象是如何创建以及如何组织的。
有利于整个软件体系的结构的优化。
不难发现,简单工厂模式的缺点也正体现在其工厂类上,由于工厂类集中了所有实例的创建逻辑,所以”高内聚”方面做的并不好。
另外,当系统中的具体产品类不断增多时,可能会出现要求工厂类也要做相应的修改,扩展性并不好。

实例

Fruit接口

1
2
3
4
public interface Fruit{
//采集
public void get();
}

Apple实现Fruit接口

1
2
3
4
5
public class Apple implements Fruit{
public void get(){
System.out.print("采集苹果");
}
}

Banana实现Fruit接口

1
2
3
4
5
public class Banana implements Fruit{
public void get(){
System.out.print("采集香蕉");
}
}

简单工厂FruitFactory

1
2
3
4
5
6
7
public class FruitFactory{
public static Fruit getFruit(String type) throws InstantiationException,
IllegalAccessException, ClassNotFoundException{

Class fruit = Class.forName(type);
return (Fruit) fruit.newInstance();
}
}

测试类

1
2
3
4
5
6
7
8
9
public class MainClass{
public static void main(String[] args) throws InstantiationException,
IllegalAccessException, ClassNotFoundException{

Fruit apple = FruitFactory.getFruit("Apple");
Fruit banana = FruitFactory.getFruit("Banana");
apple.get();
banana.get();
}
}




工厂方法

工厂方法模式同样属于类的创建型模式又称多态工厂模式。
工厂方法模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中。
核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅负责具体工厂子类必须实现的接口。
这样进一步抽象化的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新的产品。
1.抽象工厂(Creator)角色
工厂方法模式的核心,任何工厂类都必须实现这个接口。
2.具体工厂(Concrete Creator)角色
具体工厂类是抽象工厂的一个实现,负责实例化产品对象。
3.抽象(Product)角色
工厂方法模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。
4.具体产品(Concrete Product)角色
工厂方法模式所创建的具体实例对象。

工厂方法模式和简单工厂模式比较

工厂方法的核心是一个抽象工厂类,而简单工厂模式把核心放在一个具体类上。
工厂方法模式之所以有一个别名叫多态工厂模式是因为具体工厂类都有共同的接口,或者有共同的抽象父类。
当系统扩展需要添加新的产品对象时,仅仅需要添加一个具体对象以及一个具体工厂对象,原有工厂对象不需要进行任何修改,也不需要修改客户端,很好的符合了“开放-封闭”原则。
而简单工厂模式在添加新产品对象后不得不修改工厂方法,扩展性不好。

实例

Fruit接口

1
2
3
public interface Fruit{
public void get();
}

Apple类

1
2
3
4
5
public class Apple implements Fruit{
public void get(){
System.out.print("采集苹果");
}
}

Banana类

1
2
3
4
5
public class Banana implements Fruit{
public void get(){
System.out.print("采集香蕉");
}
}

Pear类

1
2
3
4
5
public class Pear implements Fruit{
public void get(){
System.out.print("采集梨子");
}
}

抽象的FruitFactory

1
2
3
public interface FruitFactory{
public Fruit getFruit();
}

AppleFactory

1
2
3
4
5
public class AppleFactory implements FruitFactory{
public Fruit getFruit(){
return new Apple();
}
}

BananaFactory

1
2
3
4
5
public class BananaFactory implements FruitFactory{
public Fruit getFruit(){
return new Banana();
}
}

PearFactory

1
2
3
4
5
public class PearFactory implements FruitFactory{
public Fruit getFruit(){
return new Pear();
}
}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MainClass{
public static void main(String[] args){
FruitFactory ff = new AppleFactory();
Fruit apple = ff.getFruit();
apple.get();

FruitFactory ff2 = new BananaFactory();
Fruit banana = ff2.getFruit();
banana.get();

FruitFactory ff3 = new PearFactory();
Fruit pear = ff3.getFruit();
pear.get();
}
}

实例2

Operation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public abstract class Operation{
private double num1;
private double num2;

public double getNum1(){
return num1;
}

public void setNum1(double num1){
this.num1 = num1;
}

public double getNum2(double num2){
this.num2 = num2;
}

public abstract double getResult();
}

OperationFactory接口

1
2
3
public interface OperationFactory{
public Operation getOperation();
}

AddOperation

1
2
3
4
5
6
public class AddOperation extends Operation{
public double getResult(){
double result = this.getNum1() + this.getNum2();
return result;
}
}

AddOperationFactory

1
2
3
4
5
public class AddOperationFactory implements OperationFactory{
public Operation getOperation(){
return new AddOperation();
}
}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class MainClass{
public static void main(String[] args){
System.out.print("--计算器程序--");
System.out.print("输入第一个操作数");
Scanner scanner = new Scanner(System.in);
String strNum1 = scanner.nextLine();

System.out.println("输入运算符");
String strNum2 = scanner.nextLine();
double result = 0;
double num1 = Double.parseDouble(strNum1);
double num2 = Double.parseDouble(strNum2);

if("+".equals(oper)){
OperationFactory factory = new AddOperationFactory();
Operation operation = factory.getOperation();
operation.setNum1(num1);
operation.setNum2(num2);
result = operation.getResult();
}

System.out.print(strNum1 + oper + strNum2 + "=" + result);
}
}




抽象工厂

抽象工厂模式是所有形态的工厂模式中最为抽象和最其一般性的。
抽象工厂模式可以向客户端提供一接口,使得客户端在不必指定产品的具体类型的情况下,能够创建多个产品族的产品对象。

角色及其职责

1.抽象工厂(Creator)角色
抽象工厂模式的核心,包含对多个产品结构的声明,任何工厂类都必须实现这个接口。
2.具体工厂(Concrete Creator)角色
具体工厂类是抽象工厂的一个实现,负责实例化某个产品族中的产品对象。
3.抽象(Product)角色
抽象模式所创建的具体实例对象。
4.具体产品(Concrete Product)角色
抽象模式所创建的具体实例对象。
总结:抽象工厂中方法对应产品结构,具体工厂对应产品族。

实例

Fruit接口

1
2
3
public interface Fruit{
public void get();
}

Apple接口

1
2
3
public abstract class Apple implements Fruit{
public abstract void get();
}

Banana接口

1
2
3
public abstract class Banana implements Fruit{
public abstract void get();
}

FruitFactory接口

1
2
3
4
public interface FruitFactory{
public Fruit getApple();
public Fruit getBanana();
}

NorthApple类

1
2
3
4
5
public class NorthApple extends Apple{
public void get(){
System.out.print("采集北方苹果");
}
}

NorthBanana类

1
2
3
4
5
public class NorthBanana extends Banana{
public void get(){
System.out.print("采集北方香蕉");
}
}

NorthFruitFactory

1
2
3
4
5
6
7
8
9
public class NorthFruitFactory implements FruitFactory{
public Fruit getApple(){
return new NorthApple();
}

public Fruit getBanana(){
return new NorthBanana();
}
}

SouthApple

1
2
3
4
5
public class SouthApple extends Apple{
public void get(){
System.out.print("采集南方苹果");
}
}

SouthBanana

1
2
3
4
5
public class SouthBanana extends Banana{
public void get(){
System.out.print("采集南方香蕉");
}
}

SouthFruitFactory

1
2
3
4
5
6
7
8
9
public class SouthFruitFactory implements FruitFactory{
public Fruit getApple(){
return new SouthApple();
}

public Fruit getBanana(){
return new SouthBanan();
}
}

WenshiApple

1
2
3
4
5
public class WenshiApple extends Apple{
public void get(){
System.out.print("采集温室苹果");
}
}

WenshiBanana

1
2
3
4
5
public class WenshiBanana extends Banana{
public void get(){
System.out.print("采集温室香蕉");
}
}

WenshiFruitFactory

1
2
3
4
5
6
7
8
9
public class WenshiFruitFactory implements FruitFactory{
public Fruit getApple(){
return new WenshiApple();
}

public Fruit getBanana(){
return new WenshiBanana();
}
}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class MainClass{
public static void main(String[] args){
FruitFactory ff = new NorthFruitFactory();
Fruit apple = ff.getApple();
apple.get();

Fruit banana = ff.getBanana();
banana.get();

FruitFactory ff2 = new SouthFruitFactory();
Fruit apple2 = ff2.getApple();
apple2.get();

Fruit banana2 = ff2.getBanana();
banana2.get();

FruitFactory ff3 = new WenshiFruitFactory();
Fruit apple3 = ff3.getApple();
apple3.get();

Fruit banana3 = ff3.getBanana();
banana3.get();
}
}




单例模式

饿汉式
懒汉式
双重检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Person{
private String name;
private static Person person;
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
private Person(){
}

public static Person getPerson(){
if(person == null){
synchronized (Person.class){
if(person == null){
person = new Person();
}
}
}
return person;
}
}

测试类

1
2
3
4
5
6
7
8
9
10
11
public class MainClass{
public static void main(String[] args){
Person per = Person.getPerson();
Person per2 = Person.getPerson();
per.setName("张三");
pre2.setName("李四");

System.out.print(per.getName());
System.out.print(per2.getNumber());
}
}




原型模式

Prototype模式是一种对象创建型模式,它采用复制原型对象的方法来创建对象的实例。
使用Prototype模式创建的实例,具有与原型一样的数据结构。

原型模式的特点

1.由原型对象自身创建目标对象。也就是说,对象创建这一动作发自原型对象本身。
2.目标对象是原型对象的一个克隆。也就是说,通过Prototype模式创建的对象,不仅仅与原型对象具有相同的结构,还与原型对象具有相同的值。
3.根据对象克隆的层次的不同,有浅克隆与深克隆。

原型模式应用场景。

在创建对象的时候,我们不只是希望被创建的对象继承其基类的基本结构,还希望继承原型对象的数据。
希望对目标对象的修改不影响既有的原型对象(深度克隆时完全互不影响)
隐藏克隆操作的细节,很多时候,对对象本身的克隆需要涉及到类本身的数据细节。

实例

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
50
51
public class Person implements Cloneable{
private String name;
private int age;
private String sex;
private List<String> friends;
public List<String> getFriends(){
return friends;
}
public void setFriends(List<String> friends){
this.friends = friends;
}

public String getName(){
return name;
}

public void setName(String name){
this.name = name;
}

public int getAge(){
return age;
}

public void setAge(int age){
this.age = age;
}

public String getSex(){
return sex;
}

public void setSex(String sex){
this.sex = sex;
}

public Person clone(){
try{
Person person = (Person)super.clone();
List<String> newFriends = new ArrayList<String>();
for(String friend : this.getFriends()){
newfriends.add(friend);
}
person.setFriends(newfriends);
return person;
}catch(CloneNotSupportedException e){
e.printStackTrace();
return null;
}
}
}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class MainClass{
Person person1 = new Person();
List<String> friends = new ArrayList<String>();
friends.add("James");
friends.add("Yao");

person1.setFriends(friends);
Peron preson2 = person1.clone();
System.out.print(person1.getFriends());
System.out.print(person2.getFriends());

friends.add("Mike");
person1.setFriends(friends):
System.out.println(person1.getFriends());
System.out.println(person2.getFriends());

}




建造者模式

Builder模式是一种对象创建型模式。
用来隐藏复合对象的创建过程,它把复合对象的创建过程加以抽象,通过子类继承和重载的方法,动态地创建具有复合属性的对象。

建造者模式应用场景

对象的创建:Builder模式是为对象的创建而设计的模式。
创建的是一个复合对象:被创建的对象为一个具有复合属性的复合对象
关注对象创建的各部分的创建过程:不同的工厂(这里指builder生成器)对产品属性有不同的创建方法。

实例

House类

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
public class House{
private String floor;
private String wall;
private String housetop;
public String getFloor(){
return floor;
}
public void setFloor(String floor){
this.floor = floor;
}

public String getWall(){
return wall;
}

public void setWall(String wall){
this.wall = wall;
}

public String getHousetop(){
return housetop;
}

public void setHousetop(String housetop){
this.housetop = housetop;
}
}

HouseBuilder接口

1
2
3
4
5
6
public interface HouserBuilder{
public void makeFloor();
public void makeWall();
public void makeHousertop();
public House getHouse();
}

GongyuBuilder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class GongyuBuilder implements HouseBuilder{
House house = new Houser();
public Houser getHouse(){
return house;
}

public void makeFloor(){
house.setFloor("公寓-->地板");
}

public void makeHousetop(){
house.setHousetop("公寓-->房顶");
}

public void makeWall(){
house.setWall("公寓-->墙");
}
}

PingFangBuilder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class PingFangBuilder implements HouseBuilder{
House house = new House();
public void makeFloor(){
house.setFloor("平方-->地板");
}

public void makeHousetop(){
house.setHousetop("平方-->房顶");
}

public void makeWall(){
house.setWall("平方-->墙");
}

public House getHouse(){
return house;
}
}

HouseDirector

1
2
3
4
5
6
7
public class HouseDirector{
public void makeHouse(HouseBuilder builder){
builder.makeFloor();
builder.makeWall();
builder.makeHousetop();
}
}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
public class MainClass{
public static void main(String[] args){
HouseBuilder builder = new GongyuBuilder();
HouseDirector director = new HouseDirector();
director.makeHouse(builder);

Houser house = builder.getHouse();
System.out.print(house.getFloor());
System.out.print(house.getWall());
System.out.print(house.getHousetop());
}
}




装饰者模式

装饰(Decorator)模式又称包装模式。
通过一种对客户端透明的方式来扩展对象的功能,是继承关系的一个替换方案。

装饰模式的角色和职责

抽象组件角色:一个抽象接口,是被装饰类和装饰类的父接口。
具体组件角色:为抽象组件的实现类。
抽象装饰角色:包含一个组件的引用,并定义了与抽象组件一致的接口。
具体装饰角色:为抽象装饰角色的实现类。负责具体的装饰。

实例

Car

1
2
3
4
public interface Car{
public void show();
public void run();
}

CarDecorator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public abstract class CarDecorator implements Car{
private Car car;
public Car getCar(){
return car;
}
public void setCar(Car car){
this.car = car;
}

public CarDecorator(Car car){
this.car = car;
}

public abstract void show();
}

RunCar

1
2
3
4
5
6
7
8
9
public class RunCar implements Car{
public void run(){
System.out.print("可以跑");
}

public void show(){
this.run();
}
}

SwimCarDecorator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class SwimCarDecorator extends CarDecorator{
public SwimCarDecorator(Car car){
super(car);
}

public void show(){
this.getCar().show();
this.swim();
}

public void swim(){
System.out.print("可以游");
}

public void run(){
}
}

FlyCarDecorator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class FlyCarDecorator extends CarDecorator{
public FlyCarDecorator(Car car){
super(car);
}

public void show(){
this.getCar().show();
this.fly();
}

public void fly(){
System.out.println("可以飞");
}

public void run(){

}
}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MainClass{
public static void main(String[] args){
Car car = new RunCar();
car.show();
System.out.println("----");

Car swimCar = new SwimCarDecorator(car);
swimCar.show();
System.out.println("----------");

Car flySwimCar = new FlyCarDecorator(swimCar);
flySwimCar.show();
}
}




策略模式

Strategy模式也叫策略模式是行为模式之一。
它对一系列的算法加以封装,为所有算法定义一个抽象的算法接口,并通过继承该抽象算法接口对所有的算法加以封装和实现,
具体的算法选择交由客户端决定(策略)。
Stragey模式主要用来平滑地处理算法的切换。

策略模式的角色和职责

Strategy:策略(算法)抽象。
ConcreteStrategy:各种策略(算法)的具体实现。
Context:策略的外部封装类,或者受策略的容器类。根据不同策略执行不同的行为。策略由外部环境决定。

策略模式的优点

1.策略模式提供了管理相关的算法族的办法。
策略类的等级结构定义了一个算法或行为族。
恰当使用继承可以把公共的代码移到父类里面,从而避免重复的代码。
2.策略模式提供了可以替换继承关系的办法。
继承可以处理多种算法或行为。
如果不是用策略模式,那么使用算法或行为的环境类就可能会有一些子类,每一个子类提供一个不同的算法或行为。
但是,这样一来算法或行为的使用者就和算法或行为混在一起。
决定使用哪一种算法或采取哪一种行为的逻辑就和算法或行为的逻辑混合在一起,从而不可能再独立演化。
继承使得动态改变算法或行为变得不可能。
3.使用策略模式可以避免使用多重条件转移语句。
多重转移语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑和算法或行为的逻辑混在一起,统统列在一个多重转移语句里面,比使用继承的办法还要原始和落后。

策略模式的缺点

1.客户端必须知道所有的策略类,并自行决定使用哪一种策略。
这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。
换言之,策略模式只适用于客户端知道所有的算法或行为的情况。
2.策略模式造成很多的策略类。
有时可以通过把依赖于环境的状态保存到客户端里面,而将策略类设计成可共享的,这样策略类实例可以被不同客户单使用。
换言之,可以使用享元模式来减少对象的数量。

实例1

Strategy

1
2
3
public interface Strategy{
public void encrypt();
}

MDSStrategy

1
2
3
4
5
public class MDSStrategy implements Strategy{
public void encrypt(){
System.out.println("执行MDS加密");
}
}

MD5Strategy

1
2
3
4
5
public class MD5Strategy implements Strategy{
public void encrypt(){
System.out.println("执行MD5加密");
}
}

Context

1
2
3
4
5
6
7
8
9
10
public class Context{
private Strategy strategy;
public Context(Strategy strategy){
this.strategy = strategy;
}

public void encrypt(){
this.strategy.encrypt();
}
}

测试类

1
2
3
4
5
6
public class MainClass{
public static void main(String[] args){
Context context = new Context(new MDSStrategy());
context.encrypt();
}
}

实例2

Strategy

1
2
3
public interface Strategy{
public double cost(double num);
}

StrategyA

1
2
3
4
5
public class StrategyA implements Strategy{
public double cost(double num){
return num * 0.8;
}
}

StrategyB

1
2
3
4
5
6
7
8
public class StrategyB implements Strategy{
public double cost(double num){
if(num >= 200){
return num - 50;
}
return num;
}
}

Context

1
2
3
4
5
6
7
8
9
10
11
public class Context{
private Strategy strategy;

public Context(Strategy strategy){
this.strategy = strategy;
}

public double cost(double num){
return this.strategy.cost(num);
}
}

测试类

1
2
3
4
5
6
7
8
public class MainClass{
public static void main(String[] args){
double num = 200;
Context context = new Context(new StrategyB());
double newNum = context.cost(num);
System.out.print("实际付账" + newNum + "圆");
}
}




观察者模式

Observer模式是行为模式之一,它的作用是当一个对象的状态发生变化时,能够自动通知其他关联对象。
自动刷新对象状态。
Observer模式提供给关联对象一种同步通信的手段,使某个对象与依赖它的其他对象之间保持状态同步。

观察者模式的角色和职责

Subject(被观察者)
被观察的对象。当需要被观察的状态发生变化时,需要通知队列中所有观察者对象。
Subject需要维持(添加、删除、通知)一个观察者对象的队列列表。
ConcreteSubject 被观察者的具体实心。包含一些基本的属性状态及其他操作。
Observer(观察者)
接口或抽象类。当Subject的状态发生变化时,Observer对象将通过一个callback函数得知通知。
ConcreteObserver
观察者的具体实现。得到通知后将完成一些具体的业务逻辑处理。

实例1

Person

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
public class Person extends Observable{
private String name;
private String sex;
private int age;

public String getName(){
return name;
}

public void setName(String name){
this.name = name;
this.setChanged();
this.notifyObservers();
}

public String getSex(){
return sex;
}

public void setSex(String sex){
this.sex =sex;
this.setChanged();
this.notifyObservers();
}

public int getAge(){
return age;
}

public void setAge(int age){
this.age = age;
this.setChanged();
this.notifyObservers();
}
}

MyObserver

1
2
3
4
5
public class MyObserver implements Observer{
public void update(Observable o , Object o){
System.out.print("对象发生了变化");
}
}

测试类

1
2
3
4
5
6
7
8
9
10
11
public class MainClass{
public static void main(String[] args){
Person person = new Person();
person.addObserver(new MyObserver());
person.addObserver(new MyObserver());
System.out.print(person.countObservers());
person.setName("xxx");
person.setAge("22");
person.setSex("男");
}
}

实例2

Article

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Article{
private String articleTitle;
private String articleContent;

public String getArticleTitle(){
return articleTitle;
}

public void setArticleTitle(String articleTitle){
this.articleTitle = articleTitle;
}

public String getArticleContent(){
return articleContent;
}

public void setArticleContent(String articleContent){
this.articleContent = articleContent;
}
}

BlogUser

1
2
3
4
5
6
7
8
9
10
11
public class BlogUser extends Observable{
public void publishBlog(String articleTitle,String articleContent){
Article art = new Article();
art.setArticleTitle(articleTitle);
art.setArticleContent(articleContent);
System.out.println("博主发表新文章文章标题:" + articleTitle + ",文章内容:" +
articleContent);
this.setChanged();
this.notifyObserver(art);
}
}

MyObserver

1
2
3
4
5
6
7
8
public class MyObserver implements Observer{
public void update(Observable o, Object arg){
Article art = (Article)arg;
System.out.print("博主发表新文章");
System.out.print("标题:"+ art.getArticleTitle());
System.out.print("内容:"+ art.getArticleContent());
}
}

测试类

1
2
3
4
5
6
7
public class MainClass{
public static void main(String[] args){
BlogUser user = new BlogUser();
user.addObserver(new MyObserver());
user.publishBlog("哈哈,博客上线了","人傻,钱多,速来");
}
}




享元模式

Flyweight模式也叫享元模式,是构造型模式之一,它通过与其他类似对象共享数据来减少内存占用。

享元模式的角色和职责

抽象享元角色:所有具体享元类的父类,规定一些需要实现的公共接口。
具体享元角色:抽象享元角色的具体实现类,并实现了抽象享元角色规定的方法。
享元工厂角色:负责创建和管理享元角色。

实例1

MyCharacter

1
2
3
4
5
6
7
8
9
public class MyCharacter{
private char mychar;
public MyCharacter(char mychar){
this.mychar = mychar;
}
public void display(){
System.out.println(mychar);
}
}

MyCharacterFactory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MyCharacterFactory{
private Map<Character,MyCharacter> pool;
public MyCharacterFactory(){
pool = new HashMap<Character, MyCharacter>();
}
public MyCharacter getMyCharacter(Character character){
MyCharacter myChar = pool.get(character);
if(myChar == null){
myChar = new MyCharacter(character);
pool.put(character, myChar);
}
return myChar;
}
}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class MainClass{
public static void main(String[] args){
MyCharacterFactory factory = new MyCharacterFactory();
MyCharacter myChar1 = factory.getMyCharacter('a');
MyCharacter myChar2 = factory.getMyCharacter('b');
MyCharacter myChar3 = factory.getMyCharacter('a');
MyCharacter myChar4 = factory.getMyCharacter('d');

myChar1.display();
myChar2.display();
myChar3.display();
myChar4.display();

if(myChar1 == myChar3){
System.out.println("true");
}else{
System.out.println("false");
}
}
}

实例2

Person

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
public class Person{
private String name;
private int age;
private String sex;

public Person(String name, int age, String sex){
super();
this.name = name;
this.age = age;
this.sex = sex;
}

public Person(){
}

public String getName(){
return name;
}

public void setName(String name){
this.name = name;
}

public int getAge(){
return age;
}

public void setAge(int age){
this.age = age;
}

public String getSex(){
return sex;
}

public void setSex(String sex){
this.sex = sex;
}
}

Teacher

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Teacher extends Person{
private String number;
public String getNumber(){
return number;
}

public void setNumber(String number){
this.number = number;
}

public Teacher(String name,int age, String sex, String number){
super(name, age, sex);
this.number = number;
}

public Teacher(){
super();
}
}

TeacherFactory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class TeacherFactory{
private Map<String, Teacher> pool;
public TeacherFactory(){
pool = new HashMap<String, Teacher>();
}
public Teacher getTeacher(String number){
Teacher teacher = pool.get(number);
if(teacher == null){
teacher = new Teacher();
teacher.setNumber(number);
pool.put(number,teacher);
}
return teacher;
}
}

测试类
MainClass

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class MainClass{
public static void main(String[] args){
TeacherFactory factory = new TeacherFactroy();
Teacher teacher1 = factory.getTeacher("0102034");
Teacher teacher2 = factory.getTeacher("0102035");
Teacher teacher3 = factory.getTeacher("0102034");
Teacher teahcer4 = factory.getTeacher("0102037");

System.out.println(teacher1.getNumber());
System.out.println(teacher2.getNumber());
System.out.println(teacher3.getNumber());
System.out.println(teacher4.getNumber());

if(teacher1 == teacher2){
System.out.println("true");
}else{
System.out.println("false");
}
}
}




代理模式

Proxy模式又叫做代理模式,是构造型的设计模式之一,
它可以为其它对象提供一种代理(Proxy)以控制对这个对象的访问。
所谓代理,是指具有与代理元(被代理的对象)具有相同的接口的类,客户端必须通过代理与被代理的目标类交互,
而代理一般在交互的过程中(交互前后),进行某些特别的处理。

代理模式的角色和职责

subject(抽象主题角色) 真实主题与代理主题的共同接口。
RealSubject(真实主题角色):定义了代理角色所代表的真实对象。
Proxy(代理主题角色):含有对真实主题角色的引用,代理角色通常在将客户端调用传递给真实主题对象之前或之后执行某些操作,而不是单纯返回真实的对象。

动态代理

1.InvocationHandler接口
2.invoke方法
3.Proxy.newProxyInstance();

实例1

Subject

1
2
3
public interface Subject{
public void sellBook();
}

RealSubject

1
2
3
4
5
public class RealSubject implements Subject{
public void sellBook(){
System.out.println("卖书");
}
}

ProxySubject

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ProxySubject implements Subject{
private RealSubject realSubject;

public void sellBook(){
dazhe(); //before
if(realSubject == null){
realSubject = new RealSubject();
}
realSubject.sellBook();
give(); //after
}

public void dazhe(){
System.out.println("打折");
}

public void give(){
System.out.println("赠送代金券");
}
}

测试类

1
2
3
4
5
6
public class MainClass{
public static void main(String[] args){
ProxySubject proxySubject = new ProxySubject();
proxySubject.sellBook();
}
}

实例2

Subject

1
2
3
public interface Subject{
public void sellBook();
}

RealSubject

1
2
3
4
5
public class RealSubject implements Subject{
public void sellBook(){
System.out.println("卖书");
}
}

MyHandler

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
public class MyHandler implements InvocationHandler{
private RealSubject realSubject;
public void setRealSubject(RealSubject realSubject){
this.realSubject = realSubject;
}
public Object invoke(Object proxy,Method method,Object[] args){
Object result = null;
dazhe();
try{
result = method.invoke(realSubject, args);
}catch(IllegalArgumentException e){
e.printStackTrace();
}catch(IllegalAccessException e){
e.printStackTrace();
}catch(InvocationTargetException e){
e.printStackTrace();
}
give();
return result;
}

public void dazhe(){
System.out.println("打折");
}

public void give(){
System.out.println("赠送代金券");
}

}

测试类

1
2
3
4
5
6
7
8
9
10
11
public class MainClass{
public static void main(String[] args){
RealSubject realSubject = new RealSubject();
MyHandler myHandler = new MyHandler();
myHandler.setRealSubject(realSubject);

Subject proxySubject = (Subject)Proxy.newProxyInstance(RealSubject.class.getClassLoader(),
realSubject.getClass().getInterfaces(), myHandler);
proxySubject.sellBook();
}
}




外观模式

Facade模式也叫外观模式,是由GoF提出的23种设计模式中的一种。
Facade模式为一组具有类似功能的类群,比如类库,子系统等等,提供一个一致的简单的界面。
这个一致的简单的界面被称作facade。

外观模式的角色和职责

Facade 为调用方定义简单的调用接口。
Clients 调用者。通过Facade接口调用提供某功能的内部类群。
Packages 功能提供者。指提供功能的类群(模块或子系统)。

实例1

SystemA

1
2
3
4
5
6
public class SystemA{
//A为子系统实现功能
public void dSomething(){
System.out.println("实现A子系统功能");
}
}

SystemB

1
2
3
4
5
6
public class SystemB{
//B子系统实现功能
public void doSomething(){
System.out.println("实现B子系统功能");
}
}

SystemC

1
2
3
4
5
6
public class SystemC{
//C子系统实现功能
public void doSomething(){
System.out.println("实现C子系统功能");
}
}

Facade

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Facade{
private SystemA systemA;
private SystemB systemB;
private SystemC systemC;

public Facade(){
systemA = new SystemA();
systemB = new SystemB();
systemC = new SystemC();
}

public void doABC(){
this.systemA.doSomething();
this.systemB.doSomething();
this.systemC.doSomething();
}

public void doAB(){
this.systemA.doSomething();
this.systemB.doSomething();
}
}

测试类1

1
2
3
4
5
6
public class MainClass1{
public static void main(String[] args){
Facade facade = new Facade();
facade.doABC();
}
}

测试类2

1
2
3
4
5
6
public class MainClass2{
public static void main(String[] args){
Facade facade = new Facade();
facade.doAB();
}
}

实例2

GuoZai

1
2
3
4
5
public class GuoZai{
public void mai(){
System.out.println("买国债");
}
}

Gupiao

1
2
3
4
5
public class Gupiao{
public void mai(){
System.out.println("买股票");
}
}

Qihuo

1
2
3
4
5
public class Qihuo{
public void chao(){
System.out.println("买期货");
}
}

JiJin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class JiJin{
private Gupiao gupiao;
private GuoZai guozai;
private Qihuo qihuo;

public JiJin(){
this.guozai = new GuoZai();
this.gupiao = new Gupiao();
this.qihuo = new Qihuo();
}

public void maiJijinA(){
this.guozai.mai();
this.gupiao.mai();
}

public void maiJijinB(){
this.guozai.mai();
this.gupiao.mai();
this.qihuo.chao();
}
}

测试类

1
2
3
4
5
6
public class MainClass{
public static void main(String[] args){
JiJin jijin = new JiJin();
jijin.maiJijinB();
}
}




组合模式

Composite模式也叫组合模式,是构造型的设计模式之一。
通过递归手段来构造树形的对象结构,并可以通过一个对象来访问整颗树。

组合模式的角色和职责

Component(树形结构的节点抽象)
为所有的对象定义统一的接口(公共属性,行为等的定义)
提供管理子节点对象的接口方法
[可选]提供管理父节点对象的接口方法

Leaf(树形结构的叶节点)
Component的实现子类

Composite(树形结构的枝节点)
Component的实现子类、

实例

IFile

1
2
3
4
5
6
7
8
9
10
11
12
13
public interface IFile{
//显示文件或文件夹的名称
public void display();

//添加
public boolean add(IFile file);

//移除
public boolean remove(IFile file);

//获得子节点
public List<IFile> getChild();
}

File

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class File implements IFile{
private String name;
public File(String name){
this.name = name;
}

public void display(){
System.out.println(name);
}

public List<IFile> getChild(){
return null;
}

public boolean add(IFile file){
return false;
}

public boolean remove(IFile file){
return false;
}
}

Folder

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
public class Folder implements IFile{
private String name;
private List<IFile> children;

public Folder(String name){
this.name = name;
children = new ArrayList<IFile>();
}

public void display(){
System.out.println(name);
}

public List<IFile> getChild(){
return children;
}

public boolean add(IFile file){
return children.add(file);
}

public boolean remove(IFile file){
return children.remove(file);
}
}

测试类

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
public class MainClass{
public static void main(String[] args){
//C盘
Folder rootFolder = new Folder("C:");
//newFolder目录
Folder newFolder = new Folder("newFolder");
//newfile.txt文件
File newfile = new File("newfile.txt");

rootFolder.add(newFolder);
rootFolder.add(newfile);

//inewfolder目录
Folder inewFolder = new Folder("inewfolder");
File inewfile = new File("inewfile.txt");
newFolder.add(inewFolder);
newFolder.add(inewfile);

Folder iinewFolder = new Folder("iinewfolder");
File iinewfile = new File("iinewfile.txt");
inewFolder.add(iinewFolder);
inewFolder.add(iinewfile);

displayTree(rootFolder, 0);
}

public static void displayTree(IFile rootFolder, int deep){
for(int i = 0 ; i < deep; i++){
System.out.print("--");
}
//显示自身的名称
rootFolder.display();
//获得子树
List<IFile> children = rootFolder.getChild();
//遍历子树
for(IFile file : children){
if(file instanceof File){
for(int i = 0 ; i <= deep; i++){
System.out.print("--");
}
file.display();
}else{
displayTree(file, deep + 1);
}
}
}
}




解释器模式

Interpreter模式也叫解释器模式,是行为模式之一。
它是一种特殊的设计模式,它建立一个解释器,对于特定的计算机程序设计语言,用来解释预先定义的文法。
简单地说,Interpreter模式是一种简单的语法解释器架构。

解释器模式应用场景

当有一个语言需要解释执行,并且你可将该语言中的句子表示为一个抽象语法树时,可使用解释器模式。而当存在以下情况时该模式效果最好:

  • 该文法简单对于复杂的文法,文法的类层次变得庞大而无法管理。此时语法分析程序生成器这样的工具是更好的选择。它们无需构建抽象语法树即可解释表达式,这样可以节省空间而且还可能节省时间。
  • 效率不是一个关键问题,最高效的解释器通常不是通过直接解释语法分析树实现的,而是首先将它们转换成另一种形式。例如,正则表达式通常被转换成状态机。但即使在这种情况下,转换器仍可用解释器模式实现,该模式仍是有用的。

    解释器模式的角色和职责

    Context 解释器上下文环境类。用来存储解释器的上下文环境,比如需要解释的文法等。
    AbstractExpression 解释器抽象类。
    ConcreteExpression 解释器具体实现类。

    实例

    Expression类
    1
    2
    3
    4
    5
    6
    /*
    *抽象解释器
    */

    public abstract class Expression{
    public abstract void interpret(Context context);
    }

MinusExpression类

1
2
3
4
5
6
7
8
9
10
public class MinusExpression extends Expression{
public void interpret(Context context){
System.out.println("自动递减");
String input = context.getInput();
int inInput = Integer.parseInt(input);
--inInput;
context.setInput(String.valueOf(inInput));
context.setOutput(inInput);
}
}

PlusExpression类

1
2
3
4
5
6
7
8
9
10
public class PlusExpression extends Expression{
public void interpret(Context context){
System.out.println("自动递减");
String input = context.getInput();
int intInput = Integer.parseInt(input);
++intInput;
context.setInput(String.valueOf(intInput));
context.setOutput(intInput);
}
}

Context类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Context{
private String input;
private int output;

public Context(String input){
this.input = input;
}

public String getInput(){
return input;
}

public void setInput(String input){
this.input = input;
}

public int getOutput(){
return output;
}

public void setOutput(int output){
this.output = output;
}
}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class MainClass{
public static void main(String[] args) {
String number = "20";
Context context = new Context(number);

List<Expression> list = new ArrayList<Expression>();
list.add(new PlusExpression());
list.add(new PlusExpression());
list.add(new MinusExpression());
list.add(new MinusExpression());
list.add(new MinusExpression());
list.add(new MinusExpression());

for(Expression ex :list){
ex.interpret(context);
System.out.println(context.getOutput());
}
}
}




中介者模式

Mediator模式也叫中介者模式,是由GoF提出的23种软件设计模式的一种。
Mediator模式是行为模式之一,在Mediator模式中,类之间的交互行为被统一放在Mediator的对象中,对象通过Mediator对象同其他对象交互,Mediator对象起着控制器的作用。

中介者模式的角色和职责

mediator 中介者类的抽象父类
concreteMediator 具体的中介者类。
colleague 关联类的抽象父类。
concreteColleague 具体的关联类。

中介者模式的优点

  • 将系统按功能分割成更小的对象,符合类的最小设计原则
  • 对关联对象的集中控制
  • 减小类的耦合程度,明确类之间的相互关系:当类之间的关系过于复杂时,其中任何一个类的修改都会影响到其他类,不符合类的设计的开闭原则,而Mediator模式将原来相互依存的多对多的类之间的关系简化为Mediator控制类与其他关联类的一对多的关系,当其中一个修改时,可以对其他关联类不产生影响(即使有修改,也集中在Mediator控制类)。
  • 有利于提高类的重用性

    实例

    Person
    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
    public abstract class Person{
    private String name;
    private int condition;
    private Mediator mediator;

    public Person(String name,int condition, Mediator mediator){
    super();
    this.name = name;
    this.condition = condition;
    this.mediator = mediator;
    }

    public Mediator getMediator(){
    return mediator;
    }

    public void setMediator(Mediator mediator){
    this.mediator = mediator;
    }

    public String getName(){
    return name;
    }

    public void setName(String name){
    this.name = name;
    }

    public int getCondition(){
    return condition;
    }

    public void setCondition(int condition){
    this.condition = condition;
    }

    public abstract void getPartner(Person person);

    }

Mediator

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
public class Mediator{
private Man man;
private Woman woman;

public void setMan(Man man){
this.man = man;
}

public void setWoman(Woman woman){
this.woman = woman;
}

public void getPartner(Person person){
if(person instanceof Man){
this.setMan((Man)person);
}else{
this.setWoman((Woman)person);
}

if(man == null || woman == null){
System.out.println("汗,不是同性恋");
}else{
if(man.getCondition() == woman.getCondition()){
System.out.println(man.getName() + "和" + woman.getName() + "绝配");
}else{
System.out.println(man.getName() + "和" + woman.getName() + "不相配");
}
}
}
}

Man

1
2
3
4
5
6
7
8
9
10
public class Man extends Person{
public Man(String name, int condition, Mediator mediator){
super(name, condition, mediator);
}

public void getPartner(Person person){
this.getMediator().setMan(this);
this.getMediator().getPartner(person);
}
}

Woman

1
2
3
4
5
6
7
8
9
10
public class Woman extends Person{
public Woman(String name, int condition, Mediator mediator){
super(name, condition, mediator);
}

public void getPartner(Person person){
this.getMediator().setWoman(this);
this.getMediator().getPartner(person);
}
}

测试类

1
2
3
4
5
6
7
8
9
10
public class MainClass{
public static void main(String[] args){
Mediator mediator = new Mediator();
Person zhangsan = new Man("张三", 7, mediator);
Person lisi = new Man("李四", 7, mediator);
Person xiaofang = new Woman("小芳", 7, mediator);
zhangsan.getPartner(lisi);
xiaofang.getPartner(lisi);
}
}




职责链模式

Chain of Responsibility(CoR)模式页脚职责链式或者职责连锁模式,是行为模式之一,
该模式构造一系列分别担当不同的职责的类的对象来共同完成一个任务,这些类的对象之间像链条一样紧密相连,所以被称作职责链模式。

职责链模式的应用场景

例1:比如客户Client要完成一个任务,这个任务包括a,b,c,d四个部分。
首先客户Client把任务交给A,A完成a部分之后,把任务交给B,B完成b部分,…,直到D完成d部分。
例2:比如政府部分的某项工作,县政府先完成自己能处理的部分,不能处理的部分交给省政府,省政府再完成自己的职责范围内的部分,
不能处理的部分交给中央政府,中央政府最后完成该项工作。
例3:软件窗口的消息传播。
例4:SERVLET容器的过滤其(Filter)框架实现。

职责链模式的基本条件

要实现Chain of Responsibility模式,需要满足该模式的基本条件:
1.对象链的组织。需要将某任务的所有职责执行对象以链的形式加以组织。
2.消息或请求的传递。将消息或请求沿着对象链传递,以让处于对象链中的对象得到处理机会。
3.处于对象链中的对象的职责分配。不同的对象完成不同的职责。
4.任务的完成。处于对象链的末尾的对象结束任务并停止消息或请求的继续传递。

职责链模式的角色和职责

Handler 处理类的抽象父类。
concreteHandler 具体的处理类。

职责链模式的优缺点

优点:
1.责任的分担。每个类只需要处理自己该处理的工作(不该处理的传递给下一个对象完成),明确各类的责任范围,符合类的最小封装原则。
2.可以根据需要自由组合工作流程。如工作流程发生变化,可以通过重新分配对象链便可适应新的工作流程。
3.类与类之间可以以松耦合的形式加以组织。
缺点:
因为处理时以链的形式在对象间传递消息,根据实现方式不同,有可能会影响处理的速度。

实例

1
2
3
4
5
6
7
8
9
public abstract class CarHandler{
protected CarHandler carHandler;
public CarHandler setNextHandler(CarHandler carHandler){
this.carHandler = carHandler;
return this.carHandler;
}

public abstract void HandlerCar();
}

CarHeadHandler

1
2
3
4
5
6
7
8
public class CarHeadHandler extends CarHandler{
public void HandlerCar(){
System.out.println("组装车头");
if(this.carHandler != null){
this.carHandler.HandlerCar();
}
}
}

CarBodyHandler

1
2
3
4
5
6
7
8
public class CarBodyHandler extends CarHandler{
public void HandlerCar(){
System.out.println("组装车身");
if(this.carHandler != null){
this.carHandler.HandlerCar();
}
}
}

CarTailHandler

1
2
3
4
5
6
7
8
public class CarTailHandler extends CarHandler{
public void HandlerCar(){
System.out.println("组装车尾");
if(this.carHandler != null){
this.carHandler.HandlerCar();
}
}
}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class MainClass{
public static void main(String[] args){
CarHandler headH = new CarHeadHandler();
CarHandler bodyH = new CarBodyHandler();
CarHandler tailH = new CarTailHandler();

//组装顺序 车头-->车身-->车尾
headH.setNextHandler(bodyH);
bodyH.setNextHandler(tailH);
headH.HandlerCar();

System.out.println("-----");

//顺序改变, 车身-->车头-->车尾
bodyH.setNextHandler(headH);
headH.setNextHandler(tailH);
bodyH.HandlerCar();

//简便操作
bodyH.setNextHandler(headH).setNextHandler(tailH);
bodyH.HandlerCar();

}
}




迭代模式

Iterator模式也叫迭代模式,是行为模式之一,它把对容器中包含的内部对象的访问委托给外部类,使用Iterator(遍历)按顺序进行遍历访问的设计模式。

不使用迭代模式的应用

在应用Iterator之前,首先应该明白Iterator模式用来解决什么问题。
或者说,如果不使用Iterator模式,会存在什么问题。
1.由容器自己实现顺序遍历。直接在容器类里直接添加顺序遍历方法。
2.让调用者自己实现遍历。直接暴露数据细节给外部。

迭代模式的角色和职责

Iterator(迭代器接口):
该接口必须定义实现迭代功能的最小定义方法集,比如提供hasNext()和next()方法。
ConcreteIterator(迭代器实现类):
迭代器接口Iterator的实现类。可以根据具体情况加以实现。
Aggregate(容器接口):
定义基本功能以及提供类似Iterator iterator()的方法。
concreteAggregate(容器实现类):
容器接口的实现类。必须实现Iterator iterator()方法。

迭代模式的优点

1.实现功能分离,简化容器接口。让容器只实现本身的基本功能,把迭代功能委让给外部类实现,符合类的设计原则。
2.隐藏容器的实现细节。
3.为容器或其子容器提供了一个统一接口,一方面方便调用;另一方面使得调用者不必关注迭代器的实现细节。
4.可以为容器或其子容器实现不同的迭代方法或多个迭代方法。

实例

Book

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
public class Book{
private String ISBN;
private String name;
private double price;

public Book(String isbn,String name,double price){
ISBN = isbn;
this.name = name;
this.price = price;
}

public String getISBN(){
return ISBN;
}

public String getName(){
return name;
}

public void setName(){
this.name = name;
}

public double getPrice(){
return price;
}

public void setPrice(double price){
this.price = price;
}

public void display(){
System.out.println("ISBN=" + ISBN = ",name = " + name + ",price" + price);
}
}

BookList

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
public class BookList{
private List<Book> bookList;
private int index;
private Iterator iterator;

public BookList(){
bookList = new ArrayList<Book>();
}

public void addBook(Book book){
bookList.add(book);
}

public void deleteBook(Book book){
int bookIndex = bookList.indexOf(book);
bookList.remove(bookIndex);
}

public Iterator Iterator(){
return new Itr();
}

private class Itr implements Iterator{
public boolean hasNext(){
if(index >= bookList.size()){
return false;
}
return true;
}

public Object next(){
return bookList.get(index++);
}

public void remove(){
}
}
}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class MainClass{
public static void main(String[] args){
BookList bookList = new BookList();

Book book1 = new Book("010203", "Java编程思想", 90);
Book book2 = new Book("010204", "Java从入门到精通", 60);

bookList.addBook(book1);
bookList.addBook(book2);

Iterator iter = bookList.Iterator();
while(iter.hasNext()){
Book book = (Book)iter.next();
book.display();
}
}
}




模板方法模式

Template Method模式也叫模版方法模式,是行为模式之一,它把具有特定步骤算法中的某些必要的处理委托给抽象方法,通过子类继承对抽象方法的不同实现改变整个算法的行为。

模版方法的应用场景

Template Method模式一般应用在具有以下条件的应用中:

  • 具有统一的操作步骤或操作过程
  • 具有不同的操作细节
  • 存在多个具有同样操作步骤的应用场景,但某些具体的操作细节却各不相同

    模版方法模式的角色和职责

    AbstractClass:抽象类的父类
    ConcreteClass:具体的实现子类
    templateMethod():模版方法
    method1()与method2():具体步骤方法

    实例

    MakeCar
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public abstract class MakeCar{
    public abstract void makeHead();

    public abstract void makeBody();

    public abstract void makeTail();

    public void make(){
    this.makeHead();
    this.makeBody();
    this.makeTail();
    }
    }

MakeJeep

1
2
3
4
5
6
7
8
9
10
11
12
13
public class MakeJeep extends MakeCar{
public void makeBody(){
System.out.println("jeep:组装车身");
}

public void makeHead(){
System.out.println("jeep:组装车头");
}

public void makeTail(){
System.out.println("jeep:组装车尾");
}
}

MakeKa

1
2
3
4
5
6
7
8
9
10
11
12
13
public class MakeKa extends MakeCar{
public void makeBody(){
System.out.println("ka:组装车身");
}

public void makeHead(){
System.out.println("ka:组装车头");
}

public void makeTail(){
System.out.println("ka:组装车尾");
}
}

MakeBus

1
2
3
4
5
6
7
8
9
10
11
12
13
public class MakeBus extends MakeCar{
public void makeBody(){
System.out.println("bus:组装车身");
}

public void makeHead(){
System.out.println("bus:组装车头");
}

public void makeTail(){
System.out.println("bus:组装车尾");
}
}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MainClass{
public static void main(String[] args){
MakeCar bus = new MakeBus();
bus.make();

System.out.println("--------------------");
MakeCar jeep = new MakeJeep();
jeep.make();

System.out.println("--------------------");
MakeCar ka = new MakeKa();
ka.make();
}
}




备忘录模式

Memento模式也叫备忘录模式,是行为模式之一,它的作用是保存对象的内部状态,并在需要的时候(undo/rollback)恢复对象以前的状态。

备忘录模式的角色和职责

Originator(原生者)
需要被保存状态以便恢复的那个对象。
Memento(备忘录)
该对象由Originator创建,主要用来保存Originator的内部状态。
Caretaker(管理者)
负责在适当的时间保存/恢复Originator对象的状态。

实例

Memento

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
public class Memento{
private String name;
private String sex;
private int age;

public Memento(String name,String sex,int age){
this.name = name;
this.sex = sex;
this.age = age;
}

public String getName(){
return name;
}

public void setName(String name){
this.name = name;
}

public String getSex(){
return sex;
}

public void setSex(String sex){
this.sex = sex;
}

public int getAge(){
return age;
}

public void setAge(int age){
this.age = age;
}
}

Person

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
50
51
52
53
public class Person{
private String name;
private String sex;
private int age;

public Person(){
}

public Person(String name,String sex,int age){
this.name = name;
this.sex = sex;
this.age = age;
}

public String getName(){
return name;
}

public void setName(String name){
this.name = name;
}

public String getSex(){
return sex;
}

public void setSex(String sex){
this.sex = sex;
}

public int getAge(){
return age;
}

public void setAge(int age){
this.age = age;
}

public void display(){
System.out.println("name:" + name + ",sex:" + sex + ",age:" + age);
}

//创建一个备份
public Memento createMemento(){
return new Memento(name, sex, age);
}

public void setMemento(Memento memento){
this.name = memento.getName();
this.sex = memento.getSex();
this.age = memento.getAge();
}
}

Caretaker

1
2
3
4
5
6
7
8
9
10
public class Caretaker{
private Memento memento;
public Mement getMemento(){
return memento;
}

public void setMemento(Memento memento){
this.memento = memento;
}
}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MainClass{
public static void main(String[] args){
Person per = new Person("xxx", "man", 22);
Caretaker caretaker = new Caretaker();
caretaker.setMemento(per.createMemento());

per.display();

per.setName("xxls");
per.setSex("woman");
per.setAge(11);

per.display();

per.setMemento(caretaker.getMemento());
per.display();
}
}




状态模式

State模式也叫状态模式,是行为设计模式的一种。
State模式允许通过改变对象的内部状态而改变对象的行为,这个行为表现得就好像改了它的类一样。

状态模式的应用场景

状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。
把状态的判断转译到表现不同状态的一系列类当中,可以把复杂的判断逻辑简化。

状态模式的角色和职责

Context:用户对象
拥有一个State类型的成员,以标识对象的当前状态。
State:接口或基类
封装与Context的特定状态相关的行为
ConcreteState:接口实现类或子类
实现了一个与Context某个状态相关的行为。

实例

State

1
2
3
public abstract class State{
public abstract void doSomething(Person person);
}

MState

1
2
3
4
5
6
7
8
9
10
public class MState extends State{
public void doSomething(Person person){
if(person.getHour() == 7){
System.out.println("吃早餐");
}else{
person.setState(new LState());
person.doSomething();
}
}
}

LState

1
2
3
4
5
6
7
8
9
10
public class LState extends State{
public void doSomething(Person person){
if(person.getHour() == 12){
System.out.println("吃午餐");
}else{
person.setState(new SState());
person.doSomething();
}
}
}

SState

1
2
3
4
5
6
7
8
9
10
public class SState extends State{
public void doSomething(Person person){
if(person.getHour() == 18){
System.out.println("吃晚饭");
}else{
person.setState(new NoState());
person.doSomething();
}
}
}

NoState

1
2
3
4
5
public class NoState extends State{
public void doSomething(Person person){
System.out.println(person.getHour() + "未定义");
}
}

Person

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
public class Person{
private int hour;
private State state;

public int getHour(){
return hour;
}

public void setHour(int hour){
this.hour = hour;
}

public Person(){
state = new MState();
}

public void doSomething(){
state.doSomething(this);
//复位
state = new MState();
}

public State getState(){
return state;
}

public void setState(State state){
this.state = state;
}
}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class MainClass{
public static void main(String[] args){
Person person = new Person();

person.setHour(7);
person.doSomething();

person.setHour(12);
person.doSomething();

person.setHour(18);
person.doSomething();

person.setHour(7);
person.doSomething();

person.setHour(18);
person.doSomething();
}
}




命令模式

Command模式也叫命令模式,是行为设计模式的一种。
Command模式通过被称为Command的类封装了对目标对象的调用行为以及调用参数。

命令模式的应用场景

在面向对象的设计,一个对象调用另一个对象,一般情况下的调用过程是:创建目标对象实例;
设置调用参数;调用目标对象的方法。
但在有些情况下有必要使用一个专门的类对这种调用过程加以封装,我们把这种专门的类称作command类。
整个调用过程比较频繁,或者存在多处这种调用。这时,使用Command类对该调用加以封装,便于功能的再利用。
调用前后需要对调用参数进行某些处理。
调用前后需要进行额外处理,比如日志、缓存、记录历史操作等。

命令模式的角色和职责

Command Command抽象类
ConcreteCommand Command的具体实现类
Receiver 需要被调用的目标对象
Invorker 通过Invorker执行Command对象

实例

Peddler

1
2
3
4
5
6
7
8
9
public class Peddler{
public void sellApple(){
System.out.println("卖苹果");
}

public void sellBanana(){
System.out.println("卖香蕉");
}
}

Command

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public abstract class Command{
private Peddler peddler;
public Command(Peddler peddler){
this.peddler = peddler;
}

public Peddler getPeddler(){
return peddler;
}

public void setPeddler(Peddler peddler){
this.peddler = peddler;
}

public abstract void sell();
}

Waiter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Waiter{
private List<Command> commands = new ArrayList<Command>();

public void setOrder(Command command){
commands.add(command);
}

public void removeOrder(Command command){
commands.remove(command);
}

public void sell(){
for(Command command : commands){
command.sell();
}
}
}

AppleCommand

1
2
3
4
5
6
7
8
9
public class AppleCommand extends Command{
public AppleCommand(Peddler peddler){
super(peddler);
}

public void sell(){
this.getPeddler().sellApple();
}
}

BananaCommand

1
2
3
4
5
6
7
8
9
public class BananaCommand extends Command{
public BananaCommand(Peddler peddler){
super(peddler);
}

public void sell(){
this.getPeddler().sellBanana();
}
}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MainClass{
public static void main(String[] args){
Peddler peddler = new Peddler();

Command appleCommand = new AppleCommand(peddler);
Command bananaCommand = new BananaCommand(peddler);

Waiter waiter = new Waiter();

waiter.setOrder(appleCommand);
waiter.setOrder(bananaCommand);

waiter.removeOrder(appleCommand);
waiter.sell();
}
}




访问者模式

Visitor模式也叫访问者模式,是行为模式之一,它分离对象的数据和行为。
使用Visitor模式,可以不修改已有类的情况下,增加新的操作。

访问者模式的应用示例

比如有一个公园,有一到多个不同的组成部分;
该公园存在多个访问者:

  • 清洁工A负责打扫公园A部分
  • 清洁工B负责打扫公园B部分
  • 公园管理者负责检点各项事务是否完成
  • 上级领导可以视察公园
    也就是说,对于同一个公园,不同的访问者有不同的行为操作,而且访问者的种类也可能需要根据时间的推移而变化(行为的扩展性)。

    访问者模式的角色和职责

    1.访问这角色(visitor)
    为该对象结构中on个具体元素角色声明一个访问操作接口。
    该操作接口的名字和参数标识了发送访问请求给具体访问者的具体元素角色。
    这样访问者就可以通过该元素角色的特定接口直接访问它。
    2.具体访问者角色(concrete visitor):
    实现每个由访问者角色(visitor)声明的操作
    3.元素角色(Element)
    定义一个Accept操作,它以一个访问者为参数
    4.具体元素角色(Concrete Element)
    实现由元素角色提供的Accept操作
    5.对象结构角色(Object Structure)
    这是使用访问者模式必备的角色。
    它要具备以下特征:能枚举它的元素;可以提供一个高层的接口以允许该访问者访问它的元素;
    可以是一个复合(组合模式)或是一个集合,如一个列表或一个无序结合。

    实例

    ParkElement
    1
    2
    3
    public interface ParkElement{
    public void accept(Visitor visitor);
    }

Visitor

1
2
3
4
5
public interface Visitor{
public void visit(Park park);
public void visit(ParkA parkA);
public void visit(ParkB parkB);
}

Park

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
public class Park implements ParkElement{
private String name;
private ParkA parkA;
private ParkB parkB;

public Park(){
this.parkA = new ParkA();
this.parkB = new ParkB();
parkA.setName("A");
parkB.setName("B");
}

public void accept(Visitor visitor){
visitor.visit(this);
parkA.accept(visitor);
parkB.accept(visitor);
}

public String getName(){
return name;
}

public void setName(String name){
this.name = name;
}
}

ParkA

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ParkA implements ParkElement{
private String name;
public String getName(){
return name;
}

public void setName(String name){
this.name = name;
}

public void accept(Visitor visitor){
visitor.visit(this);
}
}

ParkB

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ParkB implements ParkElement{
private String name;
public String getName(){
return name;
}

public void setName(String name){
this.name = name;
}

public void accept(Visitor visitor){
visitor.visit(this);
}
}

VisitorA

1
2
3
4
5
6
7
8
9
10
11
12
public class VisitorA implements Visitor{
public void visit(Park park){

}

public void visit(ParkA parkA){
System.out.println("清洁工A:完成公园" + parkA.getName() +"的卫生");
}

public void visit(ParkB parkB){
}
}

VisitorB

1
2
3
4
5
6
7
8
9
10
11
12
public class VisitorB implements Visitor{
public void visit(Park park){
}

public void visit(ParkA parkA){

}

public void visit(ParkB parkB){
System.out.println("清洁工B:完成公园" + parkB.getName() + "的卫生");
}
}

VisitorManager

1
2
3
4
5
6
7
8
9
10
11
public class VisitorManager implements Visitor{
public void visit(Park park){
System.out.println("管理员:负责" + park.getName() + "卫生检查");
}
public void visit(ParkA parkA){
System.out.println("管理员:负责公园" + parkA.getName() + "部分卫生检测");
}
public void visit(ParkB parkB){
System.out.println("管理员:负责公园" + parkB.getName() + "部分卫生检查");
}
}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MainClass{
public static void main(String[] args){
Park park = new Park();
park.setName("大商所公园");
VisitorA visitorA = new VisitorA();
park.accept(visitorA);

VisitorB visitorB = new VisitorB();
park.accept(visitorB);

VisitorManager visitorManager = new VisitorManager();
park.accept(visitorManager);
}
}