代理模式

它在不改变原始类(或叫被代理类)代码的情况下,通过引入代理类来给原始类附加功能.

应用场景

  1. 业务系统的非功能性需求开发, 比如:监控、统计、鉴权、限流、事务、幂等、日志。
  2. 代理模式在 RPC、缓存中的应用
  • RPC 框架也可以看作一种代理模式,GoF 的《设计模式》一书中把它称作远程代理。通过远程代理,将网络通信、数据编解码等细节隐藏起来。
  • 基于 Spring 框架来开发,可以在 AOP 切面中完成接口缓存的功能。

java动态代理示例代码

  1. public class MetricsCollectorProxy {
  2. private MetricsCollector metricsCollector;
  3. public MetricsCollectorProxy() {
  4. this.metricsCollector = new MetricsCollector();
  5. }
  6. public Object createProxy(Object proxiedObject) {
  7. Class<?>[] interfaces = proxiedObject.getClass().getInterfaces();
  8. DynamicProxyHandler handler = new DynamicProxyHandler(proxiedObject);
  9. return Proxy.newProxyInstance(proxiedObject.getClass().getClassLoader(), interfaces, handler);
  10. }
  11. private class DynamicProxyHandler implements InvocationHandler {
  12. private Object proxiedObject;
  13. public DynamicProxyHandler(Object proxiedObject) {
  14. this.proxiedObject = proxiedObject;
  15. }
  16. @Override
  17. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  18. long startTimestamp = System.currentTimeMillis();
  19. Object result = method.invoke(proxiedObject, args);
  20. long endTimeStamp = System.currentTimeMillis();
  21. long responseTime = endTimeStamp - startTimestamp;
  22. String apiName = proxiedObject.getClass().getName() + ":" + method.getName();
  23. RequestInfo requestInfo = new RequestInfo(apiName, responseTime, startTimestamp);
  24. metricsCollector.recordRequest(requestInfo);
  25. return result;
  26. }
  27. }
  28. }
  29. //MetricsCollectorProxy使用举例
  30. MetricsCollectorProxy proxy = new MetricsCollectorProxy();
  31. IUserController userController = (IUserController) proxy.createProxy(new UserController());

其余参考javaGuide吧, 感觉这个模式除了spring AOP以外, 在实际场景中的应用有限

桥接模式

  • 将抽象和实现解耦,让它们可以独立变化.
  • 一个类存在两个(或多个)独立变化的维度,我们通过组合的方式,让这两个(或多个)维度可以独立进行扩展。

应用示例

  1. public enum NotificationEmergencyLevel {
  2. SEVERE, URGENCY, NORMAL, TRIVIAL
  3. }
  4. public class Notification {
  5. private List<String> emailAddresses;
  6. private List<String> telephones;
  7. private List<String> wechatIds;
  8. public Notification() {}
  9. public void setEmailAddress(List<String> emailAddress) {
  10. this.emailAddresses = emailAddress;
  11. }
  12. public void setTelephones(List<String> telephones) {
  13. this.telephones = telephones;
  14. }
  15. public void setWechatIds(List<String> wechatIds) {
  16. this.wechatIds = wechatIds;
  17. }
  18. public void notify(NotificationEmergencyLevel level, String message) {
  19. if (level.equals(NotificationEmergencyLevel.SEVERE)) {
  20. //...自动语音电话
  21. } else if (level.equals(NotificationEmergencyLevel.URGENCY)) {
  22. //...发微信
  23. } else if (level.equals(NotificationEmergencyLevel.NORMAL)) {
  24. //...发邮件
  25. } else if (level.equals(NotificationEmergencyLevel.TRIVIAL)) {
  26. //...发邮件
  27. }
  28. }
  29. }
  30. //在API监控告警的例子中,我们如下方式来使用Notification类:
  31. public class ErrorAlertHandler extends AlertHandler {
  32. public ErrorAlertHandler(AlertRule rule, Notification notification){
  33. super(rule, notification);
  34. }
  35. @Override
  36. public void check(ApiStatInfo apiStatInfo) {
  37. if (apiStatInfo.getErrorCount() > rule.getMatchedRule(apiStatInfo.getApi()).getMaxErrorCount()) {
  38. notification.notify(NotificationEmergencyLevel.SEVERE, "...");
  39. }
  40. }
  41. }

使用桥接模式优化后:

  1. public interface MsgSender {
  2. void send(String message);
  3. }
  4. public class TelephoneMsgSender implements MsgSender {
  5. private List<String> telephones;
  6. public TelephoneMsgSender(List<String> telephones) {
  7. this.telephones = telephones;
  8. }
  9. @Override
  10. public void send(String message) {
  11. //...
  12. }
  13. }
  14. public class EmailMsgSender implements MsgSender {
  15. // 与TelephoneMsgSender代码结构类似,所以省略...
  16. }
  17. public class WechatMsgSender implements MsgSender {
  18. // 与TelephoneMsgSender代码结构类似,所以省略...
  19. }
  20. public abstract class Notification {
  21. protected MsgSender msgSender;
  22. public Notification(MsgSender msgSender) {
  23. this.msgSender = msgSender;
  24. }
  25. public abstract void notify(String message);
  26. }
  27. public class SevereNotification extends Notification {
  28. public SevereNotification(MsgSender msgSender) {
  29. super(msgSender);
  30. }
  31. @Override
  32. public void notify(String message) {
  33. msgSender.send(message);
  34. }
  35. }
  36. public class UrgencyNotification extends Notification {
  37. // 与SevereNotification代码结构类似,所以省略...
  38. }
  39. public class NormalNotification extends Notification {
  40. // 与SevereNotification代码结构类似,所以省略...
  41. }
  42. public class TrivialNotification extends Notification {
  43. // 与SevereNotification代码结构类似,所以省略...
  44. }

装饰器模式

这部分代码示例参考java的IO
装饰器模式主要解决继承关系过于复杂的问题,通过组合来替代继承。它主要的作用是给原始类添加增强功能。

装饰器模式还有一个特点,那就是可以对原始类嵌套使用多个装饰器。为了满足这个应用场景,在设计的时候,装饰器类需要跟原始类继承相同的抽象类或者接口。

  1. InputStream in = new FileInputStream("/user/wangzheng/test.txt");
  2. InputStream bin = new BufferedInputStream(in);
  3. DataInputStream din = new DataInputStream(bin);
  4. int data = din.readInt();

装饰器模式与代理模式的区别

你是一个优秀的歌手,只会唱歌这一件事,不擅长找演唱机会,谈价钱,搭台,这些事情你可以找一个经纪人帮你搞定,经纪人帮你做好这些事情你就可以安稳的唱歌了,让经纪人做你不关心的事情这叫代理模式。

你老爱记错歌词,歌迷和媒体经常吐槽你没有认真对待演唱会,于是你想了一个办法,买个高端耳机,边唱边提醒你歌词,让你摆脱了忘歌词的诟病,高端耳机让你唱歌能力增强,提高了基础能力这叫装饰者模式。

Decorator关注为对象动态的添加功能, Proxy关注对象的信息隐藏及访问控制. Decorator体现多态性, Proxy体现封装性.

代理模式中,代理类附加的是跟原始类无关的功能,而在装饰器模式中,装饰器类附加的是跟原始类相关的增强功能。

适配器模式

将不兼容的接口转换为可兼容的接口,让原本由于接口不兼容而不能一起工作的类可以一起工作.

分为类适配器和对象适配器, 比如A类方法和B接口方法不适配;

  • 创建类适配器Adapter extends A implements B, 再重写B中方法
  • 创建对象适配器Adapter implements B , 组合A对象, 使用A对象方法来实现B中方法 ```java

// 类适配器: 基于继承 public interface ITarget { void f1(); void f2(); void fc(); }

public class Adaptee { public void fa() { //… } public void fb() { //… } public void fc() { //… } }

public class Adaptor extends Adaptee implements ITarget { public void f1() { super.fa(); }

public void f2() { //…重新实现f2()… }

// 这里fc()不需要实现,直接继承自Adaptee,这是跟对象适配器最大的不同点 }

// 对象适配器:基于组合 public interface ITarget { void f1(); void f2(); void fc(); }

public class Adaptee { public void fa() { //… } public void fb() { //… } public void fc() { //… } }

public class Adaptor implements ITarget { private Adaptee adaptee;

public Adaptor(Adaptee adaptee) { this.adaptee = adaptee; }

public void f1() { adaptee.fa(); //委托给Adaptee }

public void f2() { //…重新实现f2()… }

public void fc() { adaptee.fc(); } }

  1. 应用场景:
  2. 1. 封装有缺陷的接口设计
  3. 1. 统一多个类的接口设计
  4. 1. 替换依赖的外部系统
  5. 1. 兼容老版本接口
  6. 1. 适配不同格式的数据
  7. 适配器模式在 Java 日志中的应用<br />Slf4j 的出现晚于 JULJCLlog4j 等日志框架, 所以,它不仅仅提供了统一的接口定义,还提供了针对不同日志框架的适配器。对不同日志框架的接口进行二次封装,适配成统一的 Slf4j 接口定义。
  8. ```java
  9. // slf4j统一的接口定义
  10. package org.slf4j;
  11. public interface Logger {
  12. public boolean isTraceEnabled();
  13. public void trace(String msg);
  14. public void trace(String format, Object arg);
  15. public void trace(String format, Object arg1, Object arg2);
  16. public void trace(String format, Object[] argArray);
  17. public void trace(String msg, Throwable t);
  18. public boolean isDebugEnabled();
  19. public void debug(String msg);
  20. public void debug(String format, Object arg);
  21. public void debug(String format, Object arg1, Object arg2)
  22. public void debug(String format, Object[] argArray)
  23. public void debug(String msg, Throwable t);
  24. //...省略info、warn、error等一堆接口
  25. }
  26. // log4j日志框架的适配器
  27. // Log4jLoggerAdapter实现了LocationAwareLogger接口,
  28. // 其中LocationAwareLogger继承自Logger接口,
  29. // 也就相当于Log4jLoggerAdapter实现了Logger接口。
  30. package org.slf4j.impl;
  31. public final class Log4jLoggerAdapter extends MarkerIgnoringBase
  32. implements LocationAwareLogger, Serializable {
  33. final transient org.apache.log4j.Logger logger; // log4j
  34. public boolean isDebugEnabled() {
  35. return logger.isDebugEnabled();
  36. }
  37. public void debug(String msg) {
  38. logger.log(FQCN, Level.DEBUG, msg, null);
  39. }
  40. public void debug(String format, Object arg) {
  41. if (logger.isDebugEnabled()) {
  42. FormattingTuple ft = MessageFormatter.format(format, arg);
  43. logger.log(FQCN, Level.DEBUG, ft.getMessage(), ft.getThrowable());
  44. }
  45. }
  46. public void debug(String format, Object arg1, Object arg2) {
  47. if (logger.isDebugEnabled()) {
  48. FormattingTuple ft = MessageFormatter.format(format, arg1, arg2);
  49. logger.log(FQCN, Level.DEBUG, ft.getMessage(), ft.getThrowable());
  50. }
  51. }
  52. public void debug(String format, Object[] argArray) {
  53. if (logger.isDebugEnabled()) {
  54. FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray);
  55. logger.log(FQCN, Level.DEBUG, ft.getMessage(), ft.getThrowable());
  56. }
  57. }
  58. public void debug(String msg, Throwable t) {
  59. logger.log(FQCN, Level.DEBUG, msg, t);
  60. }
  61. //...省略一堆接口的实现...
  62. }

门面模式

门面模式为子系统提供一组统一的接口,定义一组高层接口让子系统更易用。

应用场景

  1. 解决易用性问题

门面模式可以用来封装系统的底层实现,隐藏系统的复杂性,提供一组更加简单易用、更高层的接口

  1. 解决性能问题

通过将多个接口调用替换为一个门面接口调用,减少网络通信成本,提高 App 客户端的响应速度。

  1. 解决分布式事务问题

组合模式

将一组对象组织(Compose)成树形结构,以表示一种“部分 - 整体”的层次结构。组合让客户端(在很多设计模式书籍中,“客户端”代指代码的使用者。)可以统一单个对象和组合对象的处理逻辑。

组合模式的设计思路,与其说是一种设计模式,倒不如说是对业务场景的一种数据结构和算法的抽象。其中,数据可以表示成树这种数据结构,业务需求可以通过在树上的递归遍历算法来实现。

其实就是这种数据结构, 可以通过递归遍历还实现一些统计等操作

  1. class TreeNode{
  2. private List<TreeNode> subNodes;
  3. }

享元模式

所谓“享元”,顾名思义就是被共享的单元。享元模式的意图是复用对象,节省内存,前提是享元对象是不可变对象。

具体来讲,当一个系统中存在大量重复对象的时候,如果这些重复的对象是不可变对象,我们就可以利用享元模式将对象设计成享元,在内存中只保留一份实例,供多处代码引用。这样可以减少内存中对象的数量,起到节省内存的目的。实际上,不仅仅相同对象可以设计成享元,对于相似对象,我们也可以将这些对象中相同的部分(字段)提取出来,设计成享元,让这些大量相似对象引用这些享元。

比如棋盘中的棋子的文字内容以及格式都是一样的, 因此可以利用享元模式做成共享的不可变对象. 一般会利用工厂方法存储这些共享对象.

享元模式 VS 单例、缓存、对象池
应用单例模式是为了保证对象全局唯一。应用享元模式是为了实现对象复用,节省内存。且享元是共享使用. 缓存是为了提高访问效率,而非复用。池化技术中的“复用”理解为“重复使用”,主要是为了节省时间。

  • 享元模式在包装类中的使用

Byte, Short, Integer, Long的缓存池

拿Integer的代码举例
i1==i2为true, 因为当我们通过自动装箱,也就是调用 valueOf() 来创建 Integer 对象的时候,如果要创建的 Integer 对象的值在 -128 到 127 之间,会从 IntegerCache 类中直接返回

  1. Integer i1 = 56;
  2. Integer i2 = 56;
  3. Integer i3 = 129;
  4. Integer i4 = 129;
  5. System.out.println(i1 == i2);
  6. System.out.println(i3 == i4);
  1. public static Integer valueOf(int i) {
  2. if (i >= IntegerCache.low && i <= IntegerCache.high)
  3. return IntegerCache.cache[i + (-IntegerCache.low)];
  4. return new Integer(i);
  5. }
  • 享元模式在String中的应用

字符串常量池, 这里就不多说了