0%

设计模式

⭐工厂模式(Factory)

对象的创建逻辑比较复杂,就可以考虑使用工厂模式,把对象的创建过程和使用分开。

比如需要通过if-else动态创建不同的子类对象,就可以考虑把if-else的判断逻辑放到工厂类中。

比如单个对象本身的创建过程比较复杂,需要组合其它对象,做各种初始化操作,也可以把这些逻辑封装到工厂类中。

⭐简单工厂

使用工厂模式实现解析不同格式的配置文件

  1. 定义工厂类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public 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;
    }
    }
  2. 使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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. 定义工厂方法类

    1
    2
    3
    public interface IRuleConfigParserFactory {
    IRuleConfigParser createPraser();
    }
  2. 具体的工厂类

    1
    2
    3
    4
    5
    6
    7
    // json格式配置文件的工厂类
    public class JsonRuleConfigParserFactory implements IRuleConfigParserFactory {
    @Override
    public IRuleConfigParser createParser() {
    return new JsonRuleConfigParser();
    }
    }
    1
    2
    3
    4
    5
    6
    7
    // xml格式配置文件的工厂类
    public class XmlRuleConfigParserFactory implements IRuleConfigParserFactory {
    @Override
    public IRuleConfigParser createParser() {
    return new XmlRuleConfigParser();
    }
    }
    1
    2
    3
    4
    5
    6
    7
    // yaml格式配置文件的工厂类
    public class YamlRuleConfigParserFactory implements IRuleConfigParserFactory {
    @Override
    public IRuleConfigParser createParser() {
    return new YamlRuleConfigParser();
    }
    }
  3. 聚合工厂类

    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;
    }
    }
  4. 使用

    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. 定义抽象工厂

    1
    2
    3
    4
    5
    6
    public interface IConfigParserFactory {
    // 创建rule配置文件解析器工厂类
    IRuleConfigParser createRuleParser();
    // 创建system配置文件解析器工厂类
    ISystemConfigParser createSystemParser();
    }
  2. 抽象工厂实现类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class JsonConfigParserFactory implements IConfigParserFactory {
    // 创建json格式的rule配置文件解析器
    @Override
    public IRuleConfigParser createRuleParser() {
    return new JsonRuleConfigParser();
    }

    // 创建json格式的system配置文件解析器
    @Override
    public ISystemConfigParser createSystemParser() {
    return new JsonSystemConfigParser();
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class XmlConfigParserFactory implements IConfigParserFactory {
    // 创建xml格式的rule配置文件解析器
    @Override
    public IRuleConfigParser createRuleParser() {
    return new XmlRuleConfigParser();
    }

    // 创建xml格式的system配置文件解析器
    @Override
    public ISystemConfigParser createSystemParser() {
    return new XmlSystemConfigParser();
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class YamlConfigParserFactory implements IConfigParserFactory {
    // 创建yaml格式的rule配置文件解析器
    @Override
    public IRuleConfigParser createRuleParser() {
    return new YamlRuleConfigParser();
    }

    // 创建yaml格式的system配置文件解析器
    @Override
    public ISystemConfigParser createSystemParser() {
    return new YamlSystemConfigParser();
    }
    }
  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
    public 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
2
3
4
5
6
7
public class Singleton {
private static final Singleton singleton = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return singleton;
}
}

有些人认为提前初始化实例是一种浪费资源的行为,应该等用到的时候再去初始化,不过如果初始化的逻辑比较复杂,耗时比较长,这就会影响到系统的性能。

用饿汉式把一些耗时的操作,提前到程序启动的时候完成,这样就能避免初始化的性能问题。

懒汉式

懒汉式可以实现延迟加载,就是在用到的时候,才去创建实例并初始化

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

但是在多线程环境中,饿汉式就需要通过加锁来确保只会创建一个实例,如果这个类经常被调用,就会导致频繁的加锁和解锁,会影响到系统的性能。

双重检测机制

双重检测也可以实现延迟加载,主要是为了解决,多线程环境下懒汉式并发度很低的问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Singleton {
private static Singleton singleton;
private Singleton() {}
public static Singleton getInstance() {
if (singleton == null) {
synchronized(Singleton.class) { // 对Singleton这个类加锁,会锁住所有singleton对象
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}

因为指令重排,可能会导致Singleton对象被new出来之后,还没来得及初始化,就被使用了。这个问题可以通过给成员变量加上volatile关键字,禁止指令重排。

实际上这个问题只有很低的JDK版本才会有,高版本的JDK内部已经解决了这个问题,就是把创建对象的new操作,和初始化操作设计为原子操作,就可以禁止指令重排。

静态内部类

静态内部类这种方式也可以实现延迟加载

1
2
3
4
5
6
7
8
9
10
11
12
public class Singleton {
private Singleton() {} // 私有构造方法

private static class SingletonHolder { // 创建静态内部类
private static final Singleton singleton = new Singleton(); // 在静态内部类中创建实例
}

public static Singleton getInstance() {
return SingletonHolder.singleton;
}

}

静态内部类就是在类中定义一个静态内部类,在这个内部类中定义初始化的逻辑。当外部类被加载的时候,并不会创建内部类的实例对象,只有调用getInstance()方法的时候,内部类才会被加载,这个时候才会创建外部类的实例对象。

这个创建过程是由JVM来完成的,是线程安全的,也可以实现延迟加载。

枚举

基于枚举来实现单例,是最简单的一种方式了,就是通过Java枚举本身的特点,来保证实例创建的线程安全性和实例唯一性。

1
2
3
public enum Singleton {
INSTANCE;
}

⭐建造者模式(Builder)

建造者模式主要是用来创建一些比较复杂的对象。

数据填充和校验

如果一个类中有很多属性,有些属性是必填的,有些是非必填的。比较简单的做法就是把必填属性放在构造函数中,其它非必填的用set()方法设置。如果必填的属性有很多,就会导致构造函数参数列表很长,使用起来就很容易出错。

  1. 写法

    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
    public 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;
    }
    }
    }
  2. 使用

    1
    2
    3
    4
    5
    6
    7
    8
    public static void main(String[] args) {
    ResourcePoolConfig config = ResourcePoolConfig.Builder()
    .setName("test")
    .setMaxTotal(10)
    .setMaxIdle(5)
    .setMinIdle(2)
    .build();
    }

原型模式(Property)

如果一个对象的创建成本比较大,并且存在其它大部分字段都相同的对象,这种情况可以利用已有的对象,通过复制的方式来创建新的对象,这就叫原型模式

原型模式有两种实现方法,深拷贝和浅拷贝。

  • 浅拷贝只会复制对象中基本数据类型和引用对象的内存地址,不会递归的复制引用对象,以及引用对象的引用对象…。
  • 深拷贝得到的是一份完全独立的对象,所以深拷贝会更加耗时,更耗内存空间。

实现缓存功能

很多时候我们会在系统启动的时候,从数据库中加载一些数据到内存中,用来做缓存。为了保证缓存中数据的正确性,通常会定期更新内存中的数据,这个时候就可以利用原型模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class Property {
private ConcurrentHashMap<String, SearchWorld> currentKeywords = new CurrentHashMap<String, SearchWorld>();
private long lastUpdateTime = -1;

// 刷新缓存
public void refresh() {
HashMap<String, SearchWord> newKeywords = (HashMap<String, SearchWord>) currentKeywods.clone();
// 从数据库中查询发生变更的数据(更新时间 > lastUpdateTime),放入到newKeywords中
List<SearchWord> toBeUpdatedSearchWords = getSearchWordFromDB(lastUpdateTime);
long maxNewUpdateTime = lastUpdateTime;
for (SearchWord searchWord : toBeUpdatedSearchWords) {
if (searchWord.getLastUpdateTime > maxNewLastUpdateTime) {
maxNewLastUpdateTime = searchWord.getLastUpdateTime();
}
// 删除旧数据
if (newKeywods.containsKey(searchWord.getKeyword())) {
newKeywods.remove(searchWord.getKeyword(), searchWord);
}
// 覆盖掉原来的对象
newKeywods.put(searchWord.getKeyword(), searchWord);
}
lastUpdateTime = maxNewLastUpdateTime;
currentKeywords = newKeywords;
}

// TODO: 从数据库中查询更新时间>lastUpdateTime的数据
private List<SearchWord> getSearchWordFromDB(long lastUpdateTime) {
return null;
}

}

⭐代理模式(Proxy)

就是在不修改原代码的情况下,通过代理类来给原始类附加额外的功能

代理模式又分为:静态代理和动态代理。

⭐静态代理

为每个类都创建一个代理类,并且每个代理类都要把原始类中的方法实现一遍,再附加一些额外的代理逻辑。

  1. 原始类:

    1
    2
    3
    4
    5
    public class UserController {
    public void login(String username, String password) {
    // 登录逻辑...
    }
    }
  2. 代理类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class UserControllerProxy {
    private final UserController userController;

    public UserControllerProxy(UserController userController) {
    this.userController = userController;
    }

    public void login(String username, String password) {
    // 委托
    userController.login(username, password);
    // 添加一些额外的逻辑,比如登录成功后发送短信通知
    // ...
    }
    }
  3. 使用:

    1
    2
    3
    4
    public static void main(String[] args) {
    UserControllerProxy userControllerProxy = new UserControllerProxy(new UserController());
    userControllerProxy.login("zhangsan","123456");
    }

⭐动态代理

就是不预先为每个类创建代理类,而是在运行的时候,动态的创建原始类对应的代理类,然后在系统中用代理类替换掉原始类。

Java可以通过InvocationHandler接口和Proxy类来实现动态代理

  1. 代理类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class UserProxy {
    public Object createProxy(Object proxyObject) {
    Class<?>[] interfaces = proxyObject.getClass().getInterfaces();
    // 创建动态代理处理器
    DynamicProxyHandler handler = new DynamicProxyHandler(proxyObject);
    // 创建代理类
    return Proxy.newProxyInstance(proxyObject.getClass().getClassLoader(), interfaces);
    }
    }
  2. 动态代理处理器:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class DynamicProxyHandler implements InvocationHandler {
    private Object proxyObject;

    public DynamicProxyHandler(Object proxyObject) {
    this.proxyObject = proxyObject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) {
    // 代理逻辑...
    Object result = method.invoke(proxyObject, args);
    return result;
    }
    }
  3. 使用:

    1
    2
    3
    4
    5
    public static void main(String[] args) {
    UserProxy proxy = new UserProxy();
    UserController userController = (UserController) proxy.createProxy(new UserController())
    userController.login();
    }

⭐桥接模式(Bridge)

将抽象部分和实现部分分离,让它们都可以独立的变化。

⭐消息推送功能

  1. 创建消息推送接口:

    1
    2
    3
    public interface MsgSender {
    void send(String message);
    }
  2. 消息推送实现类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 以短信的方式发送消息
    public class TelephoneMsgSender implements MsgSender {
    private List<String> telephones;

    @Override
    public void send(String message) {
    // 发送短信逻辑
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 以邮件的方式发送消息
    public class EmailMsgSender implements MsgSender {
    private List<String> emails;

    @Override
    public void send(String message) {
    // 发送邮件逻辑
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 以微信的方式发送消息
    public class WechatMsgSender implements MsgSender {
    private List<String> wechats;

    @Override
    public void send(String message) {
    // 发送微信逻辑
    }
    }
  3. 创建桥接类,根据消息的紧急程度,将消息发送到不同渠道(不同紧急程度的消息和发送渠道的对应关系,可以动态配置)

    1. 定义消息通知抽象类

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      public abstract class Notification {
      // 消息发送渠道
      protected MsgSender msgSender;

      public Notification(MsgSender msgSender) {
      this.msgSender = msgSender;
      }

      public abstract void notify(String message);
      }
    2. 消息通知具体实现类

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      // 严重通知级别
      public class SevereNotification extends Notification {
      public SevereNotification(MsgSender msgSender) {
      super(msgSender);
      }

      @Override
      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);
      }

      @Override
      public void notify(String message) {
      msgSender.send(message);
      }
      }
  4. 使用

    1
    2
    3
    4
    public static void main(String[] args) {
    Notification notifiaction = new UrgencyNotification(new EmailSender());
    notification.notify("这是一条紧急信息...");
    }

⭐装饰器模式(Decoreator)

装饰器模式跟代理模式的结构基本一样,只不过代理模式是为原始类增加一些额外的功能,装饰器模式是增强原始类现有的功能

⭐增强登录功能

  1. 用户登录接口:

    1
    2
    3
    public interface LoginApi {
    boolean login(String username, String password);
    }
  2. 原始类:

    1
    2
    3
    4
    5
    6
    public class LoginController implements LoginApi {
    @Override
    public boolean login(String username, String password) {
    // ...登录逻辑
    }
    }
  3. 发送邮件的装饰器:

    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;
    }

    @Override
    public boolean login(String username, String password) {
    boolean result = loginApi.login(username, password);
    if (result) {
    // 登陆成功,发送邮件
    sender.send("登陆成功...");
    }
    return result;
    }
    }
  4. 生成登录记录的装饰器:

    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;
    }

    @Override
    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;
    }

    }
  5. 使用:

    1
    2
    3
    4
    5
    6
    7
    8
    public static void main(String[] args) {
    LoginApi loginApi = new LoginControllerWithCreateRecord(
    new LoginControllerWithSendEmail(
    new LoginController()
    )
    );
    loginApi.login("zhangsan","123456");
    }

⭐适配器模式(Adapter)

适配器模式就是用一个新类,把原来的类包装一下,用来解决两个不兼容的类可以在一起工作

比如某个功能需要依赖多个外部系统,这些外部系统接口的参数和返回值格式都不一样,这时候就可以通过适配器模式,把它们的接口适配为统一的接口定义。

⭐数据聚合功能

  1. 从多种数据库查询数据:

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

    }
    }
  2. 定义适配器接口:

    1
    2
    3
    public interface IDataProvider {
    List<String> find(String param);
    }
  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
    public class MySQLProviderAdapter implements IDataProvider {	// MySQL适配器
    private MySQLPrivider mysqlProvider = new MySQLProvider();
    @Override
    public List<String> find(String str) {
    return mysqlProvider.find(str);
    }
    }

    public class OracleProviderAdapter implements IDataProvider { // Oracle适配器
    private OracleProvidedr oracleProvider = new OracleProvider();
    @Override
    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();
    @Override
    public List<String> find(String str) {
    String data = redisProvider.get(str);
    return Collections.singletonList(data);
    }
    }
  4. 聚合适配器:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public 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;
    }
    }
  5. 使用:

    1
    2
    3
    4
    5
    6
    7
    8
    public 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. 用户注册门面服务

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class UserRegisterFacade {
    private final UserService userService;
    private final RoleService roleService;

    public void post(User user) {
    userService.create(user); // 创建用户
    roleService.setRole(user); // 分配角色
    }
    }
  2. 用户注册服务接口

    1
    2
    3
    4
    5
    6
    7
    8
    public class SystemService {
    private final UserRegisterFacade registerFacade;
    public boolean register(User user) {
    // 调用门面服务中的操作
    registerFacade.post(user);
    // 执行注册后续的操作...
    }
    }

⭐组合模式(Compose)

组合模式可以很方便的把不同类型的对象组合成树形结构。

⭐实现结构树

  1. 定义组织结构抽象类:

    1
    2
    3
    4
    5
    6
    7
    public abstract class Organization {	// 用来做一些额外的处理逻辑
    protected BigDecimal salary; // 工资
    protected int age; // 年龄
    public abstract BigDecimal calculateSalary(); // 计算部门所有工资总和(包括下级部门)
    public abstract int calculateAverageAge(); // 计算部门平均年龄
    public abstract boolean isDeparment(); // 是否是部门, 部门:true,员工:false
    }
  2. 部门类:

    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
    public class Deparment implements Organization {
    private long id;
    private String name;
    private List<Organization> subOrganizations = new ArrayList<>();

    public void addOrganization(Organization org) {
    subOrganizations.add(org);
    }

    @Override
    public BigDecimal calculateSalary() {
    BigDecimal totalSalary = BigDecimal.ZERO;
    for (Organization org : subOrganizations) {
    totalSalary.add(org.getSalary());
    }
    return totalSalary;
    }

    @Override
    public int calculateAverageAge() {
    int totalAge = 0;
    int employeeCount = 0;
    for (Organization org : subOrganizations) {
    if (!isDeparment()) {
    totalAge += org.getAge();
    employeeCount++;
    }
    }
    return totalAge / employeeCount;
    }
    @Override
    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;
    }
    }
  3. 员工类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public class Employee implements Organization {
    private long id;
    private String name;

    @Override
    public BigDecimal calculateSalary() {
    return this.salary();
    }
    @Override
    public int calculateAverageAge() {
    return this.age;
    }
    @Override
    public boolean isDeparment() {
    return false;
    }

    }
  4. 使用:

    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
    public 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. 定义享元类:

    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
    }
    }
  2. 享元工厂类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public 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);
    }
    }
  3. 棋子类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public 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
    }
  4. 棋盘类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public 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. 定义观察者接口:

    1
    2
    3
    public interface RegObserver {
    void handleRegSuccess(long userId); // 用户注册成功时的事件通知函数
    }
  2. 定义观察者实现类:

    1
    2
    3
    4
    5
    6
    7
    8
    // 为新用户生成密码
    public class RegPromotionObserver implements RegObserver {
    private PromotionService promitionService;
    @Override
    public void handleRegSuccess(long userId) {
    promotionService.generatePassword(userId);
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    // 发送欢迎邮件
    public class RegNotificationObserver implements RegObserver {
    private NotificationService notificationService;
    @Override
    public void handleRegSuccess(long userId) {
    notificationService.sendMessage(userId, "Welcome...");
    }
    }
  3. 用户注册接口:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public 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);
    }
    }
    }
  4. 使用:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public 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. 定义标记注解,用来标明观察者中,哪个函数可以接收消息。

    1
    2
    3
    4
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface Subscribe {
    }
  2. 用来表示@Subscribe 标注的方法。target表示观察者类,method表示方法。主要用在ObserverRegistry观察者注册表中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public 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();
    }
    }
    }
  3. 注册表

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    public 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;
    }

    }
  4. 事件总线

    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() {
    @Override
    public void run() {
    observerAction.execute(event);
    }
    });
    }
    }
    }
    1
    2
    3
    4
    5
    6
    // 异步事件总线
    public class AsyncEventBus {
    public AsyncEventBus(Executor executor) {
    super(executor);
    }
    }
  5. 使用

    1
    2
    3
    4
    5
    public 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. 定义模板类和模板方法

    1
    2
    3
    4
    5
    6
    7
    8
    public abstract class AbstractTemplate {
    public final void templateMethod() { // 定义模板方法,规定业务流程
    f1();
    f2();
    }
    public abstract void f1(); // 流程1
    public abstract void f2(); // 流程2
    }
  2. 定义子类实现流程1和流程2的逻辑

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class Concrete1 extends AbstractTemplate {
    @Override
    public void f1() {
    //...
    }
    @Override
    public void f2() {
    //...
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class Concrete2 extends AbstractTemplate {
    @Override
    public void f1() {
    //...
    }
    @Override
    public void f2() {
    //...
    }
    }
  3. 使用

    1
    2
    3
    4
    5
    6
    7
    public static void main(String[] args) {
    AbstractTemplate template = new Concreate1();
    template.templateMethod();

    template = new Concreate2();
    template.templateMethod();
    }

⭐策略模式(Strategy)

就是定义一个策略接口,然后再根据不同的逻辑定义一些实现这个接口的策略类。这样就可以很灵活的替换不同的策略。

策略模式一般都会通过类型,来判断创建哪个策略来使用,可以可以配合工厂模式,把创建策略的逻辑放到工厂类中。

如果策略类是无状态的,不包含成员变量,这样的策略对象是可以共享的,不需要每次都创建新的对象,可以预先创建好策略对象,缓存到工厂类中。

⭐策略模式结构

  1. 定义策略接口:

    1
    2
    3
    public interface Strategy {
    void method();
    }
  2. 定义策略实现类:

    1
    2
    3
    4
    5
    6
    7
    // 策略类1
    public class ConcreteStrategyA implements Strategy {
    @Override
    public void method() {
    // 具体的逻辑...
    }
    }
    1
    2
    3
    4
    5
    6
    7
    // 策略类2
    public class ConcreteStrategyB implements Strategy {
    @Override
    public void method() {
    // 具体的逻辑...
    }
    }
  3. 定义策略工厂类:

    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);
    }

    }
  4. 使用:

    1
    2
    3
    4
    public static void main(String[] args) {
    Strategy strategy = StrategyFactory.getStrategy("A");
    strategy.method();
    }

⭐责任链模式(Chain Of Responsibility)

就是多个类处理同一个请求,一个请求先经过A处理,然后再把请求传递给B处理,B再传递给C处理。每个类都承担着自己的职责。

Servlet中的Filter和Spring中的Interceptor都使用了责任链模式。

⭐实现过滤敏感信息功能

  1. 定义敏感信息过滤接口

    1
    2
    3
    public interface SensitiveFilter {
    boolean doFilter(Content content);
    }
  2. 根据数据类型定义不同的过滤器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 文本过滤器
    public class SensitiveTextFilter implements SensitiveFilter {
    @Override
    public boolean doFilter(Content content) {
    boolean legal = true;
    // 判断用户输入的文本是否包含敏感词汇
    return legal;
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 图片过滤器
    public class SensitiveImageFilter implements SensitiveFilter {
    @Override
    public boolean doFilter(Content content) {
    boolean legal = true;
    // 判断用户上传的图片是否包含敏感信息
    return legal;
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 视频过滤器
    public class SensitiveVideoFilter implements SensitiveFilter {
    @Override
    public boolean doFilter(Content content) {
    boolean legal = true;
    // 判断用户上传的视频是否包含敏感信息
    return legal;
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 音频过滤器
    public class SensitiveAudioFilter implements SensitiveFilter {
    @Override
    public boolean doFilter(Content content) {
    boolean legal = true;
    // 判断用户上传的音频是否包含敏感信息
    return legal;
    }
    }
  3. 定义责任链类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public 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;
    }
    }
  4. 使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public 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. 定义马里奥接口

    1
    2
    3
    4
    5
    6
    7
    8
    public interface IMario {
    // 定义事件
    void obtainMushroom(); // 获得蘑菇事件
    void obtainCape(); // 获得斗篷事件
    void obtainFireFlower(); // 获得火焰花事件
    void meetMonster(); // 遇到怪物事件
    void topBrick(); // 顶砖头事件
    }
  2. 马里奥实现类,并根据马里奥状态重写事件逻辑

    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;
    }
    @Override
    public void obtainMushroom() {
    //小马里奥获得蘑菇,变为超级马里奥
    marioStateMachine.setState(new SuperMario(marioStateMachine));
    }
    @Override
    public void obtainCape() {
    // 小马里奥获得斗篷,变成斗篷马里奥
    marioStateMachine.setState(new CapeMario(marioStateMachine));
    }
    @Override
    public void obtainFireFlower() {
    // 小马里奥获得火焰花,变成火焰马里奥
    marioStateMachine.setState(new FireMario(marioStateMachine));
    }
    @Override
    public void meetMonster() {
    // 小马里奥遇到怪物,减少一条命
    marioStateMachine.descreaseLife();
    }
    @Override
    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;
    }

    @Override
    public void obtainMushroom() {
    // 超级马里奥获得蘑菇,积分加100
    marioStateMachine.increaseScore(100);
    }
    @Override
    public void obtainCape() {
    // 超级马里奥获得斗篷,变成斗篷马里奥
    marioStateMachine.setState(new CapeMario(marioStateMachine));
    }
    @Override
    public void obtainFireFlower() {
    // 超级马里奥获得火焰花,变成火焰马里奥
    marioStateMachine.setState(new FireMario(marioStateMachine));
    }
    @Override
    public void meetMonster() {
    // 超级马里奥遇到怪物,变成小马里奥
    marioStateMachine.setState(new SmallMario(marioStateMachine));
    }
    @Override
    public void topBrick() {
    // 超级马里奥顶砖头,会破坏砖头
    marioStateMachine.distoryBrick();
    }

    }
  3. 定义状态机

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class MarioStateMachine {
    private IMario currentMario;

    public MarioStateMachine() {
    currentMario = new SmallMario(this); //默认是小马里奥
    }

    public void setState(IMario mario) {
    currentMario = mario;
    }
    public IMario getState() {
    return this.currentMario;
    }
    }
  4. 使用

    1
    2
    3
    4
    5
    6
    public 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 的元素才属于当前快照。

如果元素的添加时间大于快照的创建时间,说明元素是在快照之后才加入的,不属于当前快照。

如果元素的删除时间小于快照的创建时间,说明元素在快照之前就被删除掉了,也不属于当前快照。

  1. 定义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
    50
    public 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);
    }

    @Override
    public void add(E obj) {
    elements[totalSize] = obj;
    addTimestamps[totalSize] = System.currentTimeMills();
    delTimestamps[totalSize] = Long.MAX_VALUE;
    totalSize++;
    actualSize++;
    }

    @Override
    public void remove(E obj) {
    for (int i = 0; i < totalSize; i++) {
    if (elements[i].equals(obj)) {
    delTimestamps[i] = System.currentTimeMills();
    actualSize--;
    }
    }
    }

    @Override
    public E get(int i) {
    if (i >= totalSize) {
    throw new IndexOutOfBoundsException();
    }
    return (E) elements[i];
    }

    }
  2. 定义迭代器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    public class 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(); //先跳到这个迭代器快照的第一个元素
    }

    @Override
    public boolean hasNext() {
    return this.leftCount >= 0;
    }

    @Override
    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. 定义文件父类

    1
    2
    3
    4
    5
    6
    7
    public abstract class ResourceFile {
    protected String filePath;
    public ResourceFile(String filePath) {
    this.filePath = filePath;
    }
    public abstract void accept(Visitor visitor);
    }
  2. 根据文件类型定义不同的文件实现类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // PDF文件
    public class PdfFile extends ResourceFile {
    public PdfFile(String filePath) {
    super(filePath);
    }
    @Override
    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);
    }
    @Override
    public void accept(Visitor visitor) {
    visitor.visit(this);
    }
    }
  3. 定义访问者接口

    1
    2
    3
    4
    5
    6
    // 访问者
    public interface Visitor {
    void visit(PdfFile pdfFile);
    void visit(PPTFile pptFile);
    void visit(WordFile wordFile);
    }
  4. 定义访问者实现类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // 提取文件内容访问者
    public class Extractor implements Visitor {
    @Override
    public void visit(PdfFile pdfFile) {
    // 将pdf提取到txt文本
    }
    @Override
    public void visit(PPTFile pptFile) {
    // 将ppt提取到txt文本
    }
    @Override
    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 {
    @Override
    public void visit(PdfFile pdfFile) {
    // 压缩pdf文件
    }
    @Override
    public void visit(PPTFile pptFile) {
    // 压缩ppt文件
    }
    @Override
    public void visit(WordFile wordFile) {
    // 压缩word文件
    }
    }
  5. 使用

    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. 定义文本对象

    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;
    }
    }
  2. 保存用户输入的文本副本的容器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class SnapshotHolder {
    private Stack<Snapshot> snapshots = new Stack<>(); // 用来存储每次用户输入的文本对象的副本
    public Snapshot popSnapshot() {
    return snapshots.pop();
    }
    public void pushSnapshot(Snapshot snapshot) {
    snapshots.push(snapshot);
    }
    }
  3. 使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public 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. 定义命令接口

    1
    2
    3
    public interface Command {
    void execute();
    }
  2. 命令实现类

    1
    2
    3
    4
    5
    6
    7
    // 眩晕敌人
    public class FaintEnemyCommand implements Command {
    @Override
    public void execute() {

    }
    }
    1
    2
    3
    4
    5
    6
    7
    // 攻击敌人
    public class AttackEnemyCommand implements Command {
    @Override
    public void execute() {

    }
    }
  3. 命令执行器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public 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();
    }
    }
    }
  4. 使用

    1
    2
    3
    4
    5
    6
    public static voids main(String[] args) {
    Invoker invoke = new Invoker();
    invoke.addCommand(new FaintEnemyCommand()); // 添加眩晕敌人的命令
    invoke.addCommand(new AttackEnemyCommand()); // 添加攻击敌人的命令
    invoke.executeCommands(); // 执行所有命令
    }

⭐解释器模式(Interpreter)

解释器模式可以用来为某个语言定义它的语法,并定义 一个解释器来处理语法。

⭐实现表达式解析器

  1. 定义解析器接口

    1
    2
    3
    public interface Expression {
    boolean interpret(Map<String, Long> stats);
    }
  2. 解析器实现类

    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());
    }
    @Override
    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());
    }
    @Override
    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());
    }
    @Override
    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("...");
    }
    }
    }
    @Override
    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));
    }
    }
    @Override
    public boolean interpret(Map<String, Long> stats) {
    for (Expression expr : expressions) {
    if (expr.interpret(stats)) {
    return true;
    }
    }
    return false;
    }
    }
  3. 解析器入口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public 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);
    }
    }
  4. 使用

    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. 定义中介接口

    1
    2
    3
    public interface Mediator {
    void handleEvent(Component component, Event event);
    }
  2. 定义中介实现类

    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 LandingPageDialog implements Mediator {
    private Button loginButton;
    private Button registerButton;
    private Input usernameInput;
    private Input passwordInput;
    private Selection selection;

    @Override
    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();
    // ...
    }
    }
    }

    }
  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
    public 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() {
    @Override
    public void onClick() {
    dialog.handleEvent(loginButton, Event.CLICK);
    }
    });
    registerButton.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick() {
    dialog.handleEvent(registerButton, Event.REGISTER);
    }
    });


    }

    }