⭐工厂模式(Factory)
对象的创建逻辑比较复杂,就可以考虑使用工厂模式,把对象的创建过程和使用分开。
比如需要通过if-else动态创建不同的子类对象,就可以考虑把if-else的判断逻辑放到工厂类中。
比如单个对象本身的创建过程比较复杂,需要组合其它对象,做各种初始化操作,也可以把这些逻辑封装到工厂类中。
⭐简单工厂
使用工厂模式实现解析不同格式的配置文件:
定义工厂类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public class RuleConfigParseFactory {
private static final Map<String, IRuleConfigParser> cachedParsers = new HashMap<>();
static {
cachedParsers.put("json", new JsonRuleConfigParser());
cachedParsers.put("xml", new XmlRuleConfigParser());
cachedParsers.put("yaml", new YamlRuleConfigParser());
}
public static IRuleConfigParser createParser(String configFormat) {
if (configFormat == null || configFormat.isEmpty()) {
return null;
}
IRuleConfigParser parser = cachedParser.get(configFormat);
return parser;
}
}使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public class RuleConfigSource {
public RuleConfig load(String ruleConfigFilePath) {
String ruleConfigFileExtension = getFileExtension(ruleConfigFilePath);
IRuleConfigParser parser = RuleConfigParserFactory.createParser(ruleConfigFileExtension);
if (parser == null) {
throw new InvalidRuleConfigException("Rule config file format is not support");
}
String configText = getFileContent();
RuleConfig ruleConfig = parser.parse(configText);
return ruleConfig;
}
private String getFileExtension(String filePath) {
// ...获取文件拓展名,比如rule.json 返回json
return "json";
}
}
⭐工厂方法
使用工厂模式实现解析不同格式的配置文件:
定义工厂方法类
1
2
3public interface IRuleConfigParserFactory {
IRuleConfigParser createPraser();
}具体的工厂类
1
2
3
4
5
6
7// json格式配置文件的工厂类
public class JsonRuleConfigParserFactory implements IRuleConfigParserFactory {
public IRuleConfigParser createParser() {
return new JsonRuleConfigParser();
}
}1
2
3
4
5
6
7// xml格式配置文件的工厂类
public class XmlRuleConfigParserFactory implements IRuleConfigParserFactory {
public IRuleConfigParser createParser() {
return new XmlRuleConfigParser();
}
}1
2
3
4
5
6
7// yaml格式配置文件的工厂类
public class YamlRuleConfigParserFactory implements IRuleConfigParserFactory {
public IRuleConfigParser createParser() {
return new YamlRuleConfigParser();
}
}聚合工厂类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18// 根据文件名后缀创建不同的工厂对象
public class RuleConfigParseFactory {
private static final Map<String, IRuleConfigParserFactory> cachedParsers = new HashMap<>();
static {
cachedParsers.put("json", new JsonRuleConfigParserFactory());
cachedParsers.put("xml", new XmlRuleConfigParserFactory());
cachedParsers.put("yaml", new YamlRuleConfigParserFactory());
}
public static IRuleConfigParser createParser(String configFormat) {
if (configFormat == null || configFormat.isEmpty()) {
return null;
}
IRuleConfigParserFactory parserFactory = cachedParser.get(configFormat);
IRuleConfigParser parser = parserFactory.createParser(); // 调用工厂方法获取具体的工厂类
return parser;
}
}使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// 根据配置文件的后缀,将文件中的配置解析成内存对象
public class RuleConfigSource {
public RuleConfig load(String ruleConfigFilePath) {
String ruleConfigFileExtension = getFileExtension(ruleConfigFilePath);
IRuleConfigParser parser = RuleConfigParserFactory.createParser(ruleConfigFileExtension);
if (parser == null) {
throw new InvalidRuleConfigException("Rule config file format is not support: ");
}
String configText = getFileContent();
RuleConfig ruleConfig = parser.parse(configText);
return ruleConfig;
}
private String getFileExtension(String filePath) {
// ...获取文件拓展名,比如rule.json 返回json
return "json";
}
}
⭐抽象工厂
就是在工厂类中定义不同类型的对象的创建方式,可以减少工厂类的数量。
使用工厂模式实现解析不同格式的配置文件:
定义抽象工厂
1
2
3
4
5
6public interface IConfigParserFactory {
// 创建rule配置文件解析器工厂类
IRuleConfigParser createRuleParser();
// 创建system配置文件解析器工厂类
ISystemConfigParser createSystemParser();
}抽象工厂实现类
1
2
3
4
5
6
7
8
9
10
11
12
13public class JsonConfigParserFactory implements IConfigParserFactory {
// 创建json格式的rule配置文件解析器
public IRuleConfigParser createRuleParser() {
return new JsonRuleConfigParser();
}
// 创建json格式的system配置文件解析器
public ISystemConfigParser createSystemParser() {
return new JsonSystemConfigParser();
}
}1
2
3
4
5
6
7
8
9
10
11
12
13public class XmlConfigParserFactory implements IConfigParserFactory {
// 创建xml格式的rule配置文件解析器
public IRuleConfigParser createRuleParser() {
return new XmlRuleConfigParser();
}
// 创建xml格式的system配置文件解析器
public ISystemConfigParser createSystemParser() {
return new XmlSystemConfigParser();
}
}1
2
3
4
5
6
7
8
9
10
11
12
13public class YamlConfigParserFactory implements IConfigParserFactory {
// 创建yaml格式的rule配置文件解析器
public IRuleConfigParser createRuleParser() {
return new YamlRuleConfigParser();
}
// 创建yaml格式的system配置文件解析器
public ISystemConfigParser createSystemParser() {
return new YamlSystemConfigParser();
}
}聚合工厂类
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
28public class ConfigParserFactory {
private static final Map<String, IConfigParserFactory> map = new HashMap<>();
static {
map.put("json", new JsonConfigParserFactory());
map.put("xml", new XmlConfigParserFactory());
map.put("yaml", new YamlConfigParserFactory());
}
// 获取rule配置文件解析器
public static IRuleConfigParser createRuleParser(String configFormat) {
if (configFormat == null || configFormat.isEmpty()) {
return null;
}
IConfigParserFactory factory = map.get(configFormat);
IRuleConfigParser parser = factory.createRuleParser();
return parser;
}
// 获取system配置文件解析器
public static ISystemConfigParser createSystemParser(String configFormat) {
if (configFormat == null || configFormat.isEmpty()) {
return null;
}
IConfigParserFactory factory = map.get(configFormat);
ISystemConfigParser parser = factory.createSystemParser();
return parser;
}
}
单例模式(Singleton)
单例模式就是一个类只允许创建一个实例。
应用场景:
比如有些数据在系统中只应保存一份,就比较适合设计成单例类,比如用来保存系统配置的类,就比较适合设计成单例类。
比如某些系统中会缓存一些变动不太频繁的冷数据,通常就是在类中声明一些集合类型的成员变量用来缓存数据,这种也比较适合设计成单例类。
单例模式根据不同的创建方式,可以分细为五类:饿汉式、懒汉式、双重检测、静态内部类、枚举。
饿汉式
饿汉式就是在类加载的时候,就开始创建实例并初始化,这样的创建过程是线程安全的。
1 | public class Singleton { |
有些人认为提前初始化实例是一种浪费资源的行为,应该等用到的时候再去初始化,不过如果初始化的逻辑比较复杂,耗时比较长,这就会影响到系统的性能。
用饿汉式把一些耗时的操作,提前到程序启动的时候完成,这样就能避免初始化的性能问题。
懒汉式
懒汉式可以实现延迟加载,就是在用到的时候,才去创建实例并初始化。
1 | public class Singleton { |
但是在多线程环境中,饿汉式就需要通过加锁来确保只会创建一个实例,如果这个类经常被调用,就会导致频繁的加锁和解锁,会影响到系统的性能。
双重检测机制
双重检测也可以实现延迟加载,主要是为了解决,多线程环境下懒汉式并发度很低的问题。
1 | public class Singleton { |
因为指令重排,可能会导致Singleton对象被new出来之后,还没来得及初始化,就被使用了。这个问题可以通过给成员变量加上volatile关键字,禁止指令重排。
实际上这个问题只有很低的JDK版本才会有,高版本的JDK内部已经解决了这个问题,就是把创建对象的new操作,和初始化操作设计为原子操作,就可以禁止指令重排。
静态内部类
静态内部类这种方式也可以实现延迟加载。
1 | public class Singleton { |
静态内部类就是在类中定义一个静态内部类,在这个内部类中定义初始化的逻辑。当外部类被加载的时候,并不会创建内部类的实例对象,只有调用getInstance()方法的时候,内部类才会被加载,这个时候才会创建外部类的实例对象。
这个创建过程是由JVM来完成的,是线程安全的,也可以实现延迟加载。
枚举
基于枚举来实现单例,是最简单的一种方式了,就是通过Java枚举本身的特点,来保证实例创建的线程安全性和实例唯一性。
1 | public enum Singleton { |
⭐建造者模式(Builder)
建造者模式主要是用来创建一些比较复杂的对象。
数据填充和校验
如果一个类中有很多属性,有些属性是必填的,有些是非必填的。比较简单的做法就是把必填属性放在构造函数中,其它非必填的用set()方法设置。如果必填的属性有很多,就会导致构造函数参数列表很长,使用起来就很容易出错。
写法
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
49public class ResourcePoolConfig {
private String name;
private int maxTotal;
private int maxIdle;
private int minIdle;
private ResourcePoolConfig(Builder builder) {
this.name = builder.name;
this.maxTotal = builder.maxTotal;
this.maxIdle = builder.maxIdle;
this.minIdle = builder.minIdle;
}
// 通过Builder内部类完成属性填充
public static class Builder {
private String name;
private int maxTotal;
private int maxIdle;
private int minIdle;
public ResourcePoolConfig build() {
// 对属性做校验
if (StringUtils.isBlank(name)) {
throw new IllegalArgumentException("name can't be empty");
}
return new ResourcePoolConfig(this);
}
public Builder setName(String name) {
this.name = name;
return this;
}
public Builder setMaxTotal(int maxTotal) {
this.maxTotal = maxTotal;
return this;
}
public Builder setMaxIdle(int maxIdle) {
this.maxIdle = maxIdle;
return this;
}
public Builder setMinIdle(int minIdle) {
this.minIdle = minIdle;
return this;
}
}
}使用
1
2
3
4
5
6
7
8public static void main(String[] args) {
ResourcePoolConfig config = ResourcePoolConfig.Builder()
.setName("test")
.setMaxTotal(10)
.setMaxIdle(5)
.setMinIdle(2)
.build();
}
原型模式(Property)
如果一个对象的创建成本比较大,并且存在其它大部分字段都相同的对象,这种情况可以利用已有的对象,通过复制的方式来创建新的对象,这就叫原型模式。
原型模式有两种实现方法,深拷贝和浅拷贝。
- 浅拷贝只会复制对象中基本数据类型和引用对象的内存地址,不会递归的复制引用对象,以及引用对象的引用对象…。
- 深拷贝得到的是一份完全独立的对象,所以深拷贝会更加耗时,更耗内存空间。
实现缓存功能
很多时候我们会在系统启动的时候,从数据库中加载一些数据到内存中,用来做缓存。为了保证缓存中数据的正确性,通常会定期更新内存中的数据,这个时候就可以利用原型模式。
1 | public class Property { |
⭐代理模式(Proxy)
就是在不修改原代码的情况下,通过代理类来给原始类附加额外的功能。
代理模式又分为:静态代理和动态代理。
⭐静态代理
为每个类都创建一个代理类,并且每个代理类都要把原始类中的方法实现一遍,再附加一些额外的代理逻辑。
原始类:
1
2
3
4
5public class UserController {
public void login(String username, String password) {
// 登录逻辑...
}
}代理类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14public class UserControllerProxy {
private final UserController userController;
public UserControllerProxy(UserController userController) {
this.userController = userController;
}
public void login(String username, String password) {
// 委托
userController.login(username, password);
// 添加一些额外的逻辑,比如登录成功后发送短信通知
// ...
}
}使用:
1
2
3
4public static void main(String[] args) {
UserControllerProxy userControllerProxy = new UserControllerProxy(new UserController());
userControllerProxy.login("zhangsan","123456");
}
⭐动态代理
就是不预先为每个类创建代理类,而是在运行的时候,动态的创建原始类对应的代理类,然后在系统中用代理类替换掉原始类。
Java可以通过InvocationHandler接口和Proxy类来实现动态代理:
代理类:
1
2
3
4
5
6
7
8
9public class UserProxy {
public Object createProxy(Object proxyObject) {
Class<?>[] interfaces = proxyObject.getClass().getInterfaces();
// 创建动态代理处理器
DynamicProxyHandler handler = new DynamicProxyHandler(proxyObject);
// 创建代理类
return Proxy.newProxyInstance(proxyObject.getClass().getClassLoader(), interfaces);
}
}动态代理处理器:
1
2
3
4
5
6
7
8
9
10
11
12
13
14public class DynamicProxyHandler implements InvocationHandler {
private Object proxyObject;
public DynamicProxyHandler(Object proxyObject) {
this.proxyObject = proxyObject;
}
public Object invoke(Object proxy, Method method, Object[] args) {
// 代理逻辑...
Object result = method.invoke(proxyObject, args);
return result;
}
}使用:
1
2
3
4
5public static void main(String[] args) {
UserProxy proxy = new UserProxy();
UserController userController = (UserController) proxy.createProxy(new UserController())
userController.login();
}
⭐桥接模式(Bridge)
将抽象部分和实现部分分离,让它们都可以独立的变化。
⭐消息推送功能
创建消息推送接口:
1
2
3public interface MsgSender {
void send(String message);
}消息推送实现类:
1
2
3
4
5
6
7
8
9// 以短信的方式发送消息
public class TelephoneMsgSender implements MsgSender {
private List<String> telephones;
public void send(String message) {
// 发送短信逻辑
}
}1
2
3
4
5
6
7
8
9// 以邮件的方式发送消息
public class EmailMsgSender implements MsgSender {
private List<String> emails;
public void send(String message) {
// 发送邮件逻辑
}
}1
2
3
4
5
6
7
8
9// 以微信的方式发送消息
public class WechatMsgSender implements MsgSender {
private List<String> wechats;
public void send(String message) {
// 发送微信逻辑
}
}创建桥接类,根据消息的紧急程度,将消息发送到不同渠道(不同紧急程度的消息和发送渠道的对应关系,可以动态配置)
定义消息通知抽象类
1
2
3
4
5
6
7
8
9
10public abstract class Notification {
// 消息发送渠道
protected MsgSender msgSender;
public Notification(MsgSender msgSender) {
this.msgSender = msgSender;
}
public abstract void notify(String message);
}消息通知具体实现类
1
2
3
4
5
6
7
8
9
10
11// 严重通知级别
public class SevereNotification extends Notification {
public SevereNotification(MsgSender msgSender) {
super(msgSender);
}
public void notify(String message) {
msgSender.send(message);
}
}1
2
3
4
5
6
7
8
9
10
11// 紧急通知级别
public class UrgencyNotification extends Notification {
public UrgencyNotification(MsgSender msgSender) {
super(msgSender);
}
public void notify(String message) {
msgSender.send(message);
}
}
使用
1
2
3
4public static void main(String[] args) {
Notification notifiaction = new UrgencyNotification(new EmailSender());
notification.notify("这是一条紧急信息...");
}
⭐装饰器模式(Decoreator)
装饰器模式跟代理模式的结构基本一样,只不过代理模式是为原始类增加一些额外的功能,装饰器模式是增强原始类现有的功能。
⭐增强登录功能
用户登录接口:
1
2
3public interface LoginApi {
boolean login(String username, String password);
}原始类:
1
2
3
4
5
6public class LoginController implements LoginApi {
public boolean login(String username, String password) {
// ...登录逻辑
}
}发送邮件的装饰器:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18// 登陆成功后发送邮件的装饰器
public class LoginControllerWithSendEmail implements LoginApi {
private final LoginApi loginApi;
private final Sender sender = new EmailSender(); // 发送邮件工具类
public UserControllerWithSendEmail(LoginApi loginApi) {
this.loginApi = loginApi;
}
public boolean login(String username, String password) {
boolean result = loginApi.login(username, password);
if (result) {
// 登陆成功,发送邮件
sender.send("登陆成功...");
}
return result;
}
}生成登录记录的装饰器:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25// 登陆成功后生成一条登录信息
public class LoginControllerWithCreateRecord implements LoginApi {
private final LoginApi loginApi;
private final LoginRecordRepository loginRecordRepository = new LoginRecordRepository();
public LoginControllerWithCreateRecord(LoginApi loginApi) {
this.loginApi = loginApi;
}
public boolean login(String username, String password) {
boolean result = loginApi.login(username, password);
if (result) {
// 创建一条登录信息
LoginRecord loginRecord = LoginRecord.builder()
.address("合肥")
.ip("127.0.0.1")
.timestamp(LocalDateTime.now())
.build();
loginRecordRepository.create(loginRecord);
}
return result;
}
}使用:
1
2
3
4
5
6
7
8public static void main(String[] args) {
LoginApi loginApi = new LoginControllerWithCreateRecord(
new LoginControllerWithSendEmail(
new LoginController()
)
);
loginApi.login("zhangsan","123456");
}
⭐适配器模式(Adapter)
适配器模式就是用一个新类,把原来的类包装一下,用来解决两个不兼容的类可以在一起工作。
比如某个功能需要依赖多个外部系统,这些外部系统接口的参数和返回值格式都不一样,这时候就可以通过适配器模式,把它们的接口适配为统一的接口定义。
⭐数据聚合功能
从多种数据库查询数据:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20//... 从mysql中获取数据
public class MySQLProvider {
public List<String> find(String str1) {
}
}
//.. 从Oracle中获取数据
public class OracleProvider {
public Map<String, String> find(String str1, String str2) {
}
}
//... 从Redis中获取数据
public class RedisProvider {
public String get(String key) {
}
}定义适配器接口:
1
2
3public interface IDataProvider {
List<String> find(String param);
}根据不同的数据渠道定义不同的适配器:
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
26public class MySQLProviderAdapter implements IDataProvider { // MySQL适配器
private MySQLPrivider mysqlProvider = new MySQLProvider();
public List<String> find(String str) {
return mysqlProvider.find(str);
}
}
public class OracleProviderAdapter implements IDataProvider { // Oracle适配器
private OracleProvidedr oracleProvider = new OracleProvider();
public List<String> find(String str) {
Map<String, String> data = oracleProvider.find(str, "xxx");
List<String> res = map2List(data);
return res;
}
}
public class RedisProvider implements IDataProvider { // Redis适配器
private RedisProvider redisProvider = new RedisProvider();
public List<String> find(String str) {
String data = redisProvider.get(str);
return Collections.singletonList(data);
}
}聚合适配器:
1
2
3
4
5
6
7
8
9
10
11
12
13
14public class DataManagement {
private List<IDataProvider> providers = new ArrayList<>();
public void addProvider(IDataProvider provider) {
providers.add(provider);
}
public List<String> find(String str) {
List<String> result = new ArrayList<>();
for (IDataProvider provider : providers) {
List<String> data = provider.find(str);
result.addAll(data);
}
return result;
}
}使用:
1
2
3
4
5
6
7
8public static void main(String[] args) {
// 使用
DataManagement dataManagement = new DataManagement();
dataManagement.addProvider(new MySQLProviderAdapter()); // 添加MySQL适配器
dataManagement.addProvider(new OracleProviderAdapter()); // 添加Oracle适配器
dataManagement.addProvider(new RedisProviderAdapter()); // 添加Redis适配器
List<String> result = dataManagement.find("...");
}
门面模式(Facade)
比如用户注册的时候,需要生成一条用户信息,还要给用户分配角色。这样注册这个功能就需要依赖,用户和角色两个服务。
传统的做法就是在用户注册服务中,依赖注入创建用户接口和分配角色接口。如果注册时还有更多的操作,就需要依赖更多的接口。
这种情况就可以定义一个门面服务,让门面服务去依赖用户服务和角色服务。这样注册服务只需要依赖门面服务就可以了。
提高效率:如果依赖的服务需要通过RPC调用,也可以通过门面模式把多次RPC调用减少为1次。
实现事物:有些时候需要调用两个更新数据的方法,这两个操作需要同时成功或者同时失败,最简单的方式就是:再设计一个包含这两个方法的新方法。这也是门面模式的一种设计思想。
实现用户注册功能
用户注册门面服务
1
2
3
4
5
6
7
8
9public class UserRegisterFacade {
private final UserService userService;
private final RoleService roleService;
public void post(User user) {
userService.create(user); // 创建用户
roleService.setRole(user); // 分配角色
}
}用户注册服务接口
1
2
3
4
5
6
7
8public class SystemService {
private final UserRegisterFacade registerFacade;
public boolean register(User user) {
// 调用门面服务中的操作
registerFacade.post(user);
// 执行注册后续的操作...
}
}
⭐组合模式(Compose)
组合模式可以很方便的把不同类型的对象组合成树形结构。
⭐实现结构树
定义组织结构抽象类:
1
2
3
4
5
6
7public abstract class Organization { // 用来做一些额外的处理逻辑
protected BigDecimal salary; // 工资
protected int age; // 年龄
public abstract BigDecimal calculateSalary(); // 计算部门所有工资总和(包括下级部门)
public abstract int calculateAverageAge(); // 计算部门平均年龄
public abstract boolean isDeparment(); // 是否是部门, 部门:true,员工:false
}部门类:
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
45public class Deparment implements Organization {
private long id;
private String name;
private List<Organization> subOrganizations = new ArrayList<>();
public void addOrganization(Organization org) {
subOrganizations.add(org);
}
public BigDecimal calculateSalary() {
BigDecimal totalSalary = BigDecimal.ZERO;
for (Organization org : subOrganizations) {
totalSalary.add(org.getSalary());
}
return totalSalary;
}
public int calculateAverageAge() {
int totalAge = 0;
int employeeCount = 0;
for (Organization org : subOrganizations) {
if (!isDeparment()) {
totalAge += org.getAge();
employeeCount++;
}
}
return totalAge / employeeCount;
}
public boolean isDeparment() {
return true;
}
public BigDecimal getSalary() {
BigDecimal totalSalary = BigDecimal.ZERO;
for (Organization org : subOrganizations) {
if (!org.isDepartment()) {
totalSalary.add(org.getSalary());
}
}
return totalSalary;
}
}员工类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public class Employee implements Organization {
private long id;
private String name;
public BigDecimal calculateSalary() {
return this.salary();
}
public int calculateAverageAge() {
return this.age;
}
public boolean isDeparment() {
return false;
}
}使用:
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
28public static void main(String[] args) {
private DeparmentRepository deparmentRepo = new DeparmentRepository();
private EmployeeRepository employeeRepo = new EmployeeRepository();
private static final long ORGANIZATION_ROOT_ID = 1; // 顶级部门ID
public void buildOrganization() {
Deparment deparment = new Deparment();
deparment.setId(ORGANIZATION_ROOT_ID);
buildOrganization(deparment);
}
public void buildOrganization(Deparment deparment) {
// 根据部门ID获取某个部门的下级部门
List<Deparemnt> subDeparments = deparmentRepo.getDeparmentByParentId(deparment.getId());
for (Deparment subDeparment : subDeparments) {
deparment.addOrganization(subDeparment);
buildOrganization(subDeparment);
}
// 根据部门ID获取员工列表
List<Employee> employees = employeeRepo.getEmployeeByDeparmentId(deparment.getId());
for (Employee employee : employees) {
deparment.addOrganization(employee);
}
}
}
⭐享元模式(Flyweight)
享元模式可以复用对象,节省内存,不过前提是享元对象是不可变的对象。
如果系统中有很多重复的对象,并且这些对象都是不可变的,就可以利用享元模式,让这些对象在内存中只保留一份,提供多处引用,这样可以减少内存中对象的数量。
也可以把对象之间相同的字段提取出来,设计成享元类,让这些对象引用享元对象。
不可变对象是指,对象通过构造函数初始化完成之后,就不能再做修改了,所以不能暴露任何set()方法,因为享元对象会被多处引用,如果某一个地方修改了享元对象,就会影响到其它代码。
比较常见的做法就是通过工厂模式,在工厂类中,用一个Map来缓存已经创建过的享元对象,来达到复用的目的。
比如Integer类中的IntegerCache、Long类中的LongCache也都是利用享元模式来预先存储一些常用的数字(-128~127)
缺点:享元模式会导致享元类无法被GC回收掉,因为享元工厂类会一直保存着享元对象的引用,这就会导致享元对象在没有任何代码使用的情况下,也不会被JVM回收掉。
⭐实现棋盘游戏
定义享元类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// 内部定义了一些通用的属性
public class ChessPieceUnit {
private int id;
private String text;
private Color color;
public ChessPieceUnit(int id, String text, Color color) {
this.id = id;
this.text = text;
this.color = color;
}
public static enum Color {
RED, BLACK
}
}享元工厂类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14public class ChessPieceUnitFactory {
private static final Map<Integer, ChessPieceUnit> pieces = new HashMap<>();
static {
pieces.put(1, new ChessPieceUnit(1, "车", ChessPieceUnit.Color.BLACK));
pieces.put(2, new ChessPieceUnit(2, "马", ChessPieceUnit.Color.BLACK));
pieces.put(3, new ChessPieceUnit(3, "象", ChessPieceUnit.Color.BLACK));
pieces.put(4, new ChessPieceUnit(4, "士", ChessPieceUnit.Color.BLACK));
pieces.put(5, new ChessPieceUnit(5, "将", ChessPieceUnit.Color.BLACK));
...
}
public static ChessPieceUnit getChessPiece(int chessPieceId) {
return pieces.get(chessPieceId);
}
}棋子类:
1
2
3
4
5
6
7
8
9
10
11
12public class ChessPiece {
private ChessPieceUnit chessPieceUnit; // 引用的享元类
private int positionX; // 棋子在棋盘上的x坐标
private int positionY; // 棋子在棋盘上的y坐标
public ChessPiece(ChessPieceUnit chessPieceUnit, int positionX, int positionY) {
this.chessPieceUnit = chessPieceUnit;
this.positionX = positionX;
this.positionY = positionY;
}
// getter/setter
}棋盘类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public class ChessBoard {
private Map<Integer, ChessPiece> chessPieces = new HashMap<>();
public ChessBoard() {
init();
}
private void init() {
chessPieces.put(1,new ChessPiece(ChessPieceUnitFactory.getChessPiece(1), 0,0));
chessPieces.put(2,new ChessPiece(ChessPieceUnitFactory.getChessPiece(2), 0,1));
chessPieces.put(3,new ChessPiece(ChessPieceUnitFactory.getChessPiece(3), 0,2));
chessPieces.put(4,new ChessPiece(ChessPieceUnitFactory.getChessPiece(4), 0,3));
chessPieces.put(5,new ChessPiece(ChessPieceUnitFactory.getChessPiece(5), 0,4));
}
public void move(int chessPieceId, int toPositionX, int toPositionY) {
// ...
}
}
⭐观察者模式(Observer)
就是在对象之间维护一个中间层,当一个对象状态改变的时候,其它所有依赖的对象都会自动收到通知。
guava包中的事件总线就是观察者模式(EventBus)
⭐实现注册功能
定义观察者接口:
1
2
3public interface RegObserver {
void handleRegSuccess(long userId); // 用户注册成功时的事件通知函数
}定义观察者实现类:
1
2
3
4
5
6
7
8// 为新用户生成密码
public class RegPromotionObserver implements RegObserver {
private PromotionService promitionService;
public void handleRegSuccess(long userId) {
promotionService.generatePassword(userId);
}
}1
2
3
4
5
6
7
8// 发送欢迎邮件
public class RegNotificationObserver implements RegObserver {
private NotificationService notificationService;
public void handleRegSuccess(long userId) {
notificationService.sendMessage(userId, "Welcome...");
}
}用户注册接口:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public class UserController {
private UserService userService;
// 观察者
private List<RegObserver> regObservers;
public void setRegObservers(List<RegObserver> observers) {
regObservers = observers;
}
public void register(String telephone, String password) {
// 用户注册
long userId = userService.register(telephone, password);
// 用户注册完成后,通知其它服务
for (RegObserver observer : regObservers) {
observer.handleRegSuccess(userId);
}
}
}使用:
1
2
3
4
5
6
7
8
9public static void main(String[] args) {
List<RegObserver> regObservers = new ArrayList<>();
regObservers.add(new RegNotificationObserver());
regObservers.add(new RegPromotionObserver());
UserController userController = new UserController();
userController.setRegObservers(regObservers);
userController.register("admin", "123456");
}
用户注册完之后,要依次调用所有观察者的事件通知方法,都执行完,才会返回结果给客户端,这会影响到注册接口的响应时间。
可以改成异步非阻塞的方式,调用观察者的通知方法,比如在线程池中执行事件通知方法。
也可以用Event Bus(事件总线)框架,或者RocketMQ之类的消息队列。
⭐事件总线
定义标记注解,用来标明观察者中,哪个函数可以接收消息。
1
2
3
4
public Subscribe {
}用来表示@Subscribe 标注的方法。target表示观察者类,method表示方法。主要用在ObserverRegistry观察者注册表中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19public class ObserverAction {
private Object target;
private Method method;
public ObserverAction(Object target, Method method) {
this.target = Precondition.checkNotNull(target);
this.method = method;
this.method.setAccessable(true);
}
// event是method方法的参数
public void execute(Object event) {
try {
method.invoke(target, event);
} catch(Exception e) {
e.printStackTrace();
}
}
}注册表
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
54public class ObserverRegistry {
private ConcurrentMap<Class<?>, CopyOnWriteArraySet<ObserverAction>> registry = new ConcurrentHashMap<>();
// 注册
public void register(Object observer) {
Map<Class<?>, Collection<ObserverAction> observerActions = findAllObserverActions(observer);
for (Map.Entry<Class<?>, Collection<ObserverAction>> entry : observerActions.entrySet()) {
Class<?> eventType = entry.getKey();
Collection<ObserverAction> eventActions = entry.getValue();
CopyOnWriteArraySet<ObserverAction> registeredEventActions = registry.get(eventType);
if (registeredEventActions == null) {
registry.putIfAbsent(eventType, new CopyOnWriteArraySet<>());
registeredEventActions = registry.get(eventType);
}
registeredEventActions.addAll(eventActions);
registry.put(eventType, registeredEventActions);
}
}
// 找到符合条件的处理器
public List<ObserverAction> getMatchedObserverActions(Object event) {
List<ObserverAction> matchedObservers = new ArrayList<>();
Class<?> postedEventType = event.getClass();
for (Map.Entry<Class<?>, CopyOnWriteArraySet<ObserverAction>> entry : registry) {
Class<?> eventType = entry.getKey();
CopyOnWriteArraySet<ObserverAction> eventActions = entry.getValue();
// 判断是否是父类
if (postedEventType.isAssignableFrom(eventType)) {
matchedObservers.addAll(eventActions);
}
}
return matchedObservers;
}
private Map<Class<?>, Collection<ObserverAction>> findAllObserverActions(Object observer) {
Map<Class<?>, Collection<ObserverAction>> observerActions = new HashMap<>();
Class<?> clazz = observer.getClass();
for (Method method : clazz.getDeclaredMethods() {
if (method.isAnnotationPresent(Subscribe.class)) {
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length != 1) {
throw new ArgumentException("参数长度错误");
}
Class<?> eventType = parameterTypes[0];
if (!observerActions.containsKey(eventType)) {
observerActions.put(eventType, new ArrayList<>());
}
observerActions.get(eventType).add(new ObserverAction(observer, method));
}
}
return observerActions;
}
}事件总线
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30// 同步事件总线
public class EventBus {
private Executor executor;
private ObserverRegistry registry = new ObserverRegistry();
public EventBus() {
// MoreExecutors.directExecutor()是Google Guava提供的工具类,实际上是一个单线程的线程池,主要还是为了跟AsyncEventBus统一代码逻辑,做到代码复用。
this(MoreExecutors.directExecutor());
}
protected EventBus(Executor executor) {
this.executor = executor;
}
public void register(Object object) {
registry.register(object);
}
public void post(Object event) {
List<ObserverAction> observerActions = registry.getMatchedObserverActions(event);
for (ObserverAction observerAction : observerActions) {
executor.execute(new Runable() {
public void run() {
observerAction.execute(event);
}
});
}
}
}1
2
3
4
5
6// 异步事件总线
public class AsyncEventBus {
public AsyncEventBus(Executor executor) {
super(executor);
}
}使用
1
2
3
4
5public static void main(String[] args) {
EventBus eventBus = new EventBus();
eventBus.register(new RegNotificationObserver());
eventBus.post("xxx");
}
⭐模板模式(Template Method)
就是定义一个模板方法,规定业务流程,让子类来实现流程的业务逻辑。
在Java中,通常是把模板方法定义成final的,避免被子类重写,需要子类重写的方法,定义为abstract的,强迫子类实现。
可以提高代码的复用性和拓展性。
JDK中有很多类都用到了模板模式,比如InputStream、OutputStream、Reader、Writer。
InputStream中read()方法就是一个模板方法,规定了读取数据的流程,而且还暴露了一个重载的抽象方法,让子类来实现读取数据的逻辑。
AbstractList中addAll()也是一个模板方法,需要依赖子类重写add()方法才能调用。
⭐模板模式结构
定义模板类和模板方法
1
2
3
4
5
6
7
8public abstract class AbstractTemplate {
public final void templateMethod() { // 定义模板方法,规定业务流程
f1();
f2();
}
public abstract void f1(); // 流程1
public abstract void f2(); // 流程2
}定义子类实现流程1和流程2的逻辑
1
2
3
4
5
6
7
8
9
10public class Concrete1 extends AbstractTemplate {
public void f1() {
//...
}
public void f2() {
//...
}
}1
2
3
4
5
6
7
8
9
10public class Concrete2 extends AbstractTemplate {
public void f1() {
//...
}
public void f2() {
//...
}
}使用
1
2
3
4
5
6
7public static void main(String[] args) {
AbstractTemplate template = new Concreate1();
template.templateMethod();
template = new Concreate2();
template.templateMethod();
}
⭐策略模式(Strategy)
就是定义一个策略接口,然后再根据不同的逻辑定义一些实现这个接口的策略类。这样就可以很灵活的替换不同的策略。
策略模式一般都会通过类型,来判断创建哪个策略来使用,可以可以配合工厂模式,把创建策略的逻辑放到工厂类中。
如果策略类是无状态的,不包含成员变量,这样的策略对象是可以共享的,不需要每次都创建新的对象,可以预先创建好策略对象,缓存到工厂类中。
⭐策略模式结构
定义策略接口:
1
2
3public interface Strategy {
void method();
}定义策略实现类:
1
2
3
4
5
6
7// 策略类1
public class ConcreteStrategyA implements Strategy {
public void method() {
// 具体的逻辑...
}
}1
2
3
4
5
6
7// 策略类2
public class ConcreteStrategyB implements Strategy {
public void method() {
// 具体的逻辑...
}
}定义策略工厂类:
1
2
3
4
5
6
7
8
9
10
11
12
13// 定义策略工厂类,用来创建具体的策略
public class StrategyFactory {
private static final Map<String, Strategy> strategies = new HashMap<>(); // 用来缓存策略对象
static {
strategies.put("A", new ConcreteStrategyA());
strategies.put("B", new ConcreteStrategyB());
}
public static Strategy getStrategy(String type) {
return strategies.get(type);
}
}使用:
1
2
3
4public static void main(String[] args) {
Strategy strategy = StrategyFactory.getStrategy("A");
strategy.method();
}
⭐责任链模式(Chain Of Responsibility)
就是多个类处理同一个请求,一个请求先经过A处理,然后再把请求传递给B处理,B再传递给C处理。每个类都承担着自己的职责。
Servlet中的Filter和Spring中的Interceptor都使用了责任链模式。
⭐实现过滤敏感信息功能
定义敏感信息过滤接口
1
2
3public interface SensitiveFilter {
boolean doFilter(Content content);
}根据数据类型定义不同的过滤器
1
2
3
4
5
6
7
8
9// 文本过滤器
public class SensitiveTextFilter implements SensitiveFilter {
public boolean doFilter(Content content) {
boolean legal = true;
// 判断用户输入的文本是否包含敏感词汇
return legal;
}
}1
2
3
4
5
6
7
8
9// 图片过滤器
public class SensitiveImageFilter implements SensitiveFilter {
public boolean doFilter(Content content) {
boolean legal = true;
// 判断用户上传的图片是否包含敏感信息
return legal;
}
}1
2
3
4
5
6
7
8
9// 视频过滤器
public class SensitiveVideoFilter implements SensitiveFilter {
public boolean doFilter(Content content) {
boolean legal = true;
// 判断用户上传的视频是否包含敏感信息
return legal;
}
}1
2
3
4
5
6
7
8
9// 音频过滤器
public class SensitiveAudioFilter implements SensitiveFilter {
public boolean doFilter(Content content) {
boolean legal = true;
// 判断用户上传的音频是否包含敏感信息
return legal;
}
}定义责任链类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public class SensitiveFilterChain {
// 过滤器
private List<SensitiveFilter> filters = new ArrayList<>();
public void addFilter(SensitiveFilter filter) {
this.filters.add(filter);
}
public boolean filter(Content content) {
for (SensitiveFilter filter : filters) {
if (!filter.doFilter(content)) {
return false;
}
}
return true;
}
}使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14public static void main(Stirng[] args) {
SensitiveFilterChain filterChain = new SensitiveFilterChain();
filterChain.addFilter(new SensitiveTextFilter());
filterChain.addFilter(new SensitiveImageFilter());
filterChain.addFilter(new SensitiveVideoFilter());
filterChain.addFilter(new SensitiveAudioFilter());
boolean legal = filterChain.filter(new Content());
if (legal) {
// 合法
} else {
// 不合法
}
}
⭐状态模式(Finite State Machine)
状态模式就是把,事件触发的状态转移和执行动作,拆分到不同的类中。
⭐超级马里奥游戏
定义马里奥接口
1
2
3
4
5
6
7
8public interface IMario {
// 定义事件
void obtainMushroom(); // 获得蘑菇事件
void obtainCape(); // 获得斗篷事件
void obtainFireFlower(); // 获得火焰花事件
void meetMonster(); // 遇到怪物事件
void topBrick(); // 顶砖头事件
}马里奥实现类,并根据马里奥状态重写事件逻辑
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// 小马里奥
public class SmallMario implements IMario {
private MarioStateMachine marioStateMachine;
public SmallMario(MarioStateMachine marioStateMachine) {
this.marioStateMachine = marioStateMachine;
}
public void obtainMushroom() {
//小马里奥获得蘑菇,变为超级马里奥
marioStateMachine.setState(new SuperMario(marioStateMachine));
}
public void obtainCape() {
// 小马里奥获得斗篷,变成斗篷马里奥
marioStateMachine.setState(new CapeMario(marioStateMachine));
}
public void obtainFireFlower() {
// 小马里奥获得火焰花,变成火焰马里奥
marioStateMachine.setState(new FireMario(marioStateMachine));
}
public void meetMonster() {
// 小马里奥遇到怪物,减少一条命
marioStateMachine.descreaseLife();
}
public void topBrick() {
// 小马里奥顶砖头,不会破坏砖头
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35// 超级马里奥
public class SuperMario implements IMario {
private MarioStateMachine marioStateMachine;
public SuperMario(MarioStateMachine marioStateMachine) {
this.marioStateMachine = marioStateMachine;
}
public void obtainMushroom() {
// 超级马里奥获得蘑菇,积分加100
marioStateMachine.increaseScore(100);
}
public void obtainCape() {
// 超级马里奥获得斗篷,变成斗篷马里奥
marioStateMachine.setState(new CapeMario(marioStateMachine));
}
public void obtainFireFlower() {
// 超级马里奥获得火焰花,变成火焰马里奥
marioStateMachine.setState(new FireMario(marioStateMachine));
}
public void meetMonster() {
// 超级马里奥遇到怪物,变成小马里奥
marioStateMachine.setState(new SmallMario(marioStateMachine));
}
public void topBrick() {
// 超级马里奥顶砖头,会破坏砖头
marioStateMachine.distoryBrick();
}
}定义状态机
1
2
3
4
5
6
7
8
9
10
11
12
13
14public class MarioStateMachine {
private IMario currentMario;
public MarioStateMachine() {
currentMario = new SmallMario(this); //默认是小马里奥
}
public void setState(IMario mario) {
currentMario = mario;
}
public IMario getState() {
return this.currentMario;
}
}使用
1
2
3
4
5
6public static void main(String[] args) {
MarioStateMachine marioStateMachine = new MarioStateMachine();
marioStateMachine.obtainMushroom(); // 吃蘑菇
marioStateMachine.topBrick(); // 顶砖头
marioStateMachine.getState();
}
迭代器模式(Iterator)
迭代器模式就是用来遍历集合的,它会把集合的遍历操作从集合类中拆分出来,放到迭代器类中,让双方的职责更单一。
foreach循环是一个语法糖,底层是基于迭代器来实现的。
for循环比较适合遍历数组、链表这种线性数据结构。对于复杂的数据结构,比如树、图,树有前中后序、按层遍历,图有深度优先、广度优先遍历。使用迭代器模式就可以针对深度优先和广度优先,定义两个迭代器,分别实现深度优先和广度优先。
基于快照的迭代器
为每个元素保存两个时间戳,一个是添加时间addTimestamp,另一个是删除时间delTimestamp。
当元素被加入到集合的时候,addTimestamp = 当前时间、delTimestamp = Long.MAX_VALUE。
当元素被删除的时候,delTimestamp = 当前时间。表示已经被删除。
再定义一个快照的创建时间 snapshotTimestamp。只有addTimestamp < snapshotTimestamp < delTimestamp 的元素才属于当前快照。
如果元素的添加时间大于快照的创建时间,说明元素是在快照之后才加入的,不属于当前快照。
如果元素的删除时间小于快照的创建时间,说明元素在快照之前就被删除掉了,也不属于当前快照。
定义ArrayList
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
50public class ArrayList<E> implements List<E> {
private static final int DEFAULT_CAPACITY = 10;
private int actualSize; // 不包含标记删除的元素。
private int totalSize; // 包含标记删除的元素
private Object[] elements;
private long[] addTimestamps;
private long[] delTimestamps;
public ArrayList() {
this.elements = new Object[DEFAULT_CAPACITY];
this.addTimestamps = new long[DEFAULT_CAPACITY];
this.delTimestamps = new long[DEFAULT_CAPACITY];
this.actualSize = 0;
this.totalSize = 0;
}
public Iterator iterator() {
return new SnapshotArrayIterator(this);
}
public void add(E obj) {
elements[totalSize] = obj;
addTimestamps[totalSize] = System.currentTimeMills();
delTimestamps[totalSize] = Long.MAX_VALUE;
totalSize++;
actualSize++;
}
public void remove(E obj) {
for (int i = 0; i < totalSize; i++) {
if (elements[i].equals(obj)) {
delTimestamps[i] = System.currentTimeMills();
actualSize--;
}
}
}
public E get(int i) {
if (i >= totalSize) {
throw new IndexOutOfBoundsException();
}
return (E) elements[i];
}
}定义迭代器
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
39public class SnapshotArrayIterator<E> implements Iterator<E> {
private long snapshotTimestamp;
private int cursorInAll; // 在整个容器中的下标,而非快照中的下标
private int leftCount; // 快照中还有几个元素未被遍历
private ArrayList<E> arrayList;
public SnapshotArrayIterator(ArrayList<E> arrayList) {
this.snapshotTimestamp = System.currentTimeMills();
this.cursorInAll = 0;
this.leftCount = arrayList.actualSize();
this.arrayList = arrayList;
justNext(); //先跳到这个迭代器快照的第一个元素
}
public boolean hasNext() {
return this.leftCount >= 0;
}
public E next() {
E currentItem = arrayList.get(cursorInAll);
justNext();
return currentItem;
}
private void justNext() {
while(cursorInAll < arrayList.totalSize()) {
long addTimestamp = arrayList.getAddTimestmap(cursorInAll);
long delTimestamp = arrayList.getDelTimestamp(cursorInALl);
if (snapshotTimestamp > addTimestamp && snapshotTimestamp < delTimestamp) {
leftCount--;
break;
}
cursorInAll++;
}
}
}
访问者模式(Visitor Pattern)
访问者模式针对的是一组不同类型的对象,但是这组对象需要继承相同的父类。为了避免不断添加新功能导致类不断膨胀,可以使用访问者模式将对象与业务逻辑解耦,可以把这些业务逻辑抽离出来,定义在访问者类中。
实现文件解析和压缩功能
定义文件父类
1
2
3
4
5
6
7public abstract class ResourceFile {
protected String filePath;
public ResourceFile(String filePath) {
this.filePath = filePath;
}
public abstract void accept(Visitor visitor);
}根据文件类型定义不同的文件实现类
1
2
3
4
5
6
7
8
9
10// PDF文件
public class PdfFile extends ResourceFile {
public PdfFile(String filePath) {
super(filePath);
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
}1
2
3
4
5
6
7
8
9
10// PPT文件
public class PPTFile extends ResourceFile {
public PPTFile(String filePath) {
super(filePath);
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
}定义访问者接口
1
2
3
4
5
6// 访问者
public interface Visitor {
void visit(PdfFile pdfFile);
void visit(PPTFile pptFile);
void visit(WordFile wordFile);
}定义访问者实现类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// 提取文件内容访问者
public class Extractor implements Visitor {
public void visit(PdfFile pdfFile) {
// 将pdf提取到txt文本
}
public void visit(PPTFile pptFile) {
// 将ppt提取到txt文本
}
public void visit(WordFile wordFile) {
// 将word提取到txt文本
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// 压缩文件访问者
public class Compressor implements Visitor {
public void visit(PdfFile pdfFile) {
// 压缩pdf文件
}
public void visit(PPTFile pptFile) {
// 压缩ppt文件
}
public void visit(WordFile wordFile) {
// 压缩word文件
}
}使用
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 ToolApplication {
public static void main(String[] args) {
Extactor extractor = new Extractor();
List<ResourceFile> resourceFiles = listAllResourceFiles(new File("D:/test/"));
for (ResourceFile file : resourceFiles) {
file.accept(extractor); // 提取文件内容
}
Compressor compressor = new Compressor();
for (ResourceFile file : resourceFiles) {
file.accept(compressor); // 压缩文件
}
}
public List<ResourceFile> listAllResourceFiles(File resourceDirectory) {
List<ResourceFile> resourceFiles = new ArrayList<>();
File[] files = resourceDirectory.listFiles();
for(File file : files) {
ResourceFile resourceFile = ResourceFileFactory.createResourceFile(file);
resourceFiles.add(resourceFile);
}
return resourceFiles;
}
}
备忘录模式(Memento)
在不违背封装原则的前提下,获取并保存一个对象的内部状态,以便之后恢复对象为之前的状态。
备忘录模式主要分为两部分:一部分是存储副本以便后期恢复,另一部分是在不违反封装原则的前提下,进行对象备份和恢复。
比如在很多文本编辑器中,使用Ctrl+Z可以撤销上一次输入的文本,这样的场景就可以使用备忘录模式。
实现文本编辑器
定义文本对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// 用来存储用户输入的所有文本内容
public class InputText {
private StringBuilder text = new StringBuilder();
public String getText() {
return text.toString();
}
public void append(String input) {
text.append(input);
}
public void restoreSnapshot(Snapshot snapshot) {
this.text.replace(0, this.text.length(), snapshot.getText());
}
}1
2
3
4
5
6
7
8
9
10// 用户输入的文本对象
public class Snapshot {
private String text;
public Snapshot(String text) {
this.text = text;
}
public String getText() {
return text;
}
}保存用户输入的文本副本的容器
1
2
3
4
5
6
7
8
9public class SnapshotHolder {
private Stack<Snapshot> snapshots = new Stack<>(); // 用来存储每次用户输入的文本对象的副本
public Snapshot popSnapshot() {
return snapshots.pop();
}
public void pushSnapshot(Snapshot snapshot) {
snapshots.push(snapshot);
}
}使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public static void main(String[] args) {
InputTtext inputText = new InputText();
SnapshotHolder snapshotHolder = new SnapshotHolder();
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
String input = scanner.next();
if ("ctrl+z".equals(input)) {
Snapshot snapshot = snapshotHolder.popSnapshot();
inputText.restoreSnapshot(snapshot);
} else {
inputText.append(input);
snapshotHolder.pushSnapshot(new Snapshot(input));
}
}
}
命令模式(Command)
命令模式就是,将函数封装成一个对象,这样就可以把函数当成变量一样传递来传递去。
基本结构
定义命令接口
1
2
3public interface Command {
void execute();
}命令实现类
1
2
3
4
5
6
7// 眩晕敌人
public class FaintEnemyCommand implements Command {
public void execute() {
}
}1
2
3
4
5
6
7// 攻击敌人
public class AttackEnemyCommand implements Command {
public void execute() {
}
}命令执行器
1
2
3
4
5
6
7
8
9
10
11
12public class Invoker {
private Queue<Command> commands = new LinkedList<>();
public void addCommand(Command command) {
commands.add(command);
}
public void executeCommands() {
while (!commands.isEmpty()) {
Command command = commands.poll();
command.execute();
}
}
}使用
1
2
3
4
5
6public static voids main(String[] args) {
Invoker invoke = new Invoker();
invoke.addCommand(new FaintEnemyCommand()); // 添加眩晕敌人的命令
invoke.addCommand(new AttackEnemyCommand()); // 添加攻击敌人的命令
invoke.executeCommands(); // 执行所有命令
}
⭐解释器模式(Interpreter)
解释器模式可以用来为某个语言定义它的语法,并定义 一个解释器来处理语法。
⭐实现表达式解析器
定义解析器接口
1
2
3public interface Expression {
boolean interpret(Map<String, Long> stats);
}解析器实现类
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 static class GreaterExpression implements Expression {
private final String key;
private final long value;
public GreaterExpression(String key, long value) {
this.key = key;
this.value = value;
}
public GreaterExpression(String strExpression) {
String[] elements = strExpression.trim().split("\\s+");
if (elements.length > 3 || !elements[1].trim().equals(">")) {
throw new RuntimeException("...");
}
this.key = elements[0].trim();
this.value = Long.parseLong(elements[2].trim());
}
public boolean interpret(Map<String, Long> stats) {
if (!stats.containsKey(key)) {
return false;
}
long statValue = stats.get(key);
return statValue > 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 static class LessExpression implements Expression {
private final String key;
private final long value;
public LessExpression(String key, long value) {
this.key = key;
this.value = value;
}
public LessExpression(String strExpression) {
String[] elements = strExpression.trim().split("\\s+");
if (elements.length < 3 || !elements[1].trim().equals("<")) {
throw new RuntimeException("...");
}
this.key = elements[0].trim();
this.value = Long.parseLong(elements[2].trim());
}
public boolean interpret(Map<String, Long> stats) {
if (!stats.containsKey(key)) {
return false;
}
Long statValue = stats.get(key);
return statValue < 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 static class EqualsExpression implements Expression {
private final String key;
private final long value;
public EqualsExpression(String key, long value) {
this.key = key;
this.value = value;
}
public EqualsExpression(String strExpression) {
String[] elements = strExpression.trim().split("\\s+");
if (elements.length < 3 || !elements[1].trim().equals("==")) {
throw new RuntimeException("...");
}
this.key = elements[0].trim();
this.value = Long.parseLong(elements[2].trim());
}
public boolean interpret(Map<String, Long> stats) {
if (!stats.containsKey(key)) {
return false;
}
Long statValue = stats.get(key);
return statValue == 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
27
28
29
30
31// and解析器
public static class AndExpression implements Expression {
private List<Expression> expressions = new ArrayList<>();
public AndExpression(List<Expression> expressions) {
this.expressions = expressions;
}
public AndExpression(String strExpression) {
String[] elements = strExpression.split("&&");
for (String str : elements) {
if (str.contains(">")) {
expressions.add(new GreaterExpression(str));
} else if (str.contains("<")) {
expressions.add(new LessExpression(str));
} else if (str.contains("==")) {
expressions.add(new EqualsExpression(str));
} else {
throw new RuntimeException("...");
}
}
}
public boolean interpret(Map<String, Long> stats) {
for (Expression expression : expressions) {
if (!expression.interpret(stats)) {
return false;
}
}
return true;
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22// or解析器
public static class OrExpression implements Expression {
private List<Expression> expressions = new ArrayList<>();
public OrExpression(List<Expression> expressions) {
this.expressions = expressions;
}
public OrExpression(String strExpression) {
String[] strExpressions = strExpression.split("\\|\\|");
for (String expr : strExpressions) {
expressions.add(new AndExpression(expr));
}
}
public boolean interpret(Map<String, Long> stats) {
for (Expression expr : expressions) {
if (expr.interpret(stats)) {
return true;
}
}
return false;
}
}解析器入口
1
2
3
4
5
6
7
8
9public static class AlertRuleInterpret {
private final Expression expression;
public AlertRuleInterpret(String ruleExpression) {
this.expression = new OrExpression(ruleExpression);
}
public boolean interpret(Map<String, Long> stats) {
return expression.interpret(stats);
}
}使用
1
2
3
4
5
6
7
8
9
10
11
12// 使用
public static void main(String[] args) {
String rule = "key1 > 100 || key2 == 50 && key3 == 30";
Map<String, Long> stats = new HashMap<>();
stats.put("key1", 100L);
stats.put("key2", 50L);
stats.put("key3", 20L);
stats.put("key4", 80L);
AlertRuleInterpret alertRuleInterpret = new AlertRuleInterpret(rule);
boolean result = alertRuleInterpret.interpret(stats);
System.out.println(result);
}
中介模式(Mediator)
中介模式就是把,一组对象之间的依赖关系,从多对多,转换成一对多。微服务中的注册中心,就可以理解为是一种中介模式。
实现UI控制器
定义中介接口
1
2
3public interface Mediator {
void handleEvent(Component component, Event event);
}定义中介实现类
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
29public class LandingPageDialog implements Mediator {
private Button loginButton;
private Button registerButton;
private Input usernameInput;
private Input passwordInput;
private Selection selection;
public void handleEvent(Componment component, Event event) {
if (component.equals(loginButton)) {
String username = usernameInput.text();
String password = passwordInput.text();
// ...
} else if (component.equals(registerButton)) {
String username = usernameInput.text();
String password = passwordInput.text();
// 校验数据
// 业务处理
} else if (component.equals(selection)) {
String selectionItem = selection.select();
if (selectionItem.equals("login")) {
usernameInput.show();
passwordInput.show();
// ...
}
}
}
}使用
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
38public class UIControl {
private static final String LOGIN_BUTTON_ID = "login_button";
private static final String REGISTER_BUTTON_ID = "register_button";
private static final String USERNAME_INPUT_ID = "username_input";
private static final String PASSWORD_INPUT_ID = "password_input";
private static final String SELECTION_ID = "selection";
public static void main(String[] args) {
Button loginButton = (Button) findComponmentById(LOGIN_BUTTON_ID);
Button registerButton = (Button) findComponmentById(REGISTER_BUTTON_ID);
Input usernameInput = (Input) findComponmentById(USERNAME_INPUT_ID);
Input passwordInput = (Input) findComponmentById(PASSWORD_INPUT_ID);
Selection selection = (Selection) findComponmentById(SELECTION_ID);
Mediator dialog = new LandingPageDialog();
dialog.setLoginButton(loginButton);
dialog.setRegisterButton(registerButton);
dialog.setUsernameInput(usernameInput);
dialog.setPasswordInput(passwordInput);
dialog.setSelection(selection);
loginButton.setOnClickListener(new OnClickListener() {
public void onClick() {
dialog.handleEvent(loginButton, Event.CLICK);
}
});
registerButton.setOnClickListener(new OnClickListener() {
public void onClick() {
dialog.handleEvent(registerButton, Event.REGISTER);
}
});
}
}