Java Design Pattern

作者:郭霖

单例:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
组合:将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

策略:它定义了算法家庭,分别封装起来。让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。

模板方法: 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

适配器:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

在sLogUtil还没被初始化的时候才会进入到第3行,然后加上同步锁。等sLogUtil一但初始化完成了,就再也走不到第3行了,这样执行getInstance方法也不会再受到同步锁的影响,效率上会有一定的提升。

1
2
3
4
5
6
7
8
9
10
public static LogUtil getInstance() {
if (sLogUtil == null) {
synchronized (LogUtil.class) {
if (sLogUtil == null) {
sLogUtil = new LogUtil();
}
}
}
return sLogUtil;
}

扩展

A. 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。
B. 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。
C. 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。

在AllStatistics的构造函数中将小说类书籍和科技类书籍作为子分类添加到了statistics列表当中,然后使用同样的方法在getBrowseCount和getSalesCount方法中统计出所有书籍的浏览量和销售量。

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
public interface Statistics {
int getBrowseCount();
int getSalesCount();
}
public class AllStatistics implements Statistics {
private List<Statistics> statistics = new ArrayList<Statistics>();
public AllStatistics() {
statistics.add(new NovelStatistics());
statistics.add(new TechnicalStatistics());
}
@Override
public int getBrowseCount() {
int browseCount = 0;
for (Statistics s : statistics) {
browseCount += s.getBrowseCount();
}
return browseCount;
}
@Override
public int getSalesCount() {
int saleCount = 0;
for (Statistics s : statistics) {
saleCount += s.getBrowseCount();
}
return saleCount;
}
}

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 interface Strategy {
String getSQL(String[] usernames);
}
public class Strategy1 implements Strategy {
@Override
public String getSQL(String[] usernames) {
StringBuilder sql = new StringBuilder("select * from user_info where ");
for (String user : usernames) {
sql.append("username = '");
sql.append(user);
sql.append("' or ");
}
sql.delete(sql.length() - " or ".length(), sql.length());
return sql.toString();
}
}
public class Strategy2 implements Strategy {
@Override
public String getSQL(String[] usernames) {
StringBuilder sql = new StringBuilder("select * from user_info where ");
boolean needOr = false;
for (String user : usernames) {
if (needOr) {
sql.append(" or ");
}
sql.append("username = '");
sql.append(user);
sql.append("'");
needOr = true;
}
return sql.toString();
}
}
public class QueryUtil {
public void findUserInfo(String[] usernames, Strategy strategy) throws Exception {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root",
"123456");
Statement stat = conn.createStatement();
String sql = strategy.getSQL(usernames);
System.out.println(sql);
ResultSet resultSet = stat.executeQuery(sql);
while (resultSet.next()) {
// 处理从数据库读出来的数据
}
// 后面应将读到的数据组装成对象返回,这里略去。
}
}
public class Test {
public static void main(String[] args) throws Exception {
QueryUtil query = new QueryUtil();
query.findUserInfo(new String[] { "Tom", "Jim", "Anna" }, new Strategy1());
query.findUserInfo(new String[] { "Jac", "Joe", "Rose" }, new Strategy2());
}
}
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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
public abstract class Formatter {
public String formatBook(Book book, int format) {
beforeFormat();
String result = formating(book);
afterFormat();
return result;
}
protected void beforeFormat() {
System.out.println("format begins");
}
protected abstract String formating(Book book);
protected void afterFormat() {
System.out.println("format finished");
}
}
public class XMLFormatter extends Formatter {
@Override
protected String formating(Book book) {
String result = "";
result += "<book_name>" + book.getBookName() + "</book_name>\n";
result += "<pages>" + book.getPages() + "</pages>\n";
result += "<price>" + book.getPrice() + "</price>\n";
result += "<author>" + book.getAuthor() + "</author>\n";
result += "<isbn>" + book.getIsbn() + "</isbn>\n";
return result;
}
}
public class JSONFormatter extends Formatter {
@Override
protected String formating(Book book) {
String result = "";
result += "{\n";
result += "\"book_name\" : \"" + book.getBookName() + "\",\n";
result += "\"pages\" : \"" + book.getPages() + "\",\n";
result += "\"price\" : \"" + book.getPrice() + "\",\n";
result += "\"author\" : \"" + book.getAuthor() + "\",\n";
result += "\"isbn\" : \"" + book.getIsbn() + "\",\n";
result += "}";
return result;
}
}
public class YAMLFormatter extends Formatter {
@Override
protected String formating(Book book) {
String result = "";
result += "book_name: " + book.getBookName() + "\n";
result += "pages: " + book.getPages() + "\n";
result += "price: " + book.getPrice() + "\n";
result += "author: " + book.getAuthor() + "\n";
result += "isbn: " + book.getIsbn() + "\n";
return result;
}
}
public class Test {
public static void main(String[] args) throws Exception {
Book book = new Book();
book.setBookName("Thinking in Java");
book.setPages(880);
book.setPrice(68);
book.setAuthor("Bruce Eckel");
book.setIsbn("9787111213826");
XMLFormatter xmlFormatter = new XMLFormatter();
String result = xmlFormatter.formatBook(book);
System.out.println(result);
JSONFormatter jsonFormatter = new JSONFormatter();
result = jsonFormatter.formatBook(book);
System.out.println(result);
YAMLFormatter yamlFormatter = new YAMLFormatter();
String result = yamlFormatter.formatBook(book);
System.out.println(result);
}
}
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
public interface PlayerCount {
String getServerName();
int getPlayerCount();
}
public class ServerOne implements PlayerCount {
@Override
public String getServerName() {
return "一服";
}
@Override
public int getPlayerCount() {
return Utility.getOnlinePlayerCount(1);
}
}
public class ServerTwo implements PlayerCount {
@Override
public String getServerName() {
return "二服";
}
@Override
public int getPlayerCount() {
return Utility.getOnlinePlayerCount(2);
}
}
public class ServerThree implements PlayerCount {
@Override
public String getServerName() {
return "三服";
}
@Override
public int getPlayerCount() {
return Utility.getOnlinePlayerCount(3);
}
}
public class XMLBuilder {
public static String buildXML(PlayerCount player) {
StringBuilder builder = new StringBuilder();
builder.append("<root>");
builder.append("<server>").append(player.getServerName()).append("</server>");
builder.append("<player_count").append(player.getPlayerCount()).append("</player_count>");
builder.append("</root>");
return builder.toString();
}
}
XMLBuilder.buildXML(new ServerOne());
XMLBuilder.buildXML(new ServerTwo());
XMLBuilder.buildXML(new ServerThree());

查询在线玩家数量的功能早就有了,使用的是ServerFirst这个类。当时写Utility.getOnlinePlayerCount()这个方法主要是为了针对新开的二服和三服,就没把一服的查询功能再重复做一遍。
Utility和ServerFirst这两个类都已经被打到Jar包里了,没法修改啊

XMLBuilder中使用PlayerCount这个接口来拼装XML,而ServerFirst并没有实现PlayerCount这个接口,这个时候就需要一个适配器类来为XMLBuilder和ServerFirst之间搭起一座桥梁,毫无疑问,ServerOne就将充当适配器类的角色。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ServerOne implements PlayerCount {
private ServerFirst mServerFirst;
public ServerOne() {
mServerFirst = new ServerFirst();
}
@Override
public String getServerName() {
return "一服";
}
@Override
public int getPlayerCount() {
return mServerFirst.getOnlinePlayerCount();
}
}

需要值得注意的一点是,适配器模式不并是那种会让架构变得更合理的模式,更多的时候它只是充当救火队员的角色,帮助解决由于前期架构设计不合理导致的接口不匹配的问题。

参考阅读

Java设计模式透析之 —— 单例(Singleton)
Java设计模式透析之 —— 组合(Composite)
Java设计模式透析之 —— 策略(Strategy)
Java设计模式透析之 —— 模板(Template)
Java设计模式透析之 —— 适配器(Adapter)
Java中Synchronized的用法