荏苒追寻个人博客

做一个有追求的青年


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 日程表

未命名

发表于 2021-08-05 | 分类于 Android

本文链接:https://rainmonth.github.io/posts/D210805.html

  • 通用功能——git 攻略
  • 通用功能——Hexo搭建个人博客
  • 通用功能——Mac 常见问题及解决方案
  • 通用功能——Mac 使用技巧
  • 通用功能——Mac 使用tree打印目录结构
  • 通用功能——UML 类图箭头和线条详解

博客导航

发表于 2021-08-04 | 分类于 Android , 系统源码分析

https://rainmonth.github.io/posts/A1910110.html

Java 并发——Future详解

发表于 2021-07-22 | 分类于 Java , 并发

https://rainmonth.github.io/posts/J20210722

概念

Future接口,表示的是异步计算(异步耗时任务)的结果,该接口提供了检查异步计算是否完成、等待异步调用完成、检索异步调用结果的方法。异步调用的结果只能通过该接口的get及其重载方法来获取。但异步调用完成时,调用get可以获取到异步计算的结果,如果没有完成调用get就会阻塞直到异步调用完成,同时还提供了cancel方法来异步任务的调用,isCanceled和isDone来判断异步任务是否取消或是否完成。

Future接口设计出来的目的主要就是为了充分利用CPU,通过多线程,在一个线程执行耗时异步任务的同时,其他线程可以继续自己的工作,其他线程可以主动获取到耗时异步任务的执行结果。这里涉及到了Java多线程编程中的一个常用的多线程并行设计模式——Future模式。关于多线程并行设计模式,可以参考一下文章:

  • 并行设计模式(一)—— Future模式
  • 并行设计模式(二)— Master-Worker模式
  • 并行设计模式(三)— Guarded Suspeionsion模式
  • 并行设计模式(四)— 不变模式
  • 并行设计模式(五)— 生产者-消费者模式

主要方法

  • cancel(boolean mayInterruptIfRunning),取消异步任务的执行,mayInterruptIfRunning为true时表示会中断正在进行的任务,mayInterruptIfRunning表示不会中断正在执行的任务;
  • isCancelled(),任务是否取消;
  • isDone(),任务是否执行完成;
  • get(),获取任务的结果,如果任务尚未执行完,会阻塞直到任务执行完;
  • get(long timeout, TimeUnit unit),带超时等待的获取任务执行结果,规定时间还没有获取到结果的话,会报TimeoutException异常。

类关系

FutureTask

ForkJoinTask

Java 设计模式——迭代器模式

发表于 2021-06-23 | 分类于 Java , 设计模式

模式定义

迭代器模式(Iterator Pattern),属于行为型模式,是 Java非常常用的设计模式。这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。迭代器在Java中有专门的Iterator接口,Java集合类一般都实现了该接口,也就是说都支持通过迭代器来进行顺序元素访问。

模式示例

迭代器模式实现的关键就是迭代接口的定义了,一般需要定义以下方法:

  • hashNext(),是否存在下一个元素;
  • next(),获取下一个元素;

下面通过示例来实现迭代器模式,需要定义聚合对象,迭代器。下例主要完成对一组姓名信息的迭代输出(只为演示使用)。

定义迭代器接口

1
2
3
4
public interface Iterator {
boolean hasNext();
Object next();
}

定义Container接口,用于获取迭代器

1
2
3
public interface Container {
Iterator getIterator();
}

定义聚合对象,并实现迭代器接口

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
public class NameRepository implements Container {
public String names[] = {"Ethan", "Jason", "Karen", "Ascii"};

@Override
public Iterator getIterator() {
return new NameIterator();
}

private class NameIterator implements Iterator {

int index;

@Override
public boolean hasNext() {
if(index < names.length){
return true;
}
return false;
}

@Override
public Object next() {
if(hasNext()){
return names[index++];
}
return null;
}
}
}

编写客户端进行迭代访问

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

public static void main(String[] args) {
NameRepository namesRepository = new NameRepository();

Iterator iterator = namesRepository.getIterator();

while (iterator.hasNext()) {
String name = (String)iterator.next();
System.out.println("Name : " + name);
}
}
}

输出结果如下

1
2
3
4
Name : Ethan
Name : Jason
Name : Karen
Name : Ascii

注意:在需要对集合遍历并删除指定元素时,用迭代器可以很好的避免边移除边遍历造成的问题。

模式总结

从Java集合类大都实现了Iterator这一点可以看出,迭代器模式肯定会给设计者带来诸多好处。它主要解决对聚合对象的遍历问题,可以通过定义不同的迭代器来完成不同方式的遍历。

优缺点

  • 优点
    1. 它支持以不同的方式遍历一个聚合对象;
    2. 迭代器简化了聚合类;
    3. 在同一个聚合上可以有多个遍历;
    4. 在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。
  • 缺点
    1. 由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。

使用场景

  1. 访问一个聚合对象的内容而无须暴露它的内部表示。
  2. 需要为聚合对象提供多种遍历方式。
  3. 为遍历不同的聚合结构提供一个统一的接口。

注意事项

迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据。

Java 设计模式——解释器模式

发表于 2021-06-22 | 分类于 Java , 设计模式

模式定义

解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。

代码示例

下面这个示例(简单计算器实现),对输入的一段文本(表达式),这里主要支持数字、变量和加法和乘法

  1. 先根据定义的文本规则将其符号化,得到一个符号化列表,同时在最后面加上终止符号,然后返回符号化列表,
  2. 然后根据规则取出token,将其组合解释成表达式。具体规则如下:
    1. 开头必须是变量或者数字;
    2. 先处理乘法在处理加法
  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
/**
* 定义符号(空格、数字、加、乘、变量、终止符)
*/
public class Token {
public enum TokenType {
SPACE,
NUMBER,
ADD,
MULTIPLY,
VARIABLE,
EPSILON, // The end of the entire expression.
}

private final TokenType type;
private final String string;

public Token(TokenType type, String string) {
this.type = type;
this.string = string;
}

@Override
public String toString() {
return String.format("(%s, \"%s\")", type, string);
}

public TokenType getType() {
return type;
}

public String getString() {
return string;
}

public double getAsNumber() throws InterpreterException {
if (type != TokenType.NUMBER) {
throw new InterpreterException("Incorrect token type. ");
}
return Double.valueOf(string);
}
}
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
/**
* 将输入内容按规则解析成Token列表
*/
public class Tokenizer {
private static class TokenRule {
private final Pattern pattern;
private final TokenType type;

public TokenRule(String pattern, TokenType type) {
this.pattern = Pattern.compile(pattern);
this.type = type;
}

public Pattern getPattern() {
return pattern;
}

public TokenType getType() {
return type;
}
}

private static final List<TokenRule> rules = new ArrayList<TokenRule>();

static {
rules.add(new TokenRule("^\\d+(\\.\\d+)?", TokenType.NUMBER));
rules.add(new TokenRule("^[ \t\n\r]+", TokenType.SPACE));
rules.add(new TokenRule("^\\+", TokenType.ADD));
rules.add(new TokenRule("^\\*", TokenType.MULTIPLY));
rules.add(new TokenRule("^[A-Za-z][A-Za-z0-9]*", TokenType.VARIABLE));
}

public static List<Token> tokenize(String exprStr) throws InterpreterException {
Tokenizer tokenizer = new Tokenizer(exprStr);
return tokenizer.tokenize();
}

private final String exprStr;

private Tokenizer(String exprStr) {
this.exprStr = exprStr;
}

private List<Token> tokenize() throws InterpreterException {
List<Token> tokens = new ArrayList<Token>();
int pos = 0;
int len = exprStr.length();
while (pos < len) {
CharSequence sequence = exprStr.subSequence(pos, len);
boolean matched = false;
for (TokenRule rule : rules) {
Matcher matcher = rule.getPattern().matcher(sequence);
if (!matcher.find()) {
continue;
}
matched = true;
pos += matcher.end();
TokenType type = rule.getType();
if (type != TokenType.SPACE) {
Token token = new Token(type, matcher.group());
tokens.add(token);
}
break;
}
if (!matched) {
throw new InterpreterException("Tokenizer error at: \"" + sequence + "\"");
}
}
tokens.add(new Token(TokenType.EPSILON, ""));
return tokens;
}
}
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
92
93
94
95
96
97
98
/**
* 定义词法解析器,根据规则将输入的Tokens转化成待解释的表达式
*/
public class Parser {
private final List<Token> tokens;
int next = 0;

public static Expression parse(List<Token> tokens) throws InterpreterException {
return new Parser(tokens).parse();
}

private Parser(List<Token> tokens) {
this.tokens = tokens;
}

private Expression parse() throws InterpreterException {
next = 0;
Expression expression = expression();
except(TokenType.EPSILON);
return expression;
}

private Expression expression() throws InterpreterException {
Expression expression = term();// 先乘法
while (accept(TokenType.ADD)) {// 在加法
expression = new AddExpression(expression, term());
}
return expression;

}

private Expression term() throws InterpreterException {
Expression expression = factor();
while (accept(TokenType.MULTIPLY)) {
expression = new MultiplyExpression(expression, term());
}
return expression;
}

/**
* 获取 常量或变量表达式
* @return 常量或变量表达式
* @throws InterpreterException 不是商量两种类型就抛异常
*/
private Expression factor() throws InterpreterException {
if (accept(TokenType.NUMBER)) {
return new NumberExpression(current().getAsNumber());
}
except(TokenType.VARIABLE);
return new VariableExpression(current().getString());
}

/**
* 获取指定类型Token
* @param tokenType 获取的token类型
* @return 如果是期待的类型,返回true,同时更新next指针;不是则返回false;
*/
private boolean accept(TokenType tokenType) {
if (tokens.get(next).getType() == tokenType) {
++next;
return true;
} else {
return false;
}
}

/**
* 需要的类型
* @param tokenType 期待的类型
* @throws InterpreterException 不是期待的token类型则抛出异常
*/
private void except(TokenType tokenType) throws InterpreterException {
if (!accept(tokenType)) {
throw new InterpreterException(
"Parser error. Unexcepted token at: \""
+ getRemainingString() + "\"");
}
}

/**
* 当前处理的token
* @return 当前处理的token
*/
private Token current() {
return tokens.get(next - 1);
}

private String getRemainingString() {
StringBuffer buffer = new StringBuffer();
for (int i = next; i < tokens.size(); ++i) {
if (i != next)
buffer.append(" ");
buffer.append(tokens.get(i).getString());
}
return buffer.toString();
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 解释器中心,客户端通过该类进行输入文本的解释,不需要关心具体解释器的实现(类似于中介者)
*/
public class Interpreter {

private final List<Token> tokens;
private final Expression rootExpression;

public Interpreter(String expressionString) throws InterpreterException {
tokens = Tokenizer.tokenize(expressionString);
rootExpression = Parser.parse(tokens);
}

public List<Token> getTokens() {
return tokens;
}

public Expression getRootExpression() {
return rootExpression;
}

}

一下部分开始定义表达式

先是接口

1
2
3
4
5
6
/**
* 定义解释器(表达式)接口
*/
public interface Expression {
double interpret(Map<String, Double> context) throws InterpreterException;
}

然后是四种类型的解释器

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 AddExpression implements Expression {

private final Expression leftExpression;
private final Expression rightExpression;

public AddExpression(Expression leftExpression, Expression rightExpression) {
this.leftExpression = leftExpression;
this.rightExpression = rightExpression;
}

@Override
public double interpret(Map<String, Double> context) throws InterpreterException {
return leftExpression.interpret(context) + rightExpression.interpret(context);
}

@Override
public String toString() {
return String.format("(%s + %s)", leftExpression, rightExpression);
}

}
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 MultiplyExpression implements Expression {

private final Expression leftExpression;
private final Expression rightExpression;

public MultiplyExpression(Expression leftExpression, Expression rightExpression) {
this.leftExpression = leftExpression;
this.rightExpression = rightExpression;
}

@Override
public double interpret(Map<String, Double> context) throws InterpreterException {
return leftExpression.interpret(context) * rightExpression.interpret(context);
}

@Override
public String toString() {
return String.format("(%s * %s)", leftExpression, rightExpression);
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 数字解释器
*/
public class NumberExpression implements Expression {

private final double value;

public NumberExpression(double value) {
this.value = value;
}

@Override
public double interpret(Map<String, Double> context) {
return value;
}

@Override
public String toString() {
return String.valueOf(value);
}

}
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 VariableExpression implements Expression {

private final String name;

public VariableExpression(String name) {
this.name = name;
}

@Override
public double interpret(Map<String, Double> context) throws InterpreterException {
if (context.containsKey(name)) {
return context.get(name);
} else {
throw new InterpreterException("Variable not found: " + name);
}
}

@Override
public String toString() {
return name;
}

}

由于可能会存在解释异常,所以定义了一个异常类

1
2
3
4
5
6
7
public class InterpreterException extends Exception {
private static final long serialVersionUID = 8459803686326291973L;

public InterpreterException(String message) {
super(message);
}
}

编写客户端运行

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 Main {

static final boolean VERBOSE_PARSE = true;
static final boolean VERBOSE_DEBUG = true;
static final double EPS = 1e-7;
static final Map<String, Double> context = new HashMap<String, Double>();

static {
context.put("age", 18.0);
context.put("height", 170.0);
}

private static void execute(String expressionString, double excepted) {
System.out.println("Attempting to evaluate: " + expressionString);
try {
Interpreter interpreter = new Interpreter(expressionString);
if (VERBOSE_PARSE) {
printTokens(interpreter.getTokens());
System.out.println("Parse tree: "
+ interpreter.getRootExpression());
}
double value = interpreter.getRootExpression().interpret(context);
boolean correct = Math.abs(value - excepted) < EPS;
System.out.println(String.format(
"* %s!! Excepted: %.4f, evaluated: %.4f",
correct ? "Correct" : "Incorrect", excepted, value));
} catch (InterpreterException e) {
System.out.println("Error: " + e);
if (VERBOSE_DEBUG) {
e.printStackTrace();
}
}
}

private static void printTokens(List<Token> tokens) {
StringBuffer buffer = new StringBuffer("Tokens: ");
for (Token token : tokens) {
buffer.append(token);
}
System.out.println(buffer);
}

public static void main(String[] args) {
// execute("1.5", 1.5);
// execute("1 + 2 + 3 + 4 + 5", 1 + 2 + 3 + 4 + 5);
// execute("2 * 3 + 5 * 7", 2 * 3 + 5 * 7);
execute("age * 10 + height * 1.5", get("age") * 10 + get("height") * 1.5);
}

public static double get(String key) {
return context.get(key);
}
}

输出结果

1
2
3
4
Attempting to evaluate: age * 10 + height * 1.5
Tokens: (VARIABLE, "age")(MULTIPLY, "*")(NUMBER, "10")(ADD, "+")(VARIABLE, "height")(MULTIPLY, "*")(NUMBER, "1.5")(EPSILON, "")
Parse tree: ((age * 10.0) + (height * 1.5))
* Correct!! Excepted: 435.0000, evaluated: 435.0000

从代码量来看,这个示例还是比较复杂的,由此可见编译器实现的复杂程度。

模式总结

对于一些固定文法构建一个解释句子的解释器。如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。注意由于解释器模式实现方式通常会或多或少的存在使用迭代(或递归)的场景,所以一定要做好终止条件的判断。

优缺点

  • 优点

    1. 可扩展性比较好,灵活。
    2. 增加了新的解释表达式的方式。
    3. 易于实现简单文法。
  • 缺点

    1. 可利用场景比较少;
    2. 对于复杂的文法比较难维护;
    3. 解释器模式会引起类膨胀;
    4. 解释器模式采用递归调用方法;

使用场景

通常使用在编译器、运算表达式计算中,另外Xml解析、Json解析都或多或少体现了解释器模式的应用。

  1. 可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。
  2. 一些重复出现的问题可以用一种简单的语言来进行表达。
  3. 一个简单语法需要解释的场景。

注意事项

  1. 需要注意构建语法树,定义终结符与非终结符的方式。
  2. 可利用场景比较少,JAVA 中如果碰到可以用 expression4J 代替。
  3. 构建环境类来描述解释器运行的所需的环境,包含解释器之外的一些全局信息,一般是 HashMap。

Java 设计模式——状态模式

发表于 2021-06-21 | 分类于 Java , 设计模式

模式定义

在状态模式(State Pattern)中,类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式。

模式示例

状态模式需要定义表示各种状态的对象,同时还要有一个行为随着状态对象改变而改变的 Context对象,这个Context对象通常会关联一个状态,通过这个Context,可已调用某个状态对象具体的执行方法。下面通过电视机遥控器这个case来描述状态模式。

电视机遥控器可以控制电视机的开(Start)和关(Stop)两种状态,我们需要知道遥控器需要切换到那个状态,这里用 TvContext,这个TvContext包含了一个状态对象和一个切换方法,这个方法实际上调用的这个状态对象来处理的。

  • 定义状态接口,State;
  • 实现状态接口,TvStartState,TvStopState;
  • 定义一个类表示当前的所处的环境,TvContext;

定义状态接口

1
2
3
4
5
6
/**
* 定义状态接口
*/
public interface State {
void doAction();
}

实现状态接口

1
2
3
4
5
6
7
8
9
/**
* 开始状态
*/
public class TVStartState implements State {
@Override
public void doAction() {
System.out.println("TV is turned ON");
}
}
1
2
3
4
5
6
7
8
9
/**
* 暂停状态
*/
public class TVStopState implements State {
@Override
public void doAction() {
System.out.println("TV is turned OFF");
}
}

实现上下文对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 持有状态引用的上下文环境
* 通过它来响应不同状态的不同行为,实际上调用的时该状态所对应的行为
*/
public class TVContext {
private State state;

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

public void doAction() {
this.state.doAction();
}
}

编写控制终端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 状态模式Client
*/
public class TVRemote {
public static void main(String[] args) {

TVContext context = new TVContext();

context.setState(new TVStartState());
context.doAction();

context.setState(new TVStopState());
context.doAction();

}
}

运行结果

1
2
TV is turned ON
TV is turned OFF

模式总结

状态模式通常应用在对象行为随状态改变的情形下,当然也可以用状态模式来消除if...else判断。状态模式和命令模式有很多相似之处:

  1. 通常命令模式的接口中只有一个方法。而状态模式的接口中有一个或者多个方法。
  2. 状态模式的实现类的方法,一般返回值,或者是改变实例变量的值。也就是说,状态模式一般和对象的状态有关。实现类的方法有不同的功能,覆盖接口中的方法。
  3. 状态模式和命令模式一样,也可以用于消除 if…else 等条件选择语句。

优缺点

  • 优点
    1. 封装了转换规则。
    2. 枚举可能的状态,在枚举状态之前需要确定状态种类。
    3. 将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
    4. 允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。
    5. 可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。
  • 缺点
    1. 状态模式的使用必然会增加系统类和对象的个数。
    2. 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
    3. 状态模式对”开闭原则”的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。

使用场景

对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。所以当代码中含大量与对象状态有关的条件语句时可以考虑使用状态模式来实现。

  1. 行为随状态改变而改变的场景。
  2. 条件、分支语句的代替者。

注意事项

在行为受状态约束的时候使用状态模式,而且状态不超过 5 个。

介绍

意图:允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。

主要解决:对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。

何时使用:代码中包含大量与对象状态有关的条件语句。

如何解决:将各种具体的状态类抽象出来。

关键代码:通常命令模式的接口中只有一个方法。而状态模式的接口中有一个或者多个方法。而且,状态模式的实现类的方法,一般返回值,或者是改变实例变量的值。也就是说,状态模式一般和对象的状态有关。实现类的方法有不同的功能,覆盖接口中的方法。状态模式和命令模式一样,也可以用于消除 if…else 等条件选择语句。

Java 设计模式——访问者模式

发表于 2021-06-20 | 分类于 Java , 设计模式

模式定义

在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。

模式示例

下面以电脑启动时对各个组成部分的访问为例来说明访问者模式。主要以下几个步骤

  1. 定义元素接口,被访问者需要实现这个接口;
  2. 定义访问者接口,访问者需要实现该接口;
  3. 具体实现被访问者类和访问者类
  4. 编写客户端验证

定义元素接口

1
2
3
4
5
6
7
8
/**
* 定义元素接口
*/
public interface ComputerPart {
void accept(ComputerPartVisitor computerPartVisitor);

void accept(UserInputVisitor userInputVisitor);
}

定义访问者接口

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 定义访问者接口
*/
public interface ComputerPartVisitor {
void visit(Computer computer);

void visit(Mouse mouse);

void visit(Keyboard keyboard);

void visit(Monitor monitor);
}

这里给访问者传递的都是具体的类,如果被访问者新增元素,需要增加相应的方法,同时访问者类的实现者需要实现该方法,违反了迪米特原则。

具体实现被访问者和访问者

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 Computer implements ComputerPart {

ComputerPart[] parts;

public Computer(){
parts = new ComputerPart[] {new Mouse(), new Keyboard(), new Monitor()};
}

@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
for (int i = 0; i < parts.length; i++) {
parts[i].accept(computerPartVisitor);
}
computerPartVisitor.visit(this);
}

@Override
public void accept(UserInputVisitor userInputVisitor) {
for (int i = 0; i < parts.length; i++) {
parts[i].accept(userInputVisitor);
}
userInputVisitor.visit(this);
}
}

这是元素对象,本身可被访问,同时其组成部分也可被访问。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 键盘
*/
public class Keyboard implements ComputerPart {

@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
computerPartVisitor.visit(this);
}

@Override
public void accept(UserInputVisitor userInputVisitor) {
userInputVisitor.visit(this);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 显示器
*/
public class Monitor implements ComputerPart {

@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
computerPartVisitor.visit(this);
}

@Override
public void accept(UserInputVisitor userInputVisitor) {
userInputVisitor.visit(this);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 鼠标
*/
public class Mouse implements ComputerPart {

@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
computerPartVisitor.visit(this);
}

@Override
public void accept(UserInputVisitor userInputVisitor) {
userInputVisitor.visit(this);
}
}

定义访问者,先看整体访问者

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 ComputerPartDisplayVisitor implements ComputerPartVisitor {

@Override
public void visit(Computer computer) {
System.out.println("Displaying Computer.");
}

@Override
public void visit(Mouse mouse) {
System.out.println("Displaying Mouse.");
}

@Override
public void visit(Keyboard keyboard) {
System.out.println("Displaying Keyboard.");
}

@Override
public void visit(Monitor monitor) {
System.out.println("Displaying Monitor.");
}
}

输入设备访问者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 输入设备访问者
*/
public class UserInputVisitor implements ComputerPartVisitor {

@Override
public void visit(Computer computer) {}

@Override
public void visit(Mouse mouse) {
System.out.println("Displaying Mouse.");
}

@Override
public void visit(Keyboard keyboard) {
System.out.println("Displaying Keyboard.");
}

@Override
public void visit(Monitor monitor) {}
}

用来验证的客户端

1
2
3
4
5
6
7
8
9
10
11
12
public class VisitorPatternDemo {
public static void main(String[] args) {

ComputerPart computer = new Computer();

System.out.println("== Show all computer part: ==");
computer.accept(new ComputerPartDisplayVisitor());

System.out.println("\n== Show user input part: ==");
computer.accept(new UserInputVisitor());
}
}

输出结果:

1
2
3
4
5
6
7
8
9
== Show all computer part: ==
Displaying Mouse.
Displaying Keyboard.
Displaying Monitor.
Displaying Computer.

== Show user input part: ==
Displaying Mouse.
Displaying Keyboard.

可以看到,访问者还是可能会有一些方法的冗余的,比如UserInputVisitor,不需要访问Monitor和Computer但却不得不实现访问者接口定义的方法(空实现)

模式总结

使用访问者模式时,通常这个类(元素类)的结构(数据结构)比较固定,主要解决稳定的数据结构和易变的操作耦合问题。

优缺点

  • 优点
    1. 符合单一职责原则。
    2. 优秀的扩展性。
    3. 灵活性。
  • 缺点
    • 具体元素对访问者公布细节,违反了迪米特原则(一个对象应该尽可能的减少对其他元素的了解)。
    • 具体元素变更比较困难。
    • 违反了依赖倒置原则,依赖了具体类,没有依赖抽象。

使用场景

对固定多部分组成对象元素进行不同的操作时可以用到该模式。如电脑的结构固定,我可以定义一个访问者只访问输入设备,也可以定义一个访问者访问显示设备。

  1. 对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。
  2. 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作”污染”这些对象的类,也不希望在增加新操作时修改这些类。

注意事项

  1. 如果元素经常变动,结构不固定,就不适合使用访问者模式了,因为该模式增加新元素比较困难;
  2. 访问者可以对功能进行统一,可以做报表、UI、拦截器与过滤器。
  3. 在数据基础类里面有一个方法接受访问者,将自身引用传入访问者,已让访问者访问。
  4. 在被访问的类里面加一个对外提供接待访问者的接口。

Java设计模式——观察者模式

发表于 2021-06-18 | 分类于 Java , 设计模式

模式定义

当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。

模式示例

Java本身对观察者模式的实现有良好的支持,被观察者类只要继承java.util.Observable即可,观察者类只要实现java.util.Observer接口即可。下面写一个Demo来描述观察者模式,这个示例如下,一个新闻事件中心,可以能会发生各种事件,有几家报社在新闻事件中心发生事件后,会根据事件做相应的报道,这是典型的一对多关系。

  • 定义被观察者接口,新闻中心实现被观察者接口,支持事件通知、添加或移除报社(观察者)操作;
  • 定义观察者接口,报社要实现该接口,当事件发生时,可以对事件进行响应。
  • 定义客户端来演示

定义被观察者

1
2
3
4
5
6
7
8
9
10
/**
* 被观察者接口
*/
public interface Subject {
void registerObserver(Observer object);

void removeObserver(Observer object);

void notifyObservers();
}

定义观察者接口

1
2
3
4
5
6
/**
* 观察者接口
*/
public interface Observer {
void update(String headlineNews, String sportsNews, String financeNews, String entertainmentNews);
}

定义真正被观察者NewsData,实现Subject接口

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
/**
* 真正的被观察者
*/
public class NewsData implements Subject {
private String headlineNews;
private String sportsNews;
private String financeNews;
private String entertainmentNews;

private List observerArrayList;

public NewsData() {
observerArrayList = new ArrayList();
}

@Override
public void registerObserver(Observer object) {
observerArrayList.add(object);
}

@Override
public void removeObserver(Observer object) {
if (observerArrayList.contains(object)) {
observerArrayList.remove(object);
}
}

@Override
public void notifyObservers() {
for (int i = 0; i < observerArrayList.size(); i++) {
((Observer) observerArrayList.get(i)).update(headlineNews, sportsNews, financeNews, entertainmentNews);
}
}

public void updateNews() {
notifyObservers();
}

public String getHeadlineNews() {
return headlineNews;
}

public String getSportsNews() {
return sportsNews;
}

public void setFinanceNews(String financeNews) {
this.financeNews = financeNews;
}

public void setEntertainmentNews(String entertainmentNews) {
this.entertainmentNews = entertainmentNews;
}

public void setHeadlineNews(String headlineNews) {
this.headlineNews = headlineNews;
}

public void setSportsNews(String sportsNews) {
this.sportsNews = sportsNews;
}

public String getFinanceNews() {
return financeNews;
}

public String getEntertainmentNews() {
return entertainmentNews;
}
}

定义四家报社(真正的观察者)这四家报社对新闻的关注点不同,可以选择性的报道:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 关注头条、体育和娱乐新闻,不关注财经新闻
*/
public class CccNewsDisplay implements Observer {
private String headlineNews;
private String sportsNews;
private String entertainmentNews;

@Override
public void update(String headlineNews, String sportsNews, String financeNews, String entertainmentNews) {
this.headlineNews = headlineNews;
this.sportsNews = sportsNews;
this.entertainmentNews = entertainmentNews;
display();
}

public void display() {
System.out.println("Ccc headlineNews: " + headlineNews);
System.out.println("Ccc sportsNews: " + sportsNews);
System.out.println("Ccc entertainmentNews: " + entertainmentNews);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 关注头条、体育和财经新闻,不关注娱乐新闻
*/
public class DnnNewsDisplay implements Observer {
private String headlineNews;
private String sportsNews;
private String financeNews;

@Override
public void update(String headlineNews, String sportsNews, String financeNews, String entertainmentNews) {
this.headlineNews = headlineNews;
this.sportsNews = sportsNews;
this.financeNews = financeNews;
display();
}

public void display() {
System.out.println("Dnn headlineNews: " + headlineNews);
System.out.println("Dnn sportsNews: " + sportsNews);
System.out.println("Dnn finaceNews: " + financeNews);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 只关注娱乐新闻
*/
public class GuavaDailyNewsDisplay implements Observer {
private String entertainmentNews;

@Override
public void update(String headlineNews, String sportsNews, String financeNews, String entertainmentNews) {
this.entertainmentNews = entertainmentNews;
display();
}

public void display() {
System.out.println("GuavaDaily entertainmentNews: " + entertainmentNews);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 关注体育和头条
*/
public class YahuNewsDisplay implements Observer {
private String headlineNews;
private String sportsNews;

@Override
public void update(String headlineNews, String sportsNews, String financeNews, String entertainmentNews) {
this.headlineNews = headlineNews;
this.sportsNews = sportsNews;
display();
}

public void display() {
System.out.println("Yahu headlineNews: " + headlineNews);
System.out.println("Yahu sportsNews: " + sportsNews);
}
}

定义客户端,进行新闻播报

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 NewsStation {

public static void main(String[] args) {
NewsData newsData = new NewsData();
CccNewsDisplay cccNewsDisplay = new CccNewsDisplay();
DnnNewsDisplay dnnNewsDisplay = new DnnNewsDisplay();
GuavaDailyNewsDisplay guavaDailyNewsDisplay = new GuavaDailyNewsDisplay();
YahuNewsDisplay yahuNewsDisplay = new YahuNewsDisplay();

newsData.registerObserver(cccNewsDisplay);
newsData.registerObserver(dnnNewsDisplay);
newsData.registerObserver(guavaDailyNewsDisplay);
newsData.registerObserver(yahuNewsDisplay);

newsData.setHeadlineNews("'Big objects found' in AirAsia hunt");
newsData.setFinanceNews("US shares barely budge in 2015");
newsData.setEntertainmentNews("Bono guitar fears after bike crash");
newsData.setSportsNews("World Tennis Championship: Andy Murray beats Rafael Nadal");

newsData.updateNews();

}

}

输出结果

1
2
3
4
5
6
7
8
9
Ccc headlineNews: 'Big objects found' in AirAsia hunt
Ccc sportsNews: World Tennis Championship: Andy Murray beats Rafael Nadal
Ccc entertainmentNews: Bono guitar fears after bike crash
Dnn headlineNews: 'Big objects found' in AirAsia hunt
Dnn sportsNews: World Tennis Championship: Andy Murray beats Rafael Nadal
Dnn finaceNews: US shares barely budge in 2015
GuavaDaily entertainmentNews: Bono guitar fears after bike crash
Yahu headlineNews: 'Big objects found' in AirAsia hunt
Yahu sportsNews: World Tennis Championship: Andy Murray beats Rafael Nadal

模式总结

一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。观察者和被观察者是抽象耦合的,被观察者持有一个观察者列表,在自身发生变化时,遍历该列表逐一通知。

观察者模式主要解决,一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。

优缺点

  • 优点
    1. 观察者和被观察者是抽象耦合的。
    2. 建立一套触发机制。
  • 缺点
    1. 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间;
    2. 可能产生循环依赖的问题,如观察者和被观察者间可能存在循环依赖的问题(EventBus使用时就会产生这种问题)
    3. 观察者模式只能告诉观察者被观察者发生了变化,但不能说明具体发生了什么变化。

使用场景

  • 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
  • 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
  • 一个对象必须通知其他对象,而并不知道这些对象是谁。
  • 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。

注意事项

  1. JAVA 中已经有了对观察者模式的支持类。
  2. 避免循环引用。
  3. 如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。

Java 设计模式14——命令模式

发表于 2021-06-13 | 分类于 Java , 设计模式

模式定义

命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。模式的核心思想将一个请求封装成一个对象,从而可以用不同的请求对客户进行参数化。

实现

定义三个角色:

  1. RealCommandExecutor, 真正的命令执行对象;
  2. Command,封装了具体命令执行者的命令对象
  3. Invoke,使用命令对象的入口

模式示例

举一个例子,一个控制终端,可以控制5种不同的角色进行攻击,这5种角色就是上面的真正命令执行对象,5中角色分别对应5个Command,调用者可以通过控制终端让执行不同的命令以让不同角色进行攻击。下面看具体代码。

1
2
3
4
5
6
public interface Command {
/**
* 攻击命令
*/
void attack();
}

定义5中不同的具体命令执行对象。

1
2
3
4
5
6
7
8
9
/**
* 术士
*/
public class Wizard {
public void spell() {
System.out.println("wizard spell");
}

}
1
2
3
4
5
6
7
8
/**
* 武士
*/
public class Warrior {
public void attack() {
System.out.println("warrior attack!");
}
}
1
2
3
4
5
6
7
8
/**
* 骑士
*/
public class Knight {
public void rideHorseAndAttack(){
System.out.println("knight ride hourse and attack!");
}
}
1
2
3
4
5
6
7
8
/**
* 哥布林(传说中的类人生物)
*/
public class Goblin {
public void attack() {
System.out.println("goblin attack!");
}
}
1
2
3
4
5
6
7
8
9
10
11
/**
* 棒球运动员
*/
public class BaseballPlayer {
/**
* 挥动球棒
*/
public void swingBat(){
System.out.println("baseball player swing bat!");
}
}

将不同角色的攻击请求封装成Command命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 术士对应的命令
*/
public class WizardCommand implements Command {

private Wizard wizard;

public WizardCommand(Wizard wizard) {
this.wizard = wizard;
}

@Override
public void attack() {
wizard.spell();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 武士对应的命令
*/
public class WarriorCommand implements Command {

private Warrior warrior;

public WarriorCommand(Warrior warrior) {
this.warrior = warrior;
}

@Override
public void attack() {
warrior.attack();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 骑士对应的命令
*/
public class KnightCommand implements Command{

private Knight knight;

public KnightCommand(Knight knight) {
this.knight = knight;
}

@Override
public void attack() {
knight.rideHorseAndAttack();
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 哥布林对应的命令
*/
public class GoblinCommand implements Command {

private Goblin goblin;

public GoblinCommand(Goblin goblin) {
this.goblin = goblin;
}

@Override
public void attack() {
goblin.attack();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 棒球运动员对应的命令
*/
public class BaseballPlayerCommand implements Command{

private BaseballPlayer baseballPlayer;

public BaseballPlayerCommand(BaseballPlayer baseballPlayer){
this.baseballPlayer = baseballPlayer;
}

@Override
public void attack() {
baseballPlayer.swingBat();
}
}

定义一个远程控制终端

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
/**
* 控制终端
*/
public class SimpleRemoteControl {
private final List<Command> commands;

public SimpleRemoteControl() {
commands = new ArrayList<Command>();
}

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

public void buttonWasPressed(int index) {
commands.get(index).attack();
}

public int getCommandsSize() {
return commands.size();
}

@Override
public String toString() {

StringBuffer stringBuff = new StringBuffer();
stringBuff.append("\n------------Remote Control------------\n");

for (int i = 0; i < commands.size(); i++) {
stringBuff.append("[slot " + i + "] " + commands.get(i).getClass().getName() + "\n");
}
return stringBuff.toString();
}
}

编写客户端运行

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

public static void main(String[] args) {
SimpleRemoteControl remoteControl = new SimpleRemoteControl();

Knight knight = new Knight();
Wizard wizard = new Wizard();
Warrior warrior = new Warrior();
Goblin goblin = new Goblin();
BaseballPlayer baseballPlayer = new BaseballPlayer();
// SonGoku sonGoku = new SonGoku();

Command knightCommand = new KnightCommand(knight);
Command wizardCommand = new WizardCommand(wizard);
Command warriorCommand = new WarriorCommand(warrior);
Command goblinCommand = new GoblinCommand(goblin);
Command baseballPlayerCommand = new BaseballPlayerCommand(baseballPlayer);
// Command sonGoKuCommand = new SonGokuCommand(sonGoku);

// List<Command> commandList= new ArrayList<Command>();
// commandList.add(sonGoKuCommand);
// commandList.add(baseballPlayerCommand);
// Command mircoCommand = new MircoCommand(commandList);

remoteControl.addCommand(knightCommand);
remoteControl.addCommand(wizardCommand);
remoteControl.addCommand(warriorCommand);
remoteControl.addCommand(goblinCommand);
remoteControl.addCommand(baseballPlayerCommand);
// remoteControl.addCommand(sonGoKuCommand);
// remoteControl.addCommand(mircoCommand);

System.out.println(remoteControl);

for (int i = 0; i < remoteControl.getCommandsSize(); i++) {
System.out.println("-------------"+i+"----------------");
remoteControl.buttonWasPressed(i);
}

}
}

输出结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
------------Remote Control------------
[slot 0] com.rainmonth.pattern.behavioral.command.command.KnightCommand
[slot 1] com.rainmonth.pattern.behavioral.command.command.WizardCommand
[slot 2] com.rainmonth.pattern.behavioral.command.command.WarriorCommand
[slot 3] com.rainmonth.pattern.behavioral.command.command.GoblinCommand
[slot 4] com.rainmonth.pattern.behavioral.command.command.BaseballPlayerCommand

-------------0----------------
knight ride hourse and attack!
-------------1----------------
wizard spell
-------------2----------------
warrior attack!
-------------3----------------
goblin attack!
-------------4----------------
baseball player swing bat!

从上例可以看出,我们可以很容易的新增一种角色,然后定义该角色的攻击方式,可以使单次攻击(attack里面调用一个攻击方法),也可以是组合攻击(attack里面调用两个攻击方法),还可以利用组合模式,将一组命令封装成Command对象来执行。当然前提是要有相应的角色,要有相应的命令,也就是说要有相应的类。这就可能造成命令泛滥的情形,需要加以控制。

模式总结

优缺点

  • 优点
    1. 降低了系统耦合度;
    2. 新的命令可以很容易添加到系统中去。
  • 缺点
    1. 使用命令模式可能会导致某些系统有过多的具体命令类;

使用场景

认为是命令的地方都可以使用命令模式,比如: 1、GUI 中每一个按钮都是一条命令。 2、模拟 CMD。

注意事项

系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作,也可以考虑使用命令模式。关于命令模式的扩展,可以参考这篇文章。

  • [设计模式(一):命令模式(2)——命令模式扩展之宏命令]
  • 设计模式(一):命令模式(3)——命令模式扩展之队列请求
  • 设计模式(一):命令模式(4)——命令模式扩展之日志请求

Java 设计模式11——享元模式

发表于 2021-06-12 | 分类于 Java , 设计模式

享元模式

享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式。

实现方式

  1. 采用HashMap或者其他支持KV访问的数据结构来存储这些创建的对象;
  2. 用唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的对象;

代码示例

享元模式尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象。我们将通过创建 5 个对象来画出 20 个分布于不同位置的圆来演示这种模式。由于只有 5 种可用的颜色,所以 color 属性被用来检查现有的 Circle 对象。具体步骤如下:

  1. 定义Shape接口;
  2. 定义Circle对象;
  3. 定义产生Circle对象的工厂;
  4. 示例运行
1
2
3
public interface Shape {
void draw();
}
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
public class Circle implements Shape {
private String color;
private int x;
private int y;
private int radius;

public Circle(String color) {
this.color = color;
}

// 这里注释掉setColor方法,不然外界拿到对象后可以调用该方法来改变Circle颜色
// public void setColor(String color) {
// this.color = color;
// }

public void setX(int x) {
this.x = x;
}

public void setY(int y) {
this.y = y;
}

public void setRadius(int radius) {
this.radius = radius;
}

@Override
public void draw() {
System.out.println("Circle: draw()[" + "color=" + color + ", x=" + x + ", y=" + y + ", radius=" + radius + ']');
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ShapeFactory {
private static final HashMap<String, Shape> circleMap = new HashMap<>();

public static Shape getCircle(String color) {
Circle circle = (Circle) circleMap.get(color);

if (circle == null) {
circle = new Circle(color);
circleMap.put(color, circle);
System.out.println("----------------------------------");
System.out.println("create circle of color:" + color);
System.out.println("----------------------------------");
}
return circle;
}
}
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 FlyWeightDemo {
private static final String[] colors = new String[]{"Red", "Green", "Blue", "Black", "Purple"};

public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
Circle circle = (Circle) ShapeFactory.getCircle(getRandomColor());
circle.setX(getRandomX());
circle.setY(getRandomY());
circle.setRadius(100);
circle.draw();
}
}

private static String getRandomColor() {
return colors[(int) (Math.random() * colors.length)];
}

private static int getRandomX() {
return (int) (Math.random() * 100);
}

private static int getRandomY() {
return (int) (Math.random() * 100);
}
}

输出结果如下:

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
----------------------------------
create circle of color:Purple
----------------------------------
Circle: draw()[color=Purple, x=70, y=24, radius=100]
----------------------------------
create circle of color:Green
----------------------------------
Circle: draw()[color=Green, x=47, y=38, radius=100]
----------------------------------
create circle of color:Red
----------------------------------
Circle: draw()[color=Red, x=75, y=7, radius=100]
Circle: draw()[color=Red, x=52, y=73, radius=100]
----------------------------------
create circle of color:Black
----------------------------------
Circle: draw()[color=Black, x=74, y=6, radius=100]
----------------------------------
create circle of color:Blue
----------------------------------
Circle: draw()[color=Blue, x=30, y=31, radius=100]
Circle: draw()[color=Black, x=64, y=81, radius=100]
Circle: draw()[color=Green, x=17, y=98, radius=100]
Circle: draw()[color=Green, x=50, y=77, radius=100]
Circle: draw()[color=Blue, x=68, y=88, radius=100]
Circle: draw()[color=Red, x=56, y=90, radius=100]
Circle: draw()[color=Black, x=3, y=46, radius=100]
Circle: draw()[color=Black, x=48, y=51, radius=100]
Circle: draw()[color=Black, x=57, y=45, radius=100]
Circle: draw()[color=Green, x=71, y=20, radius=100]
Circle: draw()[color=Blue, x=94, y=85, radius=100]
Circle: draw()[color=Purple, x=33, y=91, radius=100]
Circle: draw()[color=Purple, x=55, y=46, radius=100]
Circle: draw()[color=Green, x=36, y=52, radius=100]
Circle: draw()[color=Black, x=48, y=72, radius=100]

模式总结

享元模式主要运用共享技术有效地支持大量细粒度的对象。在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建。

优缺点

  • 优点:大大减少对象的创建,降低系统的内存,使效率提高。

  • 缺点:提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱。

使用场景

  1. 系统中有大量对象。
  2. 这些对象消耗大量内存。
  3. 这些对象的状态大部分可以外部化。
  4. 这些对象可以按照内蕴状态分为很多组,当把外蕴对象从对象中剔除出来时,每一组对象都可以用一个对象来代替
  5. 系统不依赖于这些对象身份,这些对象是不可分辨的。

应用实例

  1. JAVA 中的 String,如果有则返回,如果没有则创建一个字符串保存在字符串缓存池里面。
  2. 数据库的数据池。

注意事项

  1. 注意划分外部状态和内部状态,否则可能会引起线程安全问题。 示例中color对一个Circle来说是不变的,属于内部状态,而x、y、radius则属于外部状态,可以又外界直接改变。
  2. 这些类必须有一个工厂对象加以控制。
<i class="fa fa-angle-left" aria-label="上一页"></i>1…91011…22<i class="fa fa-angle-right" aria-label="下一页"></i>

216 日志
43 分类
43 标签
GitHub
© 2025 Randy Zhang
由 Hexo 强力驱动
|
主题 — NexT.Gemini v6.1.0