From 8b6a74c72b828b6743522d8d45b0c2c1864fd4ee Mon Sep 17 00:00:00 2001 From: CyC2018 Date: Sun, 25 Oct 2026 19:05:12 +0800 Subject: [PATCH 1/3] Update SQL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SQL 语法.md 包含相同的内容,统一由它维护,本文件改为指向它。 --- notes/SQL.md | 782 +-------------------------------------------------- 1 file changed, 1 insertion(+), 781 deletions(-) diff --git a/notes/SQL.md b/notes/SQL.md index 8645b6d7..decccd9f 100644 --- a/notes/SQL.md +++ b/notes/SQL.md @@ -1,781 +1 @@ - -* [一、基础](#一基础) -* [二、创建表](#二创建表) -* [三、修改表](#三修改表) -* [四、插入](#四插入) -* [五、更新](#五更新) -* [六、删除](#六删除) -* [七、查询](#七查询) - * [DISTINCT](#distinct) - * [LIMIT](#limit) -* [八、排序](#八排序) -* [九、过滤](#九过滤) -* [十、通配符](#十通配符) -* [十一、计算字段](#十一计算字段) -* [十二、函数](#十二函数) - * [汇总](#汇总) - * [文本处理](#文本处理) - * [日期和时间处理](#日期和时间处理) - * [数值处理](#数值处理) -* [十三、分组](#十三分组) -* [十四、子查询](#十四子查询) -* [十五、连接](#十五连接) - * [内连接](#内连接) - * [自连接](#自连接) - * [自然连接](#自然连接) - * [外连接](#外连接) -* [十六、组合查询](#十六组合查询) -* [十七、视图](#十七视图) -* [十八、存储过程](#十八存储过程) -* [十九、游标](#十九游标) -* [二十、触发器](#二十触发器) -* [二十一、事务管理](#二十一事务管理) -* [二十二、字符集](#二十二字符集) -* [二十三、权限管理](#二十三权限管理) -* [参考资料](#参考资料) - - - -# 一、基础 - -模式定义了数据如何存储、存储什么样的数据以及数据如何分解等信息,数据库和表都有模式。 - -主键的值不允许修改,也不允许复用(不能将已经删除的主键值赋给新数据行的主键)。 - -SQL(Structured Query Language),标准 SQL 由 ANSI 标准委员会管理,从而称为 ANSI SQL。各个 DBMS 都有自己的实现,如 PL/SQL、Transact-SQL 等。 - -SQL 语句不区分大小写,但是数据库表名、列名和值是否区分依赖于具体的 DBMS 以及配置。 - -SQL 支持以下三种注释: - -```sql -# 注释 -SELECT * -FROM mytable; -- 注释 -/* 注释1 - 注释2 */ -``` - -数据库创建与使用: - -```sql -CREATE DATABASE test; -USE test; -``` - -# 二、创建表 - -```sql -CREATE TABLE mytable ( - # int 类型,不为空,自增 - id INT NOT NULL AUTO_INCREMENT, - # int 类型,不可为空,默认值为 1,不为空 - col1 INT NOT NULL DEFAULT 1, - # 变长字符串类型,最长为 45 个字符,可以为空 - col2 VARCHAR(45) NULL, - # 日期类型,可为空 - col3 DATE NULL, - # 设置主键为 id - PRIMARY KEY (`id`)); -``` - -# 三、修改表 - -添加列 - -```sql -ALTER TABLE mytable -ADD col CHAR(20); -``` - -删除列 - -```sql -ALTER TABLE mytable -DROP COLUMN col; -``` - -删除表 - -```sql -DROP TABLE mytable; -``` - -# 四、插入 - -普通插入 - -```sql -INSERT INTO mytable(col1, col2) -VALUES(val1, val2); -``` - -插入检索出来的数据 - -```sql -INSERT INTO mytable1(col1, col2) -SELECT col1, col2 -FROM mytable2; -``` - -将一个表的内容插入到一个新表 - -```sql -CREATE TABLE newtable AS -SELECT * FROM mytable; -``` - -# 五、更新 - -```sql -UPDATE mytable -SET col = val -WHERE id = 1; -``` - -# 六、删除 - -```sql -DELETE FROM mytable -WHERE id = 1; -``` - -**TRUNCATE TABLE** 可以清空表,也就是删除所有行。 - -```sql -TRUNCATE TABLE mytable; -``` - -使用更新和删除操作时一定要用 WHERE 子句,不然会把整张表的数据都破坏。可以先用 SELECT 语句进行测试,防止错误删除。 - -# 七、查询 - -## DISTINCT - -相同值只会出现一次。它作用于所有列,也就是说所有列的值都相同才算相同。 - -```sql -SELECT DISTINCT col1, col2 -FROM mytable; -``` - -## LIMIT - -限制返回的行数。可以有两个参数,第一个参数为起始行,从 0 开始;第二个参数为返回的总行数。 - -返回前 5 行: - -```sql -SELECT * -FROM mytable -LIMIT 5; -``` - -```sql -SELECT * -FROM mytable -LIMIT 0, 5; -``` - -返回第 3 \~ 5 行: - -```sql -SELECT * -FROM mytable -LIMIT 2, 3; -``` - -# 八、排序 - -- **ASC** :升序(默认) -- **DESC** :降序 - -可以按多个列进行排序,并且为每个列指定不同的排序方式: - -```sql -SELECT * -FROM mytable -ORDER BY col1 DESC, col2 ASC; -``` - -# 九、过滤 - -不进行过滤的数据非常大,导致通过网络传输了多余的数据,从而浪费了网络带宽。因此尽量使用 SQL 语句来过滤不必要的数据,而不是传输所有的数据到客户端中然后由客户端进行过滤。 - -```sql -SELECT * -FROM mytable -WHERE col IS NULL; -``` - -下表显示了 WHERE 子句可用的操作符 - -| 操作符 | 说明 | -| :---: | :---: | -| = | 等于 | -| < | 小于 | -| > | 大于 | -| <> != | 不等于 | -| <= !> | 小于等于 | -| >= !< | 大于等于 | -| BETWEEN | 在两个值之间 | -| IS NULL | 为 NULL 值 | - -应该注意到,NULL 与 0、空字符串都不同。 - -**AND 和 OR** 用于连接多个过滤条件。优先处理 AND,当一个过滤表达式涉及到多个 AND 和 OR 时,可以使用 () 来决定优先级,使得优先级关系更清晰。 - -**IN** 操作符用于匹配一组值,其后也可以接一个 SELECT 子句,从而匹配子查询得到的一组值。 - -**NOT** 操作符用于否定一个条件。 - -# 十、通配符 - -通配符也是用在过滤语句中,但它只能用于文本字段。 - -- **%** 匹配 \>=0 个任意字符; - -- **\_** 匹配 ==1 个任意字符; - -- **[ ]** 可以匹配集合内的字符,例如 [ab] 将匹配字符 a 或者 b。用脱字符 ^ 可以对其进行否定,也就是不匹配集合内的字符。 - -使用 Like 来进行通配符匹配。 - -```sql -SELECT * -FROM mytable -WHERE col LIKE '[^AB]%'; -- 不以 A 和 B 开头的任意文本 -``` - -不要滥用通配符,通配符位于开头处匹配会非常慢。 - -# 十一、计算字段 - -在数据库服务器上完成数据的转换和格式化的工作往往比客户端上快得多,并且转换和格式化后的数据量更少的话可以减少网络通信量。 - -计算字段通常需要使用 **AS** 来取别名,否则输出的时候字段名为计算表达式。 - -```sql -SELECT col1 * col2 AS alias -FROM mytable; -``` - -**CONCAT()** 用于连接两个字段。许多数据库会使用空格把一个值填充为列宽,因此连接的结果会出现一些不必要的空格,使用 **TRIM()** 可以去除首尾空格。 - -```sql -SELECT CONCAT(TRIM(col1), '(', TRIM(col2), ')') AS concat_col -FROM mytable; -``` - -# 十二、函数 - -各个 DBMS 的函数都是不相同的,因此不可移植,以下主要是 MySQL 的函数。 - -## 汇总 - -|函 数 |说 明| -| :---: | :---: | -| AVG() | 返回某列的平均值 | -| COUNT() | 返回某列的行数 | -| MAX() | 返回某列的最大值 | -| MIN() | 返回某列的最小值 | -| SUM() |返回某列值之和 | - -AVG() 会忽略 NULL 行。 - -使用 DISTINCT 可以汇总不同的值。 - -```sql -SELECT AVG(DISTINCT col1) AS avg_col -FROM mytable; -``` - -## 文本处理 - -| 函数 | 说明 | -| :---: | :---: | -| LEFT() | 左边的字符 | -| RIGHT() | 右边的字符 | -| LOWER() | 转换为小写字符 | -| UPPER() | 转换为大写字符 | -| LTRIM() | 去除左边的空格 | -| RTRIM() | 去除右边的空格 | -| LENGTH() | 长度 | -| SOUNDEX() | 转换为语音值 | - -其中, **SOUNDEX()** 可以将一个字符串转换为描述其语音表示的字母数字模式。 - -```sql -SELECT * -FROM mytable -WHERE SOUNDEX(col1) = SOUNDEX('apple') -``` - -## 日期和时间处理 - - -- 日期格式:YYYY-MM-DD -- 时间格式:HH:MM:SS - -|函 数 | 说 明| -| :---: | :---: | -| ADDDATE() | 增加一个日期(天、周等)| -| ADDTIME() | 增加一个时间(时、分等)| -| CURDATE() | 返回当前日期 | -| CURTIME() | 返回当前时间 | -| DATE() |返回日期时间的日期部分| -| DATEDIFF() |计算两个日期之差| -| DATE_ADD() |高度灵活的日期运算函数| -| DATE_FORMAT() |返回一个格式化的日期或时间串| -| DAY()| 返回一个日期的天数部分| -| DAYOFWEEK() |对于一个日期,返回对应的星期几| -| HOUR() |返回一个时间的小时部分| -| MINUTE() |返回一个时间的分钟部分| -| MONTH() |返回一个日期的月份部分| -| NOW() |返回当前日期和时间| -| SECOND() |返回一个时间的秒部分| -| TIME() |返回一个日期时间的时间部分| -| YEAR() |返回一个日期的年份部分| - -```sql -mysql> SELECT NOW(); -``` - -```text -2018-4-14 20:25:11 -``` - -## 数值处理 - -| 函数 | 说明 | -| :---: | :---: | -| SIN() | 正弦 | -| COS() | 余弦 | -| TAN() | 正切 | -| ABS() | 绝对值 | -| SQRT() | 平方根 | -| MOD() | 余数 | -| EXP() | 指数 | -| PI() | 圆周率 | -| RAND() | 随机数 | - -# 十三、分组 - -把具有相同的数据值的行放在同一组中。 - -可以对同一分组数据使用汇总函数进行处理,例如求分组数据的平均值等。 - -MySQL 5.7 及之前的版本在一定条件下会自动按分组字段排序,MySQL 8.0 不再隐式排序,需要排序时应当使用 ORDER BY。 - -```sql -SELECT col, COUNT(*) AS num -FROM mytable -GROUP BY col; -``` - -ORDER BY 也可以按汇总字段来进行排序。 - -```sql -SELECT col, COUNT(*) AS num -FROM mytable -GROUP BY col -ORDER BY num; -``` - -WHERE 过滤行,HAVING 过滤分组,行过滤应当先于分组过滤。 - -```sql -SELECT col, COUNT(*) AS num -FROM mytable -WHERE col > 2 -GROUP BY col -HAVING num >= 2; -``` - -分组规定: - -- GROUP BY 子句出现在 WHERE 子句之后,ORDER BY 子句之前; -- 除了汇总字段外,SELECT 语句中的每一字段都必须在 GROUP BY 子句中给出; -- NULL 的行会单独分为一组; -- 大多数 SQL 实现不支持 GROUP BY 列具有可变长度的数据类型。 - -# 十四、子查询 - -子查询中只能返回一个字段的数据。 - -可以将子查询的结果作为 WHERE 语句的过滤条件: - -```sql -SELECT * -FROM mytable1 -WHERE col1 IN (SELECT col2 - FROM mytable2); -``` - -下面的语句可以检索出客户的订单数量,子查询语句会对第一个查询检索出的每个客户执行一次: - -```sql -SELECT cust_name, (SELECT COUNT(*) - FROM Orders - WHERE Orders.cust_id = Customers.cust_id) - AS orders_num -FROM Customers -ORDER BY cust_name; -``` - -# 十五、连接 - -连接用于连接多个表,使用 JOIN 关键字,并且条件语句使用 ON 而不是 WHERE。 - -连接可以替换子查询,并且比子查询的效率一般会更快。 - -可以用 AS 给列名、计算字段和表名取别名,给表名取别名是为了简化 SQL 语句以及连接相同表。 - -## 内连接 - -内连接又称等值连接,使用 INNER JOIN 关键字。 - -```sql -SELECT A.value, B.value -FROM tablea AS A INNER JOIN tableb AS B -ON A.key = B.key; -``` - -可以不明确使用 INNER JOIN,而使用普通查询并在 WHERE 中将两个表中要连接的列用等值方法连接起来。 - -```sql -SELECT A.value, B.value -FROM tablea AS A, tableb AS B -WHERE A.key = B.key; -``` - -## 自连接 - -自连接可以看成内连接的一种,只是连接的表是自身而已。 - -一张员工表,包含员工姓名和员工所属部门,要找出与 Jim 处在同一部门的所有员工姓名。 - -子查询版本 - -```sql -SELECT name -FROM employee -WHERE department = ( - SELECT department - FROM employee - WHERE name = "Jim"); -``` - -自连接版本 - -```sql -SELECT e1.name -FROM employee AS e1 INNER JOIN employee AS e2 -ON e1.department = e2.department - AND e2.name = "Jim"; -``` - -## 自然连接 - -自然连接是把同名列通过等值测试连接起来的,同名列可以有多个。 - -内连接和自然连接的区别:内连接提供连接的列,而自然连接自动连接所有同名列。 - -```sql -SELECT A.value, B.value -FROM tablea AS A NATURAL JOIN tableb AS B; -``` - -## 外连接 - -外连接保留了没有关联的那些行。分为左外连接,右外连接以及全外连接,左外连接就是保留左表没有关联的行。 - -检索所有顾客的订单信息,包括还没有订单信息的顾客。 - -```sql -SELECT Customers.cust_id, Customer.cust_name, Orders.order_id -FROM Customers LEFT OUTER JOIN Orders -ON Customers.cust_id = Orders.cust_id; -``` - -customers 表: - -| cust_id | cust_name | -| :---: | :---: | -| 1 | a | -| 2 | b | -| 3 | c | - -orders 表: - -| order_id | cust_id | -| :---: | :---: | -|1 | 1 | -|2 | 1 | -|3 | 3 | -|4 | 3 | - -结果: - -| cust_id | cust_name | order_id | -| :---: | :---: | :---: | -| 1 | a | 1 | -| 1 | a | 2 | -| 3 | c | 3 | -| 3 | c | 4 | -| 2 | b | Null | - -# 十六、组合查询 - -使用 **UNION** 来组合两个查询,如果第一个查询返回 M 行,第二个查询返回 N 行,那么组合查询的结果一般为 M+N 行。 - -每个查询必须包含相同的列、表达式和聚集函数。 - -默认会去除相同行,如果需要保留相同行,使用 UNION ALL。 - -只能包含一个 ORDER BY 子句,并且必须位于语句的最后。 - -```sql -SELECT col -FROM mytable -WHERE col = 1 -UNION -SELECT col -FROM mytable -WHERE col =2; -``` - -# 十七、视图 - -视图是虚拟的表,本身不包含数据,也就不能对其进行索引操作。 - -对视图的操作和对普通表的操作一样。 - -视图具有如下好处: - -- 简化复杂的 SQL 操作,比如复杂的连接; -- 只使用实际表的一部分数据; -- 通过只给用户访问视图的权限,保证数据的安全性; -- 更改数据格式和表示。 - -```sql -CREATE VIEW myview AS -SELECT Concat(col1, col2) AS concat_col, col3*col4 AS compute_col -FROM mytable -WHERE col5 = val; -``` - -# 十八、存储过程 - -存储过程可以看成是对一系列 SQL 操作的批处理。 - -使用存储过程的好处: - -- 代码封装,保证了一定的安全性; -- 代码复用; -- 由于是预先编译,因此具有很高的性能。 - -命令行中创建存储过程需要自定义分隔符,因为命令行是以 ; 为结束符,而存储过程中也包含了分号,因此会错误把这部分分号当成是结束符,造成语法错误。 - -包含 in、out 和 inout 三种参数。 - -给变量赋值都需要用 select into 语句。 - -每次只能给一个变量赋值,不支持集合的操作。 - -```sql -delimiter // - -create procedure myprocedure( out ret int ) - begin - declare y int; - select sum(col1) - from mytable - into y; - select y*y into ret; - end // - -delimiter ; -``` - -```sql -call myprocedure(@ret); -select @ret; -``` - -# 十九、游标 - -在存储过程中使用游标可以对一个结果集进行移动遍历。 - -游标主要用于交互式应用,其中用户需要对数据集中的任意行进行浏览和修改。 - -使用游标的四个步骤: - -1. 声明游标,这个过程没有实际检索出数据; -2. 打开游标; -3. 取出数据; -4. 关闭游标; - -```sql -delimiter // -create procedure myprocedure(out ret int) - begin - declare done boolean default 0; - - declare mycursor cursor for - select col1 from mytable; - # 定义了一个 continue handler,当 sqlstate '02000' 这个条件出现时,会执行 set done = 1 - declare continue handler for sqlstate '02000' set done = 1; - - open mycursor; - - repeat - fetch mycursor into ret; - select ret; - until done end repeat; - - close mycursor; - end // - delimiter ; -``` - -# 二十、触发器 - -触发器会在某个表执行以下语句时而自动执行:DELETE、INSERT、UPDATE。 - -触发器必须指定在语句执行之前还是之后自动执行,之前执行使用 BEFORE 关键字,之后执行使用 AFTER 关键字。BEFORE 用于数据验证和净化,AFTER 用于审计跟踪,将修改记录到另外一张表中。 - -INSERT 触发器包含一个名为 NEW 的虚拟表。 - -```sql -CREATE TRIGGER mytrigger AFTER INSERT ON mytable -FOR EACH ROW SELECT NEW.col into @result; - -SELECT @result; -- 获取结果 -``` - -DELETE 触发器包含一个名为 OLD 的虚拟表,并且是只读的。 - -UPDATE 触发器包含一个名为 NEW 和一个名为 OLD 的虚拟表,其中 NEW 是可以被修改的,而 OLD 是只读的。 - -MySQL 的触发器可以使用 CALL 语句调用存储过程,但是该存储过程不能向客户端返回数据,也不能使用动态 SQL。 - -# 二十一、事务管理 - -基本术语: - -- 事务(transaction)指一组 SQL 语句; -- 回退(rollback)指撤销指定 SQL 语句的过程; -- 提交(commit)指将未存储的 SQL 语句结果写入数据库表; -- 保留点(savepoint)指事务处理中设置的临时占位符(placeholder),你可以对它发布回退(与回退整个事务处理不同)。 - -不能回退 SELECT 语句,回退 SELECT 语句也没意义;也不能回退 CREATE 和 DROP 语句。 - -MySQL 的事务提交默认是隐式提交,每执行一条语句就把这条语句当成一个事务然后进行提交。当出现 START TRANSACTION 语句时,会关闭隐式提交;当 COMMIT 或 ROLLBACK 语句执行后,事务会自动关闭,重新恢复隐式提交。 - -设置 autocommit 为 0 可以取消自动提交;autocommit 标记是针对每个连接而不是针对服务器的。 - -如果没有设置保留点,ROLLBACK 会回退到 START TRANSACTION 语句处;如果设置了保留点,并且在 ROLLBACK 中指定该保留点,则会回退到该保留点。 - -```sql -START TRANSACTION -// ... -SAVEPOINT delete1 -// ... -ROLLBACK TO delete1 -// ... -COMMIT -``` - -# 二十二、字符集 - -基本术语: - -- 字符集为字母和符号的集合; -- 编码为某个字符集成员的内部表示; -- 校对字符指定如何比较,主要用于排序和分组。 - -除了给表指定字符集和校对外,也可以给列指定: - -```sql -CREATE TABLE mytable -(col VARCHAR(10) CHARACTER SET latin COLLATE latin1_general_ci ) -DEFAULT CHARACTER SET hebrew COLLATE hebrew_general_ci; -``` - -可以在排序、分组时指定校对: - -```sql -SELECT * -FROM mytable -ORDER BY col COLLATE latin1_general_ci; -``` - -# 二十三、权限管理 - -MySQL 的账户信息保存在 mysql 这个数据库中。 - -```sql -USE mysql; -SELECT user FROM user; -``` - -**创建账户** - -新创建的账户没有任何权限。 - -```sql -CREATE USER myuser IDENTIFIED BY 'mypassword'; -``` - -**修改账户名** - -```sql -RENAME USER myuser TO newuser; -``` - -**删除账户** - -```sql -DROP USER myuser; -``` - -**查看权限** - -```sql -SHOW GRANTS FOR myuser; -``` - -**授予权限** - -账户用 username@host 的形式定义,username@% 使用的是默认主机名。 - -```sql -GRANT SELECT, INSERT ON mydatabase.* TO myuser; -``` - -**删除权限** - -GRANT 和 REVOKE 可在几个层次上控制访问权限: - -- 整个服务器,使用 GRANT ALL 和 REVOKE ALL; -- 整个数据库,使用 ON database.\*; -- 特定的表,使用 ON database.table; -- 特定的列; -- 特定的存储过程。 - -```sql -REVOKE SELECT, INSERT ON mydatabase.* FROM myuser; -``` - -**更改密码** - -MySQL 8.0 移除了 Password() 函数,直接指定新密码即可。 - -```sql -SET PASSWORD FOR myuser = 'new_password'; -``` - -# 参考资料 - -- Ben Forta. SQL 必知必会 [M]. 人民邮电出版社, 2013. +本文与 [SQL 语法](SQL%20语法.md) 内容相同,已合并到该文件。 From 1689d9732e75f862296001b57a1807e97038f61b Mon Sep 17 00:00:00 2001 From: CyC2018 Date: Sun, 25 Oct 2026 19:05:12 +0800 Subject: [PATCH 2/3] =?UTF-8?q?Update=20=E8=AE=BE=E8=AE=A1=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=20-=20=E7=9B=AE=E5=BD=951.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 设计模式目录统一由设计模式 - 目录.md 维护,本文件改为指向它。 --- notes/设计模式 - 目录1.md | 47 +-------------------------------------- 1 file changed, 1 insertion(+), 46 deletions(-) diff --git a/notes/设计模式 - 目录1.md b/notes/设计模式 - 目录1.md index d61a1d98..0df38839 100644 --- a/notes/设计模式 - 目录1.md +++ b/notes/设计模式 - 目录1.md @@ -1,46 +1 @@ -# 一、前言 - -设计模式是解决问题的方案,学习现有的设计模式可以做到经验复用。拥有设计模式词汇,在沟通时就能用更少的词汇来讨论,并且不需要了解底层细节。 - -# 二、创建型 - -- [单例.md](notes/设计模式%20%20-%20单例.md) -- [简单工厂.md](notes/设计模式%20-%20简单工厂.md) -- [工厂方法.md](notes/设计模式%20-%20工厂方法.md) -- [抽象工厂.md](notes/设计模式%20-%20抽象工厂.md) -- [生成器.md](notes/设计模式%20-%20生成器.md) -- [原型模式.md](notes/设计模式%20-%20原型模式.md) - -# 三、行为型 - -- [责任链.md](notes/设计模式%20-%20责任链.md) -- [命令.md](notes/设计模式%20-%20命令.md) -- [解释器.md](notes/设计模式%20-%20解释器.md) -- [迭代器.md](notes/设计模式%20-%20迭代器.md) -- [中介者.md](notes/设计模式%20-%20中介者.md) -- [备忘录.md](notes/设计模式%20-%20备忘录.md) -- [观察者.md](notes/设计模式%20-%20观察者.md) -- [状态.md](notes/设计模式%20-%20状态.md) -- [策略.md](notes/设计模式%20-%20策略.md) -- [模板方法.md](notes/设计模式%20-%20模板方法.md) -- [访问者.md](notes/设计模式%20-%20访问者.md) -- [空对象.md](notes/设计模式%20-%20空对象.md) - -# 四、结构型 - -- [适配器.md](notes/设计模式%20-%20适配器.md) -- [桥接.md](notes/设计模式%20-%20桥接.md) -- [组合.md](notes/设计模式%20-%20组合.md) -- [装饰.md](notes/设计模式%20-%20装饰.md) -- [外观.md](notes/设计模式%20-%20外观.md) -- [享元.md](notes/设计模式%20-%20享元.md) -- [代理.md](notes/设计模式%20-%20代理.md) - -# 参考资料 - -- 弗里曼. Head First 设计模式 [M]. 中国电力出版社, 2007. -- Gamma E. 设计模式: 可复用面向对象软件的基础 [M]. 机械工业出版社, 2007. -- Bloch J. Effective java[M]. Addison-Wesley Professional, 2017. -- [Design Patterns](http://www.oodesign.com/) -- [Design patterns implemented in Java](http://java-design-patterns.com/) -- [The breakdown of design patterns in JDK](http://www.programering.com/a/MTNxAzMwATY.html) +本文是 [设计模式目录](设计模式%20-%20目录.md) 的旧版本,已合并到该文件。 From bfe43b720ce59f3982b80e711f4febcad2015231 Mon Sep 17 00:00:00 2001 From: CyC2018 Date: Sun, 25 Oct 2026 19:05:12 +0800 Subject: [PATCH 3/3] =?UTF-8?q?Update=20=E8=AE=BE=E8=AE=A1=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 设计模式统一由目录和各篇单独的笔记维护,本文件改为指向设计模式目录。 --- notes/设计模式.md | 3167 +-------------------------------------------- 1 file changed, 1 insertion(+), 3166 deletions(-) diff --git a/notes/设计模式.md b/notes/设计模式.md index 1aa6ddb2..7f4f2e02 100644 --- a/notes/设计模式.md +++ b/notes/设计模式.md @@ -1,3166 +1 @@ - -* [一、概述](#一概述) -* [二、创建型](#二创建型) - * [1. 单例(Singleton)](#1-单例singleton) - * [Intent](#intent) - * [Class Diagram](#class-diagram) - * [Implementation](#implementation) - * [Examples](#examples) - * [JDK](#jdk) - * [2. 简单工厂(Simple Factory)](#2-简单工厂simple-factory) - * [Intent](#intent) - * [Class Diagram](#class-diagram) - * [Implementation](#implementation) - * [3. 工厂方法(Factory Method)](#3-工厂方法factory-method) - * [Intent](#intent) - * [Class Diagram](#class-diagram) - * [Implementation](#implementation) - * [JDK](#jdk) - * [4. 抽象工厂(Abstract Factory)](#4-抽象工厂abstract-factory) - * [Intent](#intent) - * [Class Diagram](#class-diagram) - * [Implementation](#implementation) - * [JDK](#jdk) - * [5. 生成器(Builder)](#5-生成器builder) - * [Intent](#intent) - * [Class Diagram](#class-diagram) - * [Implementation](#implementation) - * [JDK](#jdk) - * [6. 原型模式(Prototype)](#6-原型模式prototype) - * [Intent](#intent) - * [Class Diagram](#class-diagram) - * [Implementation](#implementation) - * [JDK](#jdk) -* [三、行为型](#三行为型) - * [1. 责任链(Chain Of Responsibility)](#1-责任链chain-of-responsibility) - * [Intent](#intent) - * [Class Diagram](#class-diagram) - * [Implementation](#implementation) - * [JDK](#jdk) - * [2. 命令(Command)](#2-命令command) - * [Intent](#intent) - * [Class Diagram](#class-diagram) - * [Implementation](#implementation) - * [JDK](#jdk) - * [3. 解释器(Interpreter)](#3-解释器interpreter) - * [Intent](#intent) - * [Class Diagram](#class-diagram) - * [Implementation](#implementation) - * [JDK](#jdk) - * [4. 迭代器(Iterator)](#4-迭代器iterator) - * [Intent](#intent) - * [Class Diagram](#class-diagram) - * [Implementation](#implementation) - * [JDK](#jdk) - * [5. 中介者(Mediator)](#5-中介者mediator) - * [Intent](#intent) - * [Class Diagram](#class-diagram) - * [Implementation](#implementation) - * [JDK](#jdk) - * [6. 备忘录(Memento)](#6-备忘录memento) - * [Intent](#intent) - * [Class Diagram](#class-diagram) - * [Implementation](#implementation) - * [JDK](#jdk) - * [7. 观察者(Observer)](#7-观察者observer) - * [Intent](#intent) - * [Class Diagram](#class-diagram) - * [Implementation](#implementation) - * [JDK](#jdk) - * [8. 状态(State)](#8-状态state) - * [Intent](#intent) - * [Class Diagram](#class-diagram) - * [Implementation](#implementation) - * [9. 策略(Strategy)](#9-策略strategy) - * [Intent](#intent) - * [Class Diagram](#class-diagram) - * [与状态模式的比较](#与状态模式的比较) - * [Implementation](#implementation) - * [JDK](#jdk) - * [10. 模板方法(Template Method)](#10-模板方法template-method) - * [Intent](#intent) - * [Class Diagram](#class-diagram) - * [Implementation](#implementation) - * [JDK](#jdk) - * [11. 访问者(Visitor)](#11-访问者visitor) - * [Intent](#intent) - * [Class Diagram](#class-diagram) - * [Implementation](#implementation) - * [JDK](#jdk) - * [12. 空对象(Null)](#12-空对象null) - * [Intent](#intent) - * [Class Diagram](#class-diagram) - * [Implementation](#implementation) -* [四、结构型](#四结构型) - * [1. 适配器(Adapter)](#1-适配器adapter) - * [Intent](#intent) - * [Class Diagram](#class-diagram) - * [Implementation](#implementation) - * [JDK](#jdk) - * [2. 桥接(Bridge)](#2-桥接bridge) - * [Intent](#intent) - * [Class Diagram](#class-diagram) - * [Implementation](#implementation) - * [JDK](#jdk) - * [3. 组合(Composite)](#3-组合composite) - * [Intent](#intent) - * [Class Diagram](#class-diagram) - * [Implementation](#implementation) - * [JDK](#jdk) - * [4. 装饰(Decorator)](#4-装饰decorator) - * [Intent](#intent) - * [Class Diagram](#class-diagram) - * [Implementation](#implementation) - * [设计原则](#设计原则) - * [JDK](#jdk) - * [5. 外观(Facade)](#5-外观facade) - * [Intent](#intent) - * [Class Diagram](#class-diagram) - * [Implementation](#implementation) - * [设计原则](#设计原则) - * [6. 享元(Flyweight)](#6-享元flyweight) - * [Intent](#intent) - * [Class Diagram](#class-diagram) - * [Implementation](#implementation) - * [JDK](#jdk) - * [7. 代理(Proxy)](#7-代理proxy) - * [Intent](#intent) - * [Class Diagram](#class-diagram) - * [Implementation](#implementation) - * [JDK](#jdk) -* [参考资料](#参考资料) - - - -# 一、概述 - -设计模式是解决问题的方案,学习现有的设计模式可以做到经验复用。 - -拥有设计模式词汇,在沟通时就能用更少的词汇来讨论,并且不需要了解底层细节。 - -# 二、创建型 - -## 1. 单例(Singleton) - -### Intent - -确保一个类只有一个实例,并提供该实例的全局访问点。 - -### Class Diagram - -使用一个私有构造函数、一个私有静态变量以及一个公有静态函数来实现。 - -私有构造函数保证了不能通过构造函数来创建对象实例,只能通过公有静态函数返回唯一的私有静态变量。 - -

- -### Implementation - -#### Ⅰ 懒汉式-线程不安全 - -以下实现中,私有静态变量 uniqueInstance 被延迟实例化,这样做的好处是,如果没有用到该类,那么就不会实例化 uniqueInstance,从而节约资源。 - -这个实现在多线程环境下是不安全的,如果多个线程能够同时进入 `if (uniqueInstance == null)` ,并且此时 uniqueInstance 为 null,那么会有多个线程执行 `uniqueInstance = new Singleton();` 语句,这将导致实例化多次 uniqueInstance。 - -```java -public class Singleton { - - private static Singleton uniqueInstance; - - private Singleton() { - } - - public static Singleton getUniqueInstance() { - if (uniqueInstance == null) { - uniqueInstance = new Singleton(); - } - return uniqueInstance; - } -} -``` - -#### Ⅱ 饿汉式-线程安全 - -线程不安全问题主要是由于 uniqueInstance 被实例化多次,采取直接实例化 uniqueInstance 的方式就不会产生线程不安全问题。 - -但是直接实例化的方式也丢失了延迟实例化带来的节约资源的好处。 - -```java -private static Singleton uniqueInstance = new Singleton(); -``` - -#### Ⅲ 懒汉式-线程安全 - -只需要对 getUniqueInstance() 方法加锁,那么在一个时间点只能有一个线程能够进入该方法,从而避免了实例化多次 uniqueInstance。 - -但是当一个线程进入该方法之后,其它试图进入该方法的线程都必须等待,即使 uniqueInstance 已经被实例化了。这会让线程阻塞时间过长,因此该方法有性能问题,不推荐使用。 - -```java -public static synchronized Singleton getUniqueInstance() { - if (uniqueInstance == null) { - uniqueInstance = new Singleton(); - } - return uniqueInstance; -} -``` - -#### Ⅳ 双重校验锁-线程安全 - -uniqueInstance 只需要被实例化一次,之后就可以直接使用了。加锁操作只需要对实例化那部分的代码进行,只有当 uniqueInstance 没有被实例化时,才需要进行加锁。 - -双重校验锁先判断 uniqueInstance 是否已经被实例化,如果没有被实例化,那么才对实例化语句进行加锁。 - -```java -public class Singleton { - - private volatile static Singleton uniqueInstance; - - private Singleton() { - } - - public static Singleton getUniqueInstance() { - if (uniqueInstance == null) { - synchronized (Singleton.class) { - if (uniqueInstance == null) { - uniqueInstance = new Singleton(); - } - } - } - return uniqueInstance; - } -} -``` - -考虑下面的实现,也就是只使用了一个 if 语句。在 uniqueInstance == null 的情况下,如果两个线程都执行了 if 语句,那么两个线程都会进入 if 语句块内。虽然在 if 语句块内有加锁操作,但是两个线程都会执行 `uniqueInstance = new Singleton();` 这条语句,只是先后的问题,那么就会进行两次实例化。因此必须使用双重校验锁,也就是需要使用两个 if 语句:第一个 if 语句用来避免 uniqueInstance 已经被实例化之后的加锁操作,而第二个 if 语句进行了加锁,所以只能有一个线程进入,就不会出现 uniqueInstance == null 时两个线程同时进行实例化操作。 - -```java -if (uniqueInstance == null) { - synchronized (Singleton.class) { - uniqueInstance = new Singleton(); - } -} -``` - -uniqueInstance 采用 volatile 关键字修饰也是很有必要的, `uniqueInstance = new Singleton();` 这段代码其实是分为三步执行: - -1. 为 uniqueInstance 分配内存空间 -2. 初始化 uniqueInstance -3. 将 uniqueInstance 指向分配的内存地址 - -但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1>3>2。指令重排在单线程环境下不会出现问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程 T1 执行了 1 和 3,此时 T2 调用 getUniqueInstance() 后发现 uniqueInstance 不为空,因此返回 uniqueInstance,但此时 uniqueInstance 还未被初始化。 - -使用 volatile 可以禁止 JVM 的指令重排,保证在多线程环境下也能正常运行。 - -#### Ⅴ 静态内部类实现 - -当 Singleton 类被加载时,静态内部类 SingletonHolder 没有被加载进内存。只有当调用 `getUniqueInstance()` 方法从而触发 `SingletonHolder.INSTANCE` 时 SingletonHolder 才会被加载,此时初始化 INSTANCE 实例,并且 JVM 能确保 INSTANCE 只被实例化一次。 - -这种方式不仅具有延迟初始化的好处,而且由 JVM 提供了对线程安全的支持。 - -```java -public class Singleton { - - private Singleton() { - } - - private static class SingletonHolder { - private static final Singleton INSTANCE = new Singleton(); - } - - public static Singleton getUniqueInstance() { - return SingletonHolder.INSTANCE; - } -} -``` - -#### Ⅵ 枚举实现 - -```java -public enum Singleton { - - INSTANCE; - - private String objName; - - - public String getObjName() { - return objName; - } - - - public void setObjName(String objName) { - this.objName = objName; - } - - - public static void main(String[] args) { - - // 单例测试 - Singleton firstSingleton = Singleton.INSTANCE; - firstSingleton.setObjName("firstName"); - System.out.println(firstSingleton.getObjName()); - Singleton secondSingleton = Singleton.INSTANCE; - secondSingleton.setObjName("secondName"); - System.out.println(firstSingleton.getObjName()); - System.out.println(secondSingleton.getObjName()); - - // 反射获取实例测试 - try { - Singleton[] enumConstants = Singleton.class.getEnumConstants(); - for (Singleton enumConstant : enumConstants) { - System.out.println(enumConstant.getObjName()); - } - } catch (Exception e) { - e.printStackTrace(); - } - } -} -``` - -```html -firstName -secondName -secondName -secondName -``` - -该实现可以防止反射攻击。在其它实现中,通过 setAccessible() 方法可以将私有构造函数的访问级别设置为 public,然后调用构造函数从而实例化对象,如果要防止这种攻击,需要在构造函数中添加防止多次实例化的代码。该实现是由 JVM 保证只会实例化一次,因此不会出现上述的反射攻击。 - -该实现在多次序列化和反序列化之后,不会得到多个实例。而其它实现需要使用 transient 修饰所有字段,并且提供 readResolve() 方法。 - -### Examples - -- Logger Classes -- Configuration Classes -- Accessing resources in shared mode -- Factories implemented as Singletons - -### JDK - -- [java.lang.Runtime#getRuntime()](http://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#getRuntime%28%29) -- [java.awt.Desktop#getDesktop()](http://docs.oracle.com/javase/8/docs/api/java/awt/Desktop.html#getDesktop--) -- [java.lang.System#getSecurityManager()](http://docs.oracle.com/javase/8/docs/api/java/lang/System.html#getSecurityManager--) - -## 2. 简单工厂(Simple Factory) - -### Intent - -在创建一个对象时不向客户暴露内部细节,并提供一个创建对象的通用接口。 - -### Class Diagram - -简单工厂把实例化的操作单独放到一个类中,这个类就成为简单工厂类,让简单工厂类来决定应该用哪个具体子类来实例化。 - -这样做能把客户类和具体子类的实现解耦,客户类不再需要知道有哪些子类以及应当实例化哪个子类。客户类往往有多个,如果不使用简单工厂,那么所有的客户类都要知道所有子类的细节。而且一旦子类发生改变,例如增加子类,那么所有的客户类都要进行修改。 - -

- -### Implementation - -```java -public interface Product { -} -``` - -```java -public class ConcreteProduct implements Product { -} -``` - -```java -public class ConcreteProduct1 implements Product { -} -``` - -```java -public class ConcreteProduct2 implements Product { -} -``` - -以下的 Client 类包含了实例化的代码,这是一种错误的实现。如果在客户类中存在这种实例化代码,就需要考虑将代码放到简单工厂中。 - -```java -public class Client { - - public static void main(String[] args) { - int type = 1; - Product product; - if (type == 1) { - product = new ConcreteProduct1(); - } else if (type == 2) { - product = new ConcreteProduct2(); - } else { - product = new ConcreteProduct(); - } - // do something with the product - } -} -``` - -以下的 SimpleFactory 是简单工厂实现,它被所有需要进行实例化的客户类调用。 - -```java -public class SimpleFactory { - - public Product createProduct(int type) { - if (type == 1) { - return new ConcreteProduct1(); - } else if (type == 2) { - return new ConcreteProduct2(); - } - return new ConcreteProduct(); - } -} -``` - -```java -public class Client { - - public static void main(String[] args) { - SimpleFactory simpleFactory = new SimpleFactory(); - Product product = simpleFactory.createProduct(1); - // do something with the product - } -} -``` - -## 3. 工厂方法(Factory Method) - -### Intent - -定义了一个创建对象的接口,但由子类决定要实例化哪个类。工厂方法把实例化操作推迟到子类。 - -### Class Diagram - -在简单工厂中,创建对象的是另一个类,而在工厂方法中,是由子类来创建对象。 - -下图中,Factory 有一个 doSomething() 方法,这个方法需要用到一个产品对象,这个产品对象由 factoryMethod() 方法创建。该方法是抽象的,需要由子类去实现。 - -

- -### Implementation - -```java -public abstract class Factory { - abstract public Product factoryMethod(); - public void doSomething() { - Product product = factoryMethod(); - // do something with the product - } -} -``` - -```java -public class ConcreteFactory extends Factory { - public Product factoryMethod() { - return new ConcreteProduct(); - } -} -``` - -```java -public class ConcreteFactory1 extends Factory { - public Product factoryMethod() { - return new ConcreteProduct1(); - } -} -``` - -```java -public class ConcreteFactory2 extends Factory { - public Product factoryMethod() { - return new ConcreteProduct2(); - } -} -``` - -### JDK - -- [java.util.Calendar](http://docs.oracle.com/javase/8/docs/api/java/util/Calendar.html#getInstance--) -- [java.util.ResourceBundle](http://docs.oracle.com/javase/8/docs/api/java/util/ResourceBundle.html#getBundle-java.lang.String-) -- [java.text.NumberFormat](http://docs.oracle.com/javase/8/docs/api/java/text/NumberFormat.html#getInstance--) -- [java.nio.charset.Charset](http://docs.oracle.com/javase/8/docs/api/java/nio/charset/Charset.html#forName-java.lang.String-) -- [java.net.URLStreamHandlerFactory](http://docs.oracle.com/javase/8/docs/api/java/net/URLStreamHandlerFactory.html#createURLStreamHandler-java.lang.String-) -- [java.util.EnumSet](https://docs.oracle.com/javase/8/docs/api/java/util/EnumSet.html#of-E-) -- [javax.xml.bind.JAXBContext](https://docs.oracle.com/javase/8/docs/api/javax/xml/bind/JAXBContext.html#createMarshaller--) - -## 4. 抽象工厂(Abstract Factory) - -### Intent - -提供一个接口,用于创建 **相关的对象家族** 。 - -### Class Diagram - -抽象工厂模式创建的是对象家族,也就是很多对象而不是一个对象,并且这些对象是相关的,也就是说必须一起创建出来。而工厂方法模式只是用于创建一个对象,这和抽象工厂模式有很大不同。 - -抽象工厂模式用到了工厂方法模式来创建单一对象,AbstractFactory 中的 createProductA() 和 createProductB() 方法都是让子类来实现,这两个方法单独来看就是在创建一个对象,这符合工厂方法模式的定义。 - -至于创建对象的家族这一概念是在 Client 体现,Client 要通过 AbstractFactory 同时调用两个方法来创建出两个对象,在这里这两个对象就有很大的相关性,Client 需要同时创建出这两个对象。 - -从高层次来看,抽象工厂使用了组合,即 Cilent 组合了 AbstractFactory,而工厂方法模式使用了继承。 - -

- -### Implementation - -```java -public class AbstractProductA { -} -``` - -```java -public class AbstractProductB { -} -``` - -```java -public class ProductA1 extends AbstractProductA { -} -``` - -```java -public class ProductA2 extends AbstractProductA { -} -``` - -```java -public class ProductB1 extends AbstractProductB { -} -``` - -```java -public class ProductB2 extends AbstractProductB { -} -``` - -```java -public abstract class AbstractFactory { - abstract AbstractProductA createProductA(); - abstract AbstractProductB createProductB(); -} -``` - -```java -public class ConcreteFactory1 extends AbstractFactory { - AbstractProductA createProductA() { - return new ProductA1(); - } - - AbstractProductB createProductB() { - return new ProductB1(); - } -} -``` - -```java -public class ConcreteFactory2 extends AbstractFactory { - AbstractProductA createProductA() { - return new ProductA2(); - } - - AbstractProductB createProductB() { - return new ProductB2(); - } -} -``` - -```java -public class Client { - public static void main(String[] args) { - AbstractFactory abstractFactory = new ConcreteFactory1(); - AbstractProductA productA = abstractFactory.createProductA(); - AbstractProductB productB = abstractFactory.createProductB(); - // do something with productA and productB - } -} -``` - -### JDK - -- [javax.xml.parsers.DocumentBuilderFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/parsers/DocumentBuilderFactory.html) -- [javax.xml.transform.TransformerFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/transform/TransformerFactory.html#newInstance--) -- [javax.xml.xpath.XPathFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/xpath/XPathFactory.html#newInstance--) - -## 5. 生成器(Builder) - -### Intent - -封装一个对象的构造过程,并允许按步骤构造。 - -### Class Diagram - -

- -### Implementation - -以下是一个简易的 StringBuilder 实现,参考了 JDK 1.8 源码。 - -```java -public class AbstractStringBuilder { - protected char[] value; - - protected int count; - - public AbstractStringBuilder(int capacity) { - count = 0; - value = new char[capacity]; - } - - public AbstractStringBuilder append(char c) { - ensureCapacityInternal(count + 1); - value[count++] = c; - return this; - } - - private void ensureCapacityInternal(int minimumCapacity) { - // overflow-conscious code - if (minimumCapacity - value.length > 0) - expandCapacity(minimumCapacity); - } - - void expandCapacity(int minimumCapacity) { - int newCapacity = value.length * 2 + 2; - if (newCapacity - minimumCapacity < 0) - newCapacity = minimumCapacity; - if (newCapacity < 0) { - if (minimumCapacity < 0) // overflow - throw new OutOfMemoryError(); - newCapacity = Integer.MAX_VALUE; - } - value = Arrays.copyOf(value, newCapacity); - } -} -``` - -```java -public class StringBuilder extends AbstractStringBuilder { - public StringBuilder() { - super(16); - } - - @Override - public String toString() { - // Create a copy, don't share the array - return new String(value, 0, count); - } -} -``` - -```java -public class Client { - public static void main(String[] args) { - StringBuilder sb = new StringBuilder(); - final int count = 26; - for (int i = 0; i < count; i++) { - sb.append((char) ('a' + i)); - } - System.out.println(sb.toString()); - } -} -``` - -```html -abcdefghijklmnopqrstuvwxyz -``` - -### JDK - -- [java.lang.StringBuilder](http://docs.oracle.com/javase/8/docs/api/java/lang/StringBuilder.html) -- [java.nio.ByteBuffer](http://docs.oracle.com/javase/8/docs/api/java/nio/ByteBuffer.html#put-byte-) -- [java.lang.StringBuffer](http://docs.oracle.com/javase/8/docs/api/java/lang/StringBuffer.html#append-boolean-) -- [java.lang.Appendable](http://docs.oracle.com/javase/8/docs/api/java/lang/Appendable.html) -- [Apache Camel builders](https://github.com/apache/camel/tree/0e195428ee04531be27a0b659005e3aa8d159d23/camel-core/src/main/java/org/apache/camel/builder) - -## 6. 原型模式(Prototype) - -### Intent - -使用原型实例指定要创建对象的类型,通过复制这个原型来创建新对象。 - -### Class Diagram - -

- -### Implementation - -```java -public abstract class Prototype { - abstract Prototype myClone(); -} -``` - -```java -public class ConcretePrototype extends Prototype { - - private String filed; - - public ConcretePrototype(String filed) { - this.filed = filed; - } - - @Override - Prototype myClone() { - return new ConcretePrototype(filed); - } - - @Override - public String toString() { - return filed; - } -} -``` - -```java -public class Client { - public static void main(String[] args) { - Prototype prototype = new ConcretePrototype("abc"); - Prototype clone = prototype.myClone(); - System.out.println(clone.toString()); - } -} -``` - -```html -abc -``` - -### JDK - -- [java.lang.Object#clone()](http://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#clone%28%29) - -# 三、行为型 - -## 1. 责任链(Chain Of Responsibility) - -### Intent - -使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链发送该请求,直到有一个对象处理它为止。 - -### Class Diagram - -- Handler:定义处理请求的接口,并且实现后继链(successor) - -

- -### Implementation - -```java -public abstract class Handler { - - protected Handler successor; - - - public Handler(Handler successor) { - this.successor = successor; - } - - - protected abstract void handleRequest(Request request); -} -``` - -```java -public class ConcreteHandler1 extends Handler { - - public ConcreteHandler1(Handler successor) { - super(successor); - } - - - @Override - protected void handleRequest(Request request) { - if (request.getType() == RequestType.TYPE1) { - System.out.println(request.getName() + " is handle by ConcreteHandler1"); - return; - } - if (successor != null) { - successor.handleRequest(request); - } - } -} -``` - -```java -public class ConcreteHandler2 extends Handler { - - public ConcreteHandler2(Handler successor) { - super(successor); - } - - - @Override - protected void handleRequest(Request request) { - if (request.getType() == RequestType.TYPE2) { - System.out.println(request.getName() + " is handle by ConcreteHandler2"); - return; - } - if (successor != null) { - successor.handleRequest(request); - } - } -} -``` - -```java -public class Request { - - private RequestType type; - private String name; - - - public Request(RequestType type, String name) { - this.type = type; - this.name = name; - } - - - public RequestType getType() { - return type; - } - - - public String getName() { - return name; - } -} - -``` - -```java -public enum RequestType { - TYPE1, TYPE2 -} -``` - -```java -public class Client { - - public static void main(String[] args) { - - Handler handler1 = new ConcreteHandler1(null); - Handler handler2 = new ConcreteHandler2(handler1); - - Request request1 = new Request(RequestType.TYPE1, "request1"); - handler2.handleRequest(request1); - - Request request2 = new Request(RequestType.TYPE2, "request2"); - handler2.handleRequest(request2); - } -} -``` - -```html -request1 is handle by ConcreteHandler1 -request2 is handle by ConcreteHandler2 -``` - -### JDK - -- [java.util.logging.Logger#log()](http://docs.oracle.com/javase/8/docs/api/java/util/logging/Logger.html#log%28java.util.logging.Level,%20java.lang.String%29) -- [Apache Commons Chain](https://commons.apache.org/proper/commons-chain/index.html) -- [javax.servlet.Filter#doFilter()](http://docs.oracle.com/javaee/7/api/javax/servlet/Filter.html#doFilter-javax.servlet.ServletRequest-javax.servlet.ServletResponse-javax.servlet.FilterChain-) - -## 2. 命令(Command) - -### Intent - -将命令封装成对象中,具有以下作用: - -- 使用命令来参数化其它对象 -- 将命令放入队列中进行排队 -- 将命令的操作记录到日志中 -- 支持可撤销的操作 - -### Class Diagram - -- Command:命令 -- Receiver:命令接收者,也就是命令真正的执行者 -- Invoker:通过它来调用命令 -- Client:可以设置命令与命令的接收者 - -

- -### Implementation - -设计一个遥控器,可以控制电灯开关。 - -

- -```java -public interface Command { - void execute(); -} -``` - -```java -public class LightOnCommand implements Command { - Light light; - - public LightOnCommand(Light light) { - this.light = light; - } - - @Override - public void execute() { - light.on(); - } -} -``` - -```java -public class LightOffCommand implements Command { - Light light; - - public LightOffCommand(Light light) { - this.light = light; - } - - @Override - public void execute() { - light.off(); - } -} -``` - -```java -public class Light { - - public void on() { - System.out.println("Light is on!"); - } - - public void off() { - System.out.println("Light is off!"); - } -} -``` - -```java -/** - * 遥控器 - */ -public class Invoker { - private Command[] onCommands; - private Command[] offCommands; - private final int slotNum = 7; - - public Invoker() { - this.onCommands = new Command[slotNum]; - this.offCommands = new Command[slotNum]; - } - - public void setOnCommand(Command command, int slot) { - onCommands[slot] = command; - } - - public void setOffCommand(Command command, int slot) { - offCommands[slot] = command; - } - - public void onButtonWasPushed(int slot) { - onCommands[slot].execute(); - } - - public void offButtonWasPushed(int slot) { - offCommands[slot].execute(); - } -} -``` - -```java -public class Client { - public static void main(String[] args) { - Invoker invoker = new Invoker(); - Light light = new Light(); - Command lightOnCommand = new LightOnCommand(light); - Command lightOffCommand = new LightOffCommand(light); - invoker.setOnCommand(lightOnCommand, 0); - invoker.setOffCommand(lightOffCommand, 0); - invoker.onButtonWasPushed(0); - invoker.offButtonWasPushed(0); - } -} -``` - -### JDK - -- [java.lang.Runnable](http://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html) -- [Netflix Hystrix](https://github.com/Netflix/Hystrix/wiki) -- [javax.swing.Action](http://docs.oracle.com/javase/8/docs/api/javax/swing/Action.html) - -## 3. 解释器(Interpreter) - -### Intent - -为语言创建解释器,通常由语言的语法和语法分析来定义。 - -### Class Diagram - -- TerminalExpression:终结符表达式,每个终结符都需要一个 TerminalExpression。 -- Context:上下文,包含解释器之外的一些全局信息。 - -

- -### Implementation - -以下是一个规则检验器实现,具有 and 和 or 规则,通过规则可以构建一棵解析树,用来检验一个文本是否满足解析树定义的规则。 - -例如一棵解析树为 D And (A Or (B C)),文本 "D A" 满足该解析树定义的规则。 - -这里的 Context 指的是 String。 - -```java -public abstract class Expression { - public abstract boolean interpret(String str); -} -``` - -```java -public class TerminalExpression extends Expression { - - private String literal = null; - - public TerminalExpression(String str) { - literal = str; - } - - public boolean interpret(String str) { - StringTokenizer st = new StringTokenizer(str); - while (st.hasMoreTokens()) { - String test = st.nextToken(); - if (test.equals(literal)) { - return true; - } - } - return false; - } -} -``` - -```java -public class AndExpression extends Expression { - - private Expression expression1 = null; - private Expression expression2 = null; - - public AndExpression(Expression expression1, Expression expression2) { - this.expression1 = expression1; - this.expression2 = expression2; - } - - public boolean interpret(String str) { - return expression1.interpret(str) && expression2.interpret(str); - } -} -``` - -```java -public class OrExpression extends Expression { - private Expression expression1 = null; - private Expression expression2 = null; - - public OrExpression(Expression expression1, Expression expression2) { - this.expression1 = expression1; - this.expression2 = expression2; - } - - public boolean interpret(String str) { - return expression1.interpret(str) || expression2.interpret(str); - } -} -``` - -```java -public class Client { - - /** - * 构建解析树 - */ - public static Expression buildInterpreterTree() { - // Literal - Expression terminal1 = new TerminalExpression("A"); - Expression terminal2 = new TerminalExpression("B"); - Expression terminal3 = new TerminalExpression("C"); - Expression terminal4 = new TerminalExpression("D"); - // B C - Expression alternation1 = new OrExpression(terminal2, terminal3); - // A Or (B C) - Expression alternation2 = new OrExpression(terminal1, alternation1); - // D And (A Or (B C)) - return new AndExpression(terminal4, alternation2); - } - - public static void main(String[] args) { - Expression define = buildInterpreterTree(); - String context1 = "D A"; - String context2 = "A B"; - System.out.println(define.interpret(context1)); - System.out.println(define.interpret(context2)); - } -} -``` - -```html -true -false -``` - -### JDK - -- [java.util.Pattern](http://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html) -- [java.text.Normalizer](http://docs.oracle.com/javase/8/docs/api/java/text/Normalizer.html) -- All subclasses of [java.text.Format](http://docs.oracle.com/javase/8/docs/api/java/text/Format.html) -- [javax.el.ELResolver](http://docs.oracle.com/javaee/7/api/javax/el/ELResolver.html) - -## 4. 迭代器(Iterator) - -### Intent - -提供一种顺序访问聚合对象元素的方法,并且不暴露聚合对象的内部表示。 - -### Class Diagram - -- Aggregate 是聚合类,其中 createIterator() 方法可以产生一个 Iterator; -- Iterator 主要定义了 hasNext() 和 next() 方法。 -- Client 组合了 Aggregate,为了迭代遍历 Aggregate,也需要组合 Iterator。 - -

- -### Implementation - -```java -public interface Aggregate { - Iterator createIterator(); -} -``` - -```java -public class ConcreteAggregate implements Aggregate { - - private Integer[] items; - - public ConcreteAggregate() { - items = new Integer[10]; - for (int i = 0; i < items.length; i++) { - items[i] = i; - } - } - - @Override - public Iterator createIterator() { - return new ConcreteIterator(items); - } -} -``` - -```java -public interface Iterator { - - Item next(); - - boolean hasNext(); -} -``` - -```java -public class ConcreteIterator implements Iterator { - - private Item[] items; - private int position = 0; - - public ConcreteIterator(Item[] items) { - this.items = items; - } - - @Override - public Object next() { - return items[position++]; - } - - @Override - public boolean hasNext() { - return position < items.length; - } -} -``` - -```java -public class Client { - - public static void main(String[] args) { - Aggregate aggregate = new ConcreteAggregate(); - Iterator iterator = aggregate.createIterator(); - while (iterator.hasNext()) { - System.out.println(iterator.next()); - } - } -} -``` - -### JDK - -- [java.util.Iterator](http://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html) -- [java.util.Enumeration](http://docs.oracle.com/javase/8/docs/api/java/util/Enumeration.html) - -## 5. 中介者(Mediator) - -### Intent - -集中相关对象之间复杂的沟通和控制方式。 - -### Class Diagram - -- Mediator:中介者,定义一个接口用于与各同事(Colleague)对象通信。 -- Colleague:同事,相关对象 - -

- -### Implementation - -Alarm(闹钟)、CoffeePot(咖啡壶)、Calendar(日历)、Sprinkler(喷头)是一组相关的对象,在某个对象的事件产生时需要去操作其它对象,形成了下面这种依赖结构: - -

- -使用中介者模式可以将复杂的依赖结构变成星形结构: - -

- -```java -public abstract class Colleague { - public abstract void onEvent(Mediator mediator); -} -``` - -```java -public class Alarm extends Colleague { - - @Override - public void onEvent(Mediator mediator) { - mediator.doEvent("alarm"); - } - - public void doAlarm() { - System.out.println("doAlarm()"); - } -} -``` - -```java -public class CoffeePot extends Colleague { - @Override - public void onEvent(Mediator mediator) { - mediator.doEvent("coffeePot"); - } - - public void doCoffeePot() { - System.out.println("doCoffeePot()"); - } -} -``` - -```java -public class Calender extends Colleague { - @Override - public void onEvent(Mediator mediator) { - mediator.doEvent("calender"); - } - - public void doCalender() { - System.out.println("doCalender()"); - } -} -``` - -```java -public class Sprinkler extends Colleague { - @Override - public void onEvent(Mediator mediator) { - mediator.doEvent("sprinkler"); - } - - public void doSprinkler() { - System.out.println("doSprinkler()"); - } -} -``` - -```java -public abstract class Mediator { - public abstract void doEvent(String eventType); -} -``` - -```java -public class ConcreteMediator extends Mediator { - private Alarm alarm; - private CoffeePot coffeePot; - private Calender calender; - private Sprinkler sprinkler; - - public ConcreteMediator(Alarm alarm, CoffeePot coffeePot, Calender calender, Sprinkler sprinkler) { - this.alarm = alarm; - this.coffeePot = coffeePot; - this.calender = calender; - this.sprinkler = sprinkler; - } - - @Override - public void doEvent(String eventType) { - switch (eventType) { - case "alarm": - doAlarmEvent(); - break; - case "coffeePot": - doCoffeePotEvent(); - break; - case "calender": - doCalenderEvent(); - break; - default: - doSprinklerEvent(); - } - } - - public void doAlarmEvent() { - alarm.doAlarm(); - coffeePot.doCoffeePot(); - calender.doCalender(); - sprinkler.doSprinkler(); - } - - public void doCoffeePotEvent() { - // ... - } - - public void doCalenderEvent() { - // ... - } - - public void doSprinklerEvent() { - // ... - } -} -``` - -```java -public class Client { - public static void main(String[] args) { - Alarm alarm = new Alarm(); - CoffeePot coffeePot = new CoffeePot(); - Calender calender = new Calender(); - Sprinkler sprinkler = new Sprinkler(); - Mediator mediator = new ConcreteMediator(alarm, coffeePot, calender, sprinkler); - // 闹钟事件到达,调用中介者就可以操作相关对象 - alarm.onEvent(mediator); - } -} -``` - -```java -doAlarm() -doCoffeePot() -doCalender() -doSprinkler() -``` - -### JDK - -- All scheduleXXX() methods of [java.util.Timer](http://docs.oracle.com/javase/8/docs/api/java/util/Timer.html) -- [java.util.concurrent.Executor#execute()](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executor.html#execute-java.lang.Runnable-) -- submit() and invokeXXX() methods of [java.util.concurrent.ExecutorService](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html) -- scheduleXXX() methods of [java.util.concurrent.ScheduledExecutorService](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ScheduledExecutorService.html) -- [java.lang.reflect.Method#invoke()](http://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Method.html#invoke-java.lang.Object-java.lang.Object...-) - -## 6. 备忘录(Memento) - -### Intent - -在不违反封装的情况下获得对象的内部状态,从而在需要时可以将对象恢复到最初状态。 - -### Class Diagram - -- Originator:原始对象 -- Caretaker:负责保存好备忘录 -- Menento:备忘录,存储原始对象的的状态。备忘录实际上有两个接口,一个是提供给 Caretaker 的窄接口:它只能将备忘录传递给其它对象;一个是提供给 Originator 的宽接口,允许它访问到先前状态所需的所有数据。理想情况是只允许 Originator 访问本备忘录的内部状态。 - -

- -### Implementation - -以下实现了一个简单计算器程序,可以输入两个值,然后计算这两个值的和。备忘录模式允许将这两个值存储起来,然后在某个时刻用存储的状态进行恢复。 - -实现参考:[Memento Pattern - Calculator Example - Java Sourcecode](https://www.oodesign.com/memento-pattern-calculator-example-java-sourcecode.html) - -```java -/** - * Originator Interface - */ -public interface Calculator { - - // Create Memento - PreviousCalculationToCareTaker backupLastCalculation(); - - // setMemento - void restorePreviousCalculation(PreviousCalculationToCareTaker memento); - - int getCalculationResult(); - - void setFirstNumber(int firstNumber); - - void setSecondNumber(int secondNumber); -} -``` - -```java -/** - * Originator Implementation - */ -public class CalculatorImp implements Calculator { - - private int firstNumber; - private int secondNumber; - - @Override - public PreviousCalculationToCareTaker backupLastCalculation() { - // create a memento object used for restoring two numbers - return new PreviousCalculationImp(firstNumber, secondNumber); - } - - @Override - public void restorePreviousCalculation(PreviousCalculationToCareTaker memento) { - this.firstNumber = ((PreviousCalculationToOriginator) memento).getFirstNumber(); - this.secondNumber = ((PreviousCalculationToOriginator) memento).getSecondNumber(); - } - - @Override - public int getCalculationResult() { - // result is adding two numbers - return firstNumber + secondNumber; - } - - @Override - public void setFirstNumber(int firstNumber) { - this.firstNumber = firstNumber; - } - - @Override - public void setSecondNumber(int secondNumber) { - this.secondNumber = secondNumber; - } -} -``` - -```java -/** - * Memento Interface to Originator - * - * This interface allows the originator to restore its state - */ -public interface PreviousCalculationToOriginator { - int getFirstNumber(); - int getSecondNumber(); -} -``` - -```java -/** - * Memento interface to CalculatorOperator (Caretaker) - */ -public interface PreviousCalculationToCareTaker { - // no operations permitted for the caretaker -} -``` - -```java -/** - * Memento Object Implementation - *

- * Note that this object implements both interfaces to Originator and CareTaker - */ -public class PreviousCalculationImp implements PreviousCalculationToCareTaker, - PreviousCalculationToOriginator { - - private int firstNumber; - private int secondNumber; - - public PreviousCalculationImp(int firstNumber, int secondNumber) { - this.firstNumber = firstNumber; - this.secondNumber = secondNumber; - } - - @Override - public int getFirstNumber() { - return firstNumber; - } - - @Override - public int getSecondNumber() { - return secondNumber; - } -} -``` - -```java -/** - * CareTaker object - */ -public class Client { - - public static void main(String[] args) { - // program starts - Calculator calculator = new CalculatorImp(); - - // assume user enters two numbers - calculator.setFirstNumber(10); - calculator.setSecondNumber(100); - - // find result - System.out.println(calculator.getCalculationResult()); - - // Store result of this calculation in case of error - PreviousCalculationToCareTaker memento = calculator.backupLastCalculation(); - - // user enters a number - calculator.setFirstNumber(17); - - // user enters a wrong second number and calculates result - calculator.setSecondNumber(-290); - - // calculate result - System.out.println(calculator.getCalculationResult()); - - // user hits CTRL + Z to undo last operation and see last result - calculator.restorePreviousCalculation(memento); - - // result restored - System.out.println(calculator.getCalculationResult()); - } -} -``` - -```html -110 --273 -110 -``` - -### JDK - -- java.io.Serializable - -## 7. 观察者(Observer) - -### Intent - -定义对象之间的一对多依赖,当一个对象状态改变时,它的所有依赖都会收到通知并且自动更新状态。 - -主题(Subject)是被观察的对象,而其所有依赖者(Observer)称为观察者。 - -


- -### Class Diagram - -主题(Subject)具有注册和移除观察者、并通知所有观察者的功能,主题是通过维护一张观察者列表来实现这些操作的。 - -观察者(Observer)的注册功能需要调用主题的 registerObserver() 方法。 - -

- -### Implementation - -天气数据布告板会在天气信息发生改变时更新其内容,布告板有多个,并且在将来会继续增加。 - -

- -```java -public interface Subject { - void registerObserver(Observer o); - - void removeObserver(Observer o); - - void notifyObserver(); -} -``` - -```java -public class WeatherData implements Subject { - private List observers; - private float temperature; - private float humidity; - private float pressure; - - public WeatherData() { - observers = new ArrayList<>(); - } - - public void setMeasurements(float temperature, float humidity, float pressure) { - this.temperature = temperature; - this.humidity = humidity; - this.pressure = pressure; - notifyObserver(); - } - - @Override - public void registerObserver(Observer o) { - observers.add(o); - } - - @Override - public void removeObserver(Observer o) { - int i = observers.indexOf(o); - if (i >= 0) { - observers.remove(i); - } - } - - @Override - public void notifyObserver() { - for (Observer o : observers) { - o.update(temperature, humidity, pressure); - } - } -} -``` - -```java -public interface Observer { - void update(float temp, float humidity, float pressure); -} -``` - -```java -public class StatisticsDisplay implements Observer { - - public StatisticsDisplay(Subject weatherData) { - weatherData.reisterObserver(this); - } - - @Override - public void update(float temp, float humidity, float pressure) { - System.out.println("StatisticsDisplay.update: " + temp + " " + humidity + " " + pressure); - } -} -``` - -```java -public class CurrentConditionsDisplay implements Observer { - - public CurrentConditionsDisplay(Subject weatherData) { - weatherData.registerObserver(this); - } - - @Override - public void update(float temp, float humidity, float pressure) { - System.out.println("CurrentConditionsDisplay.update: " + temp + " " + humidity + " " + pressure); - } -} -``` - -```java -public class WeatherStation { - public static void main(String[] args) { - WeatherData weatherData = new WeatherData(); - CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay(weatherData); - StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData); - - weatherData.setMeasurements(0, 0, 0); - weatherData.setMeasurements(1, 1, 1); - } -} -``` - -```html -CurrentConditionsDisplay.update: 0.0 0.0 0.0 -StatisticsDisplay.update: 0.0 0.0 0.0 -CurrentConditionsDisplay.update: 1.0 1.0 1.0 -StatisticsDisplay.update: 1.0 1.0 1.0 -``` - -### JDK - -- [java.util.Observer](http://docs.oracle.com/javase/8/docs/api/java/util/Observer.html) -- [java.util.EventListener](http://docs.oracle.com/javase/8/docs/api/java/util/EventListener.html) -- [javax.servlet.http.HttpSessionBindingListener](http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpSessionBindingListener.html) -- [RxJava](https://github.com/ReactiveX/RxJava) - -## 8. 状态(State) - -### Intent - -允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它所属的类。 - -### Class Diagram - -

- -### Implementation - -糖果销售机有多种状态,每种状态下销售机有不同的行为,状态可以发生转移,使得销售机的行为也发生改变。 - -

- -```java -public interface State { - /** - * 投入 25 分钱 - */ - void insertQuarter(); - - /** - * 退回 25 分钱 - */ - void ejectQuarter(); - - /** - * 转动曲柄 - */ - void turnCrank(); - - /** - * 发放糖果 - */ - void dispense(); -} -``` - -```java -public class HasQuarterState implements State { - - private GumballMachine gumballMachine; - - public HasQuarterState(GumballMachine gumballMachine) { - this.gumballMachine = gumballMachine; - } - - @Override - public void insertQuarter() { - System.out.println("You can't insert another quarter"); - } - - @Override - public void ejectQuarter() { - System.out.println("Quarter returned"); - gumballMachine.setState(gumballMachine.getNoQuarterState()); - } - - @Override - public void turnCrank() { - System.out.println("You turned..."); - gumballMachine.setState(gumballMachine.getSoldState()); - } - - @Override - public void dispense() { - System.out.println("No gumball dispensed"); - } -} -``` - -```java -public class NoQuarterState implements State { - - GumballMachine gumballMachine; - - public NoQuarterState(GumballMachine gumballMachine) { - this.gumballMachine = gumballMachine; - } - - @Override - public void insertQuarter() { - System.out.println("You insert a quarter"); - gumballMachine.setState(gumballMachine.getHasQuarterState()); - } - - @Override - public void ejectQuarter() { - System.out.println("You haven't insert a quarter"); - } - - @Override - public void turnCrank() { - System.out.println("You turned, but there's no quarter"); - } - - @Override - public void dispense() { - System.out.println("You need to pay first"); - } -} -``` - -```java -public class SoldOutState implements State { - - GumballMachine gumballMachine; - - public SoldOutState(GumballMachine gumballMachine) { - this.gumballMachine = gumballMachine; - } - - @Override - public void insertQuarter() { - System.out.println("You can't insert a quarter, the machine is sold out"); - } - - @Override - public void ejectQuarter() { - System.out.println("You can't eject, you haven't inserted a quarter yet"); - } - - @Override - public void turnCrank() { - System.out.println("You turned, but there are no gumballs"); - } - - @Override - public void dispense() { - System.out.println("No gumball dispensed"); - } -} -``` - -```java -public class SoldState implements State { - - GumballMachine gumballMachine; - - public SoldState(GumballMachine gumballMachine) { - this.gumballMachine = gumballMachine; - } - - @Override - public void insertQuarter() { - System.out.println("Please wait, we're already giving you a gumball"); - } - - @Override - public void ejectQuarter() { - System.out.println("Sorry, you already turned the crank"); - } - - @Override - public void turnCrank() { - System.out.println("Turning twice doesn't get you another gumball!"); - } - - @Override - public void dispense() { - gumballMachine.releaseBall(); - if (gumballMachine.getCount() > 0) { - gumballMachine.setState(gumballMachine.getNoQuarterState()); - } else { - System.out.println("Oops, out of gumballs"); - gumballMachine.setState(gumballMachine.getSoldOutState()); - } - } -} -``` - -```java -public class GumballMachine { - - private State soldOutState; - private State noQuarterState; - private State hasQuarterState; - private State soldState; - - private State state; - private int count = 0; - - public GumballMachine(int numberGumballs) { - count = numberGumballs; - soldOutState = new SoldOutState(this); - noQuarterState = new NoQuarterState(this); - hasQuarterState = new HasQuarterState(this); - soldState = new SoldState(this); - - if (numberGumballs > 0) { - state = noQuarterState; - } else { - state = soldOutState; - } - } - - public void insertQuarter() { - state.insertQuarter(); - } - - public void ejectQuarter() { - state.ejectQuarter(); - } - - public void turnCrank() { - state.turnCrank(); - state.dispense(); - } - - public void setState(State state) { - this.state = state; - } - - public void releaseBall() { - System.out.println("A gumball comes rolling out the slot..."); - if (count != 0) { - count -= 1; - } - } - - public State getSoldOutState() { - return soldOutState; - } - - public State getNoQuarterState() { - return noQuarterState; - } - - public State getHasQuarterState() { - return hasQuarterState; - } - - public State getSoldState() { - return soldState; - } - - public int getCount() { - return count; - } -} -``` - -```java -public class Client { - - public static void main(String[] args) { - GumballMachine gumballMachine = new GumballMachine(5); - - gumballMachine.insertQuarter(); - gumballMachine.turnCrank(); - - gumballMachine.insertQuarter(); - gumballMachine.ejectQuarter(); - gumballMachine.turnCrank(); - - gumballMachine.insertQuarter(); - gumballMachine.turnCrank(); - gumballMachine.insertQuarter(); - gumballMachine.turnCrank(); - gumballMachine.ejectQuarter(); - - gumballMachine.insertQuarter(); - gumballMachine.insertQuarter(); - gumballMachine.turnCrank(); - gumballMachine.insertQuarter(); - gumballMachine.turnCrank(); - gumballMachine.insertQuarter(); - gumballMachine.turnCrank(); - } -} -``` - -```html -You insert a quarter -You turned... -A gumball comes rolling out the slot... -You insert a quarter -Quarter returned -You turned, but there's no quarter -You need to pay first -You insert a quarter -You turned... -A gumball comes rolling out the slot... -You insert a quarter -You turned... -A gumball comes rolling out the slot... -You haven't insert a quarter -You insert a quarter -You can't insert another quarter -You turned... -A gumball comes rolling out the slot... -You insert a quarter -You turned... -A gumball comes rolling out the slot... -Oops, out of gumballs -You can't insert a quarter, the machine is sold out -You turned, but there are no gumballs -No gumball dispensed -``` - -## 9. 策略(Strategy) - -### Intent - -定义一系列算法,封装每个算法,并使它们可以互换。 - -策略模式可以让算法独立于使用它的客户端。 - -### Class Diagram - -- Strategy 接口定义了一个算法族,它们都实现了 behavior() 方法。 -- Context 是使用到该算法族的类,其中的 doSomething() 方法会调用 behavior(),setStrategy(Strategy) 方法可以动态地改变 strategy 对象,也就是说能动态地改变 Context 所使用的算法。 - -

- -### 与状态模式的比较 - -状态模式的类图和策略模式类似,并且都是能够动态改变对象的行为。但是状态模式是通过状态转移来改变 Context 所组合的 State 对象,而策略模式是通过 Context 本身的决策来改变组合的 Strategy 对象。所谓的状态转移,是指 Context 在运行过程中由于一些条件发生改变而使得 State 对象发生改变,注意必须要是在运行过程中。 - -状态模式主要是用来解决状态转移的问题,当状态发生转移了,那么 Context 对象就会改变它的行为;而策略模式主要是用来封装一组可以互相替代的算法族,并且可以根据需要动态地去替换 Context 使用的算法。 - -### Implementation - -设计一个鸭子,它可以动态地改变叫声。这里的算法族是鸭子的叫声行为。 - -```java -public interface QuackBehavior { - void quack(); -} -``` - -```java -public class Quack implements QuackBehavior { - @Override - public void quack() { - System.out.println("quack!"); - } -} -``` - -```java -public class Squeak implements QuackBehavior{ - @Override - public void quack() { - System.out.println("squeak!"); - } -} -``` - -```java -public class Duck { - - private QuackBehavior quackBehavior; - - public void performQuack() { - if (quackBehavior != null) { - quackBehavior.quack(); - } - } - - public void setQuackBehavior(QuackBehavior quackBehavior) { - this.quackBehavior = quackBehavior; - } -} -``` - -```java -public class Client { - - public static void main(String[] args) { - Duck duck = new Duck(); - duck.setQuackBehavior(new Squeak()); - duck.performQuack(); - duck.setQuackBehavior(new Quack()); - duck.performQuack(); - } -} -``` - -```html -squeak! -quack! -``` - -### JDK - -- java.util.Comparator#compare() -- javax.servlet.http.HttpServlet -- javax.servlet.Filter#doFilter() - -## 10. 模板方法(Template Method) - -### Intent - -定义算法框架,并将一些步骤的实现延迟到子类。 - -通过模板方法,子类可以重新定义算法的某些步骤,而不用改变算法的结构。 - -### Class Diagram - -

- -### Implementation - -冲咖啡和冲茶都有类似的流程,但是某些步骤会有点不一样,要求复用那些相同步骤的代码。 - -

- -```java -public abstract class CaffeineBeverage { - - final void prepareRecipe() { - boilWater(); - brew(); - pourInCup(); - addCondiments(); - } - - abstract void brew(); - - abstract void addCondiments(); - - void boilWater() { - System.out.println("boilWater"); - } - - void pourInCup() { - System.out.println("pourInCup"); - } -} -``` - -```java -public class Coffee extends CaffeineBeverage { - @Override - void brew() { - System.out.println("Coffee.brew"); - } - - @Override - void addCondiments() { - System.out.println("Coffee.addCondiments"); - } -} -``` - -```java -public class Tea extends CaffeineBeverage { - @Override - void brew() { - System.out.println("Tea.brew"); - } - - @Override - void addCondiments() { - System.out.println("Tea.addCondiments"); - } -} -``` - -```java -public class Client { - public static void main(String[] args) { - CaffeineBeverage caffeineBeverage = new Coffee(); - caffeineBeverage.prepareRecipe(); - System.out.println("-----------"); - caffeineBeverage = new Tea(); - caffeineBeverage.prepareRecipe(); - } -} -``` - -```html -boilWater -Coffee.brew -pourInCup -Coffee.addCondiments ------------ -boilWater -Tea.brew -pourInCup -Tea.addCondiments -``` - -### JDK - -- java.util.Collections#sort() -- java.io.InputStream#skip() -- java.io.InputStream#read() -- java.util.AbstractList#indexOf() - -## 11. 访问者(Visitor) - -### Intent - -为一个对象结构(比如组合结构)增加新能力。 - -### Class Diagram - -- Visitor:访问者,为每一个 ConcreteElement 声明一个 visit 操作 -- ConcreteVisitor:具体访问者,存储遍历过程中的累计结果 -- ObjectStructure:对象结构,可以是组合结构,或者是一个集合。 - -

- -### Implementation - -```java -public interface Element { - void accept(Visitor visitor); -} -``` - -```java -class CustomerGroup { - - private List customers = new ArrayList<>(); - - void accept(Visitor visitor) { - for (Customer customer : customers) { - customer.accept(visitor); - } - } - - void addCustomer(Customer customer) { - customers.add(customer); - } -} -``` - -```java -public class Customer implements Element { - - private String name; - private List orders = new ArrayList<>(); - - Customer(String name) { - this.name = name; - } - - String getName() { - return name; - } - - void addOrder(Order order) { - orders.add(order); - } - - public void accept(Visitor visitor) { - visitor.visit(this); - for (Order order : orders) { - order.accept(visitor); - } - } -} -``` - -```java -public class Order implements Element { - - private String name; - private List items = new ArrayList(); - - Order(String name) { - this.name = name; - } - - Order(String name, String itemName) { - this.name = name; - this.addItem(new Item(itemName)); - } - - String getName() { - return name; - } - - void addItem(Item item) { - items.add(item); - } - - public void accept(Visitor visitor) { - visitor.visit(this); - - for (Item item : items) { - item.accept(visitor); - } - } -} -``` - -```java -public class Item implements Element { - - private String name; - - Item(String name) { - this.name = name; - } - - String getName() { - return name; - } - - public void accept(Visitor visitor) { - visitor.visit(this); - } -} -``` - -```java -public interface Visitor { - void visit(Customer customer); - - void visit(Order order); - - void visit(Item item); -} -``` - -```java -public class GeneralReport implements Visitor { - - private int customersNo; - private int ordersNo; - private int itemsNo; - - public void visit(Customer customer) { - System.out.println(customer.getName()); - customersNo++; - } - - public void visit(Order order) { - System.out.println(order.getName()); - ordersNo++; - } - - public void visit(Item item) { - System.out.println(item.getName()); - itemsNo++; - } - - public void displayResults() { - System.out.println("Number of customers: " + customersNo); - System.out.println("Number of orders: " + ordersNo); - System.out.println("Number of items: " + itemsNo); - } -} -``` - -```java -public class Client { - public static void main(String[] args) { - Customer customer1 = new Customer("customer1"); - customer1.addOrder(new Order("order1", "item1")); - customer1.addOrder(new Order("order2", "item1")); - customer1.addOrder(new Order("order3", "item1")); - - Order order = new Order("order_a"); - order.addItem(new Item("item_a1")); - order.addItem(new Item("item_a2")); - order.addItem(new Item("item_a3")); - Customer customer2 = new Customer("customer2"); - customer2.addOrder(order); - - CustomerGroup customers = new CustomerGroup(); - customers.addCustomer(customer1); - customers.addCustomer(customer2); - - GeneralReport visitor = new GeneralReport(); - customers.accept(visitor); - visitor.displayResults(); - } -} -``` - -```html -customer1 -order1 -item1 -order2 -item1 -order3 -item1 -customer2 -order_a -item_a1 -item_a2 -item_a3 -Number of customers: 2 -Number of orders: 4 -Number of items: 6 -``` - -### JDK - -- javax.lang.model.element.Element and javax.lang.model.element.ElementVisitor -- javax.lang.model.type.TypeMirror and javax.lang.model.type.TypeVisitor - -## 12. 空对象(Null) - -### Intent - -使用什么都不做的空对象来代替 NULL。 - -一个方法返回 NULL,意味着方法的调用端需要去检查返回值是否是 NULL,这么做会导致非常多的冗余的检查代码。并且如果某一个调用端忘记了做这个检查返回值,而直接使用返回的对象,那么就有可能抛出空指针异常。 - -### Class Diagram - -

- -### Implementation - -```java -public abstract class AbstractOperation { - abstract void request(); -} -``` - -```java -public class RealOperation extends AbstractOperation { - @Override - void request() { - System.out.println("do something"); - } -} -``` - -```java -public class NullOperation extends AbstractOperation{ - @Override - void request() { - // do nothing - } -} -``` - -```java -public class Client { - public static void main(String[] args) { - AbstractOperation abstractOperation = func(-1); - abstractOperation.request(); - } - - public static AbstractOperation func(int para) { - if (para < 0) { - return new NullOperation(); - } - return new RealOperation(); - } -} -``` - -# 四、结构型 - -## 1. 适配器(Adapter) - -### Intent - -把一个类接口转换成另一个用户需要的接口。 - -

- -### Class Diagram - -

- -### Implementation - -鸭子(Duck)和火鸡(Turkey)拥有不同的叫声,Duck 的叫声调用 quack() 方法,而 Turkey 调用 gobble() 方法。 - -要求将 Turkey 的 gobble() 方法适配成 Duck 的 quack() 方法,从而让火鸡冒充鸭子! - -```java -public interface Duck { - void quack(); -} -``` - -```java -public interface Turkey { - void gobble(); -} -``` - -```java -public class WildTurkey implements Turkey { - @Override - public void gobble() { - System.out.println("gobble!"); - } -} -``` - -```java -public class TurkeyAdapter implements Duck { - Turkey turkey; - - public TurkeyAdapter(Turkey turkey) { - this.turkey = turkey; - } - - @Override - public void quack() { - turkey.gobble(); - } -} -``` - -```java -public class Client { - public static void main(String[] args) { - Turkey turkey = new WildTurkey(); - Duck duck = new TurkeyAdapter(turkey); - duck.quack(); - } -} -``` - -### JDK - -- [java.util.Arrays#asList()](http://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html#asList%28T...%29) -- [java.util.Collections#list()](https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#list-java.util.Enumeration-) -- [java.util.Collections#enumeration()](https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#enumeration-java.util.Collection-) -- [javax.xml.bind.annotation.adapters.XMLAdapter](http://docs.oracle.com/javase/8/docs/api/javax/xml/bind/annotation/adapters/XmlAdapter.html#marshal-BoundType-) - -## 2. 桥接(Bridge) - -### Intent - -将抽象与实现分离开来,使它们可以独立变化。 - -### Class Diagram - -- Abstraction:定义抽象类的接口 -- Implementor:定义实现类接口 - -

- -### Implementation - -RemoteControl 表示遥控器,指代 Abstraction。 - -TV 表示电视,指代 Implementor。 - -桥接模式将遥控器和电视分离开来,从而可以独立改变遥控器或者电视的实现。 - -```java -public abstract class TV { - public abstract void on(); - - public abstract void off(); - - public abstract void tuneChannel(); -} -``` - -```java -public class Sony extends TV { - @Override - public void on() { - System.out.println("Sony.on()"); - } - - @Override - public void off() { - System.out.println("Sony.off()"); - } - - @Override - public void tuneChannel() { - System.out.println("Sony.tuneChannel()"); - } -} -``` - -```java -public class RCA extends TV { - @Override - public void on() { - System.out.println("RCA.on()"); - } - - @Override - public void off() { - System.out.println("RCA.off()"); - } - - @Override - public void tuneChannel() { - System.out.println("RCA.tuneChannel()"); - } -} -``` - -```java -public abstract class RemoteControl { - protected TV tv; - - public RemoteControl(TV tv) { - this.tv = tv; - } - - public abstract void on(); - - public abstract void off(); - - public abstract void tuneChannel(); -} -``` - -```java -public class ConcreteRemoteControl1 extends RemoteControl { - public ConcreteRemoteControl1(TV tv) { - super(tv); - } - - @Override - public void on() { - System.out.println("ConcreteRemoteControl1.on()"); - tv.on(); - } - - @Override - public void off() { - System.out.println("ConcreteRemoteControl1.off()"); - tv.off(); - } - - @Override - public void tuneChannel() { - System.out.println("ConcreteRemoteControl1.tuneChannel()"); - tv.tuneChannel(); - } -} -``` - -```java -public class ConcreteRemoteControl2 extends RemoteControl { - public ConcreteRemoteControl2(TV tv) { - super(tv); - } - - @Override - public void on() { - System.out.println("ConcreteRemoteControl2.on()"); - tv.on(); - } - - @Override - public void off() { - System.out.println("ConcreteRemoteControl2.off()"); - tv.off(); - } - - @Override - public void tuneChannel() { - System.out.println("ConcreteRemoteControl2.tuneChannel()"); - tv.tuneChannel(); - } -} -``` - -```java -public class Client { - public static void main(String[] args) { - RemoteControl remoteControl1 = new ConcreteRemoteControl1(new RCA()); - remoteControl1.on(); - remoteControl1.off(); - remoteControl1.tuneChannel(); - RemoteControl remoteControl2 = new ConcreteRemoteControl2(new Sony()); - remoteControl2.on(); - remoteControl2.off(); - remoteControl2.tuneChannel(); - } -} -``` - -### JDK - -- AWT (It provides an abstraction layer which maps onto the native OS the windowing support.) -- JDBC - -## 3. 组合(Composite) - -### Intent - -将对象组合成树形结构来表示“整体/部分”层次关系,允许用户以相同的方式处理单独对象和组合对象。 - -### Class Diagram - -组件(Component)类是组合类(Composite)和叶子类(Leaf)的父类,可以把组合类看成是树的中间节点。 - -组合对象拥有一个或者多个组件对象,因此组合对象的操作可以委托给组件对象去处理,而组件对象可以是另一个组合对象或者叶子对象。 - -

- -### Implementation - -```java -public abstract class Component { - protected String name; - - public Component(String name) { - this.name = name; - } - - public void print() { - print(0); - } - - abstract void print(int level); - - abstract public void add(Component component); - - abstract public void remove(Component component); -} -``` - -```java -public class Composite extends Component { - - private List child; - - public Composite(String name) { - super(name); - child = new ArrayList<>(); - } - - @Override - void print(int level) { - for (int i = 0; i < level; i++) { - System.out.print("--"); - } - System.out.println("Composite:" + name); - for (Component component : child) { - component.print(level + 1); - } - } - - @Override - public void add(Component component) { - child.add(component); - } - - @Override - public void remove(Component component) { - child.remove(component); - } -} -``` - -```java -public class Leaf extends Component { - public Leaf(String name) { - super(name); - } - - @Override - void print(int level) { - for (int i = 0; i < level; i++) { - System.out.print("--"); - } - System.out.println("left:" + name); - } - - @Override - public void add(Component component) { - throw new UnsupportedOperationException(); // 牺牲透明性换取单一职责原则,这样就不用考虑是叶子节点还是组合节点 - } - - @Override - public void remove(Component component) { - throw new UnsupportedOperationException(); - } -} -``` - -```java -public class Client { - public static void main(String[] args) { - Composite root = new Composite("root"); - Component node1 = new Leaf("1"); - Component node2 = new Composite("2"); - Component node3 = new Leaf("3"); - root.add(node1); - root.add(node2); - root.add(node3); - Component node21 = new Leaf("21"); - Component node22 = new Composite("22"); - node2.add(node21); - node2.add(node22); - Component node221 = new Leaf("221"); - node22.add(node221); - root.print(); - } -} -``` - -```html -Composite:root ---left:1 ---Composite:2 -----left:21 -----Composite:22 -------left:221 ---left:3 -``` - -### JDK - -- javax.swing.JComponent#add(Component) -- java.awt.Container#add(Component) -- java.util.Map#putAll(Map) -- java.util.List#addAll(Collection) -- java.util.Set#addAll(Collection) - -## 4. 装饰(Decorator) - -### Intent - -为对象动态添加功能。 - -### Class Diagram - -装饰者(Decorator)和具体组件(ConcreteComponent)都继承自组件(Component),具体组件的方法实现不需要依赖于其它对象,而装饰者组合了一个组件,这样它可以装饰其它装饰者或者具体组件。所谓装饰,就是把这个装饰者套在被装饰者之上,从而动态扩展被装饰者的功能。装饰者的方法有一部分是自己的,这属于它的功能,然后调用被装饰者的方法实现,从而也保留了被装饰者的功能。可以看到,具体组件应当是装饰层次的最低层,因为只有具体组件的方法实现不需要依赖于其它对象。 - -

- -### Implementation - -设计不同种类的饮料,饮料可以添加配料,比如可以添加牛奶,并且支持动态添加新配料。每增加一种配料,该饮料的价格就会增加,要求计算一种饮料的价格。 - -下图表示在 DarkRoast 饮料上新增新添加 Mocha 配料,之后又添加了 Whip 配料。DarkRoast 被 Mocha 包裹,Mocha 又被 Whip 包裹。它们都继承自相同父类,都有 cost() 方法,外层类的 cost() 方法调用了内层类的 cost() 方法。 - -

- -```java -public interface Beverage { - double cost(); -} -``` - -```java -public class DarkRoast implements Beverage { - @Override - public double cost() { - return 1; - } -} -``` - -```java -public class HouseBlend implements Beverage { - @Override - public double cost() { - return 1; - } -} -``` - -```java -public abstract class CondimentDecorator implements Beverage { - protected Beverage beverage; -} -``` - -```java -public class Milk extends CondimentDecorator { - - public Milk(Beverage beverage) { - this.beverage = beverage; - } - - @Override - public double cost() { - return 1 + beverage.cost(); - } -} -``` - -```java -public class Mocha extends CondimentDecorator { - - public Mocha(Beverage beverage) { - this.beverage = beverage; - } - - @Override - public double cost() { - return 1 + beverage.cost(); - } -} -``` - -```java -public class Client { - - public static void main(String[] args) { - Beverage beverage = new HouseBlend(); - beverage = new Mocha(beverage); - beverage = new Milk(beverage); - System.out.println(beverage.cost()); - } -} -``` - -```html -3.0 -``` - -### 设计原则 - -类应该对扩展开放,对修改关闭:也就是添加新功能时不需要修改代码。饮料可以动态添加新的配料,而不需要去修改饮料的代码。 - -不可能把所有的类设计成都满足这一原则,应当把该原则应用于最有可能发生改变的地方。 - -### JDK - -- java.io.BufferedInputStream(InputStream) -- java.io.DataInputStream(InputStream) -- java.io.BufferedOutputStream(OutputStream) -- java.util.zip.ZipOutputStream(OutputStream) -- java.util.Collections#checked[List|Map|Set|SortedSet|SortedMap]() - -## 5. 外观(Facade) - -### Intent - -提供了一个统一的接口,用来访问子系统中的一群接口,从而让子系统更容易使用。 - -### Class Diagram - -

- -### Implementation - -观看电影需要操作很多电器,使用外观模式实现一键看电影功能。 - -```java -public class SubSystem { - public void turnOnTV() { - System.out.println("turnOnTV()"); - } - - public void setCD(String cd) { - System.out.println("setCD( " + cd + " )"); - } - - public void startWatching(){ - System.out.println("startWatching()"); - } -} -``` - -```java -public class Facade { - private SubSystem subSystem = new SubSystem(); - - public void watchMovie() { - subSystem.turnOnTV(); - subSystem.setCD("a movie"); - subSystem.startWatching(); - } -} -``` - -```java -public class Client { - public static void main(String[] args) { - Facade facade = new Facade(); - facade.watchMovie(); - } -} -``` - -### 设计原则 - -最少知识原则:只和你的密友谈话。也就是说客户对象所需要交互的对象应当尽可能少。 - -## 6. 享元(Flyweight) - -### Intent - -利用共享的方式来支持大量细粒度的对象,这些对象一部分内部状态是相同的。 - -### Class Diagram - -- Flyweight:享元对象 -- IntrinsicState:内部状态,享元对象共享内部状态 -- ExtrinsicState:外部状态,每个享元对象的外部状态不同 - -

- -### Implementation - -```java -public interface Flyweight { - void doOperation(String extrinsicState); -} -``` - -```java -public class ConcreteFlyweight implements Flyweight { - - private String intrinsicState; - - public ConcreteFlyweight(String intrinsicState) { - this.intrinsicState = intrinsicState; - } - - @Override - public void doOperation(String extrinsicState) { - System.out.println("Object address: " + System.identityHashCode(this)); - System.out.println("IntrinsicState: " + intrinsicState); - System.out.println("ExtrinsicState: " + extrinsicState); - } -} -``` - -```java -public class FlyweightFactory { - - private HashMap flyweights = new HashMap<>(); - - Flyweight getFlyweight(String intrinsicState) { - if (!flyweights.containsKey(intrinsicState)) { - Flyweight flyweight = new ConcreteFlyweight(intrinsicState); - flyweights.put(intrinsicState, flyweight); - } - return flyweights.get(intrinsicState); - } -} -``` - -```java -public class Client { - - public static void main(String[] args) { - FlyweightFactory factory = new FlyweightFactory(); - Flyweight flyweight1 = factory.getFlyweight("aa"); - Flyweight flyweight2 = factory.getFlyweight("aa"); - flyweight1.doOperation("x"); - flyweight2.doOperation("y"); - } -} -``` - -```html -Object address: 1163157884 -IntrinsicState: aa -ExtrinsicState: x -Object address: 1163157884 -IntrinsicState: aa -ExtrinsicState: y -``` - -### JDK - -Java 利用缓存来加速大量小对象的访问时间。 - -- java.lang.Integer#valueOf(int) -- java.lang.Boolean#valueOf(boolean) -- java.lang.Byte#valueOf(byte) -- java.lang.Character#valueOf(char) - -## 7. 代理(Proxy) - -### Intent - -控制对其它对象的访问。 - -### Class Diagram - -代理有以下四类: - -- 远程代理(Remote Proxy):控制对远程对象(不同地址空间)的访问,它负责将请求及其参数进行编码,并向不同地址空间中的对象发送已经编码的请求。 -- 虚拟代理(Virtual Proxy):根据需要创建开销很大的对象,它可以缓存实体的附加信息,以便延迟对它的访问,例如在网站加载一个很大图片时,不能马上完成,可以用虚拟代理缓存图片的大小信息,然后生成一张临时图片代替原始图片。 -- 保护代理(Protection Proxy):按权限控制对象的访问,它负责检查调用者是否具有实现一个请求所必须的访问权限。 -- 智能代理(Smart Reference):取代了简单的指针,它在访问对象时执行一些附加操作:记录对象的引用次数;当第一次引用一个对象时,将它装入内存;在访问一个实际对象前,检查是否已经锁定了它,以确保其它对象不能改变它。 - -

- -### Implementation - -以下是一个虚拟代理的实现,模拟了图片延迟加载的情况下使用与图片大小相等的临时内容去替换原始图片,直到图片加载完成才将图片显示出来。 - -```java -public interface Image { - void showImage(); -} -``` - -```java -public class HighResolutionImage implements Image { - - private URL imageURL; - private long startTime; - private int height; - private int width; - - public int getHeight() { - return height; - } - - public int getWidth() { - return width; - } - - public HighResolutionImage(URL imageURL) { - this.imageURL = imageURL; - this.startTime = System.currentTimeMillis(); - this.width = 600; - this.height = 600; - } - - public boolean isLoad() { - // 模拟图片加载,延迟 3s 加载完成 - long endTime = System.currentTimeMillis(); - return endTime - startTime > 3000; - } - - @Override - public void showImage() { - System.out.println("Real Image: " + imageURL); - } -} -``` - -```java -public class ImageProxy implements Image { - - private HighResolutionImage highResolutionImage; - - public ImageProxy(HighResolutionImage highResolutionImage) { - this.highResolutionImage = highResolutionImage; - } - - @Override - public void showImage() { - while (!highResolutionImage.isLoad()) { - try { - System.out.println("Temp Image: " + highResolutionImage.getWidth() + " " + highResolutionImage.getHeight()); - Thread.sleep(100); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - highResolutionImage.showImage(); - } -} -``` - -```java -public class ImageViewer { - - public static void main(String[] args) throws Exception { - String image = "http://image.jpg"; - URL url = new URL(image); - HighResolutionImage highResolutionImage = new HighResolutionImage(url); - ImageProxy imageProxy = new ImageProxy(highResolutionImage); - imageProxy.showImage(); - } -} -``` - -### JDK - -- java.lang.reflect.Proxy -- RMI - -# 参考资料 - -- 弗里曼. Head First 设计模式 [M]. 中国电力出版社, 2007. -- Gamma E. 设计模式: 可复用面向对象软件的基础 [M]. 机械工业出版社, 2007. -- Bloch J. Effective java[M]. Addison-Wesley Professional, 2017. -- [Design Patterns](http://www.oodesign.com/) -- [Design patterns implemented in Java](http://java-design-patterns.com/) -- [The breakdown of design patterns in JDK](http://www.programering.com/a/MTNxAzMwATY.html) +本文是各设计模式笔记的合并版,内容已经落后于单独的笔记,请从 [设计模式目录](设计模式%20-%20目录.md) 阅读。