1.作用

对一类编码的设计经验思想;提升编码思维、复用性、可读性等。

2.设计模式分类

1.创建型模式

描述“如何创建对象”,主要特点是对象创建与使用分离。
包含:单例、工厂方法、抽象工厂、原型、建造者等5中模式;

2.结构型模式

描述“类或对象如何布局成更大结构”。
包含:代理、适配器、桥接、装饰、外观、享元、组合等7中模式;

3.行为型模式

描述“类或对象如何协作完成任务”
包含:模板方法、策略、命令、责任链、状态、观察者、中介者、迭代器、访问者、备忘录、解释器等11种模式。
image.png

3.UML类图

1.类图表示

使用一个圆角矩形表示,包含三个模块:
类名
属性:访问类型:属性名称:数据类型
行为:访问类型:属性名称:返回值类型
image.png

2.类图关系

关联关系是类之间的引用关系。包含一般关联关系、聚合关系、组合关系。

1.关联关系-单向

每个顾客都有一个地址,符号如下。
image.png

2.关联关系-双向

image.png

3.关联关系-自关联

image.png

4.聚合关系

整体与部分之间的关系。如:大学-学生。
image.png

5.组合关系

整体与部分之间的关系。关系更强、整体与部分共存亡。
image.png

6.依赖关系

image.png

7.继承关系

image.png

8.实现关系

image.png

4.七大原则

1.单一原则

模块职责单一化,降低了类的复杂度;

2.开闭原则

模块对扩展开放、对修改关闭;如使用接口、抽象类等。

3.里氏替换原则

子类继承父类之后,子类可以扩展父类,但不能修改父类原有的功能。

4.依赖倒转原则

高层模块不直接依赖底层模块,应该依赖抽象。
如下图所示,电脑组装与不同组件之间通过接口依赖。
image.png

5.接口隔离原则

类与类之间的依赖建立在最小关系上,不应该依赖自己不需要的方法。
如:实现接口需实现全部方法、继承抽象类只需要实现部分方法。

6.迪米特法则

模块之间最少了解、通过第三方进行转发调用。

7.合成复用原则

尽量使用组合、聚合等关联关系实现,之后再考虑继承。

5.创建者模式

1.单例模式

单例模式是一种访问唯一性的模式,不需要创建过多的对象,只需要保持一个实例且不需要实例化。
为何要用单例模式?
多个并发请求、只产生一个实例对象,节约内存资源。如:短信发送、验证码等。

1.实现方式

单例模式提供了两种实现方式:
饿汉式:类加载在进行单例对象的初始化创建
懒汉式:类加载不进行创建,在首次使用时才进行创建

2.饿汉式-静态变量/代码块

该方式中,通过静态变量、代码块直接声明单例实例。
会造成初始化实例后未使用,占用资源。

  1. public class Singleton{
  2. // 1、声明静态变量实例——直接创建对象
  3. private static Singleton singleton = new Singleton;
  4. // 2、声明静态变量实例——使用静态代码块进行初始化
  5. static {
  6. singleton = new Singleton;
  7. }
  8. // 私有化构造方法
  9. private Singleton(){}
  10. // 提供访问获取方法
  11. public static Singleton getInstance(){
  12. return singleton;
  13. }
  14. }

3.懒汉式-线程不安全

该方式中,在访问获取方法中进行实例化,多线程中会出现线程安全问题。
多线程访问:多个线程进入getInstance()方法,创建多个实例。

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

4.懒汉式-线程安全

这种模式中,静态实例方法中通过synchronized方法锁进行修饰、不会出现线程并发访问问题。
加锁之后、锁定当前类对象、性能会有所降低。
注意点:对代码块加锁时,并发情况下并不能保证有一个实例存在。

  1. public class Solution{
  2. public static Singleton singleton;
  3. private Singleton(){};
  4. // 只能对类进行加锁、代码块加锁并不能保证单例模式
  5. public static synchronized Singleton getInstance(){
  6. if(singleton == null){
  7. singleton = new Singleton();
  8. }
  9. return singleton;
  10. }
  11. // 这种代码块锁通过双重非null检验可以避免、但没必要(过于复杂)
  12. public static synchronized Singleton getInstane(){
  13. if(singleton == null){
  14. synchronized(Solution.class){
  15. if(singleton == null){
  16. singleton = new Singleton();
  17. }
  18. }
  19. }
  20. return singleton;
  21. }
  22. }

5.懒汉式-双重检验

双重检验中、通过volatile、synchronized双重校验机制保证单例模式的并发线程安全模式
volatile:保证了线程并发情况下的可见性(线程之间共享变量的最新状态)
读取前:先从主存中刷新最新的值
写入后:立即同步到主存中

  1. public class Solution{
  2. // volatile防止指令重排、可见性、同步刷新
  3. private static volatile Singleton singleton;
  4. private Solution(){}
  5. public static getInstance(){
  6. if(singleton == null){
  7. synchronized(Solution.class){
  8. if(singleton == null){
  9. singleton = new Singleton();
  10. }
  11. }
  12. }
  13. return singleton;
  14. }
  15. }

2.工厂模式

将对象的创建过程进行内部封装、调用者无需知道对象的创建过程,只需要通过工厂类中传递指定方式获取即可。
特点:隐藏对象的创建细节。

1.工程模式类型

简单工厂模式、工厂方法模式、抽象工厂模式

2.简单工厂

内部封装具有相同行为特征的接口实现类、隐藏这些类的具体实现细节,对外提供一个Factory对象工厂。调用者只需要传递对应的字符串获取相应的对象实体执行调用即可。
特点:调用者无需关系具体的创建过程、只需要获取即可
缺点:扩展时、不仅需要增加响应的实现类、也需要增加响应的工厂类

  1. /**
  2. 创建产品接口
  3. */
  4. public interface Product{
  5. // 接口方法
  6. void method();
  7. }
  8. /**
  9. 产品1-实现产品接口
  10. */
  11. public class ProductA implements Product{
  12. @Override
  13. void method(){
  14. // 代码块
  15. }
  16. }
  17. /**
  18. 产品2-实现产品接口
  19. */
  20. public class ProductB implements Product{
  21. @Override
  22. void method(){
  23. // 代码块
  24. }
  25. }
  26. /**
  27. 产品工厂类
  28. */
  29. public class ProductFactory{
  30. public Product getProductInstance(String type){
  31. if(type == A){
  32. return new ProductA();
  33. }else if(type == B){
  34. return new ProductB();
  35. }
  36. return null;
  37. }
  38. }

3.工厂方法

image.png
简单工厂模式中、系统扩展过程中,如果需要添加产品之外的其他类型,如支付方式时,则需要创建响应的工厂。
工厂方法则是对上述模式的一种优化、工厂接口中创建不同的工厂实现类实现工厂接口(不同的产品中又有不同的大小)
特点:高可扩展性
缺点:扩展难度高、需要创建对应的工厂类方法

  1. /**
  2. 创建产品接口
  3. */
  4. public interface Product{
  5. // 接口方法
  6. void method();
  7. }
  8. /**
  9. 产品1-实现产品接口
  10. */
  11. public class ProductASizeA implements Product{
  12. @Override
  13. void method(){
  14. // 代码块
  15. }
  16. }
  17. /**
  18. 产品1-实现产品接口
  19. */
  20. public class ProductASizeB implements Product{
  21. @Override
  22. void method(){
  23. // 代码块
  24. }
  25. }
  26. /**
  27. 产品2-实现产品接口
  28. */
  29. public class ProductBSizeA implements Product{
  30. @Override
  31. void method(){
  32. // 代码块
  33. }
  34. }
  35. /**
  36. 产品2-实现产品接口
  37. */
  38. public class ProductBSizeB implements Product{
  39. @Override
  40. void method(){
  41. // 代码块
  42. }
  43. }
  44. // 创建工厂接口
  45. public interface Factory{
  46. public Product getProduct(size);
  47. }
  48. // 创建A类产品工厂
  49. public class ProductAFactory implements Factory{
  50. @Override
  51. public Product getProductA(String size){
  52. if(size == A){
  53. return new ProductASizeA();
  54. }else if(size == B){
  55. return new ProductASizeB();
  56. }
  57. }
  58. }
  59. // 创建B类产品工厂
  60. public class ProductBFactory implements Factory{
  61. @Override
  62. public Product getProductB(String size){
  63. if(size == A){
  64. return new ProductBSizeA();
  65. }else if(size == B){
  66. return new ProductBSizeB();
  67. }
  68. }
  69. }
  70. // 工厂方法调用类
  71. public class ProductApi{
  72. Factory factory;
  73. public Product getInstance(String type, String size){
  74. if(type == A){
  75. factory = new ProductAFactory();
  76. }else{
  77. factory = new ProductBFactory();
  78. }
  79. return factory.getProduct(size);
  80. }
  81. }

4.抽象工厂

抽象工厂是对简单工厂和工厂方法的一种结合、提高了更高的可扩展性、但是维护程度更加难以维护。

3.建造者模式

1.构建过渡

传统构建:可以通过定义多个类型的构造器分别创建、这种需要重载过多的构造器、也可以通过流水线创建

  1. // 构造器创建
  2. public class Computer {
  3. private String screen;
  4. private String mouse;
  5. private String cpu;
  6. private String mainBoard;
  7. private String disk;
  8. private String memory;
  9. public Computer setScreen(String screen) {
  10. this.screen = screen;
  11. return this;
  12. }
  13. public Computer setMouse(String mouse) {
  14. this.mouse = mouse;
  15. return this;
  16. }
  17. public Computer setCpu(String cpu) {
  18. this.cpu = cpu;
  19. return this;
  20. }
  21. public Computer setMainBoard(String mainBoard) {
  22. this.mainBoard = mainBoard;
  23. return this;
  24. }
  25. public Computer setDisk(String disk) {
  26. this.disk = disk;
  27. return this;
  28. }
  29. public Computer setMemory(String memory) {
  30. this.memory = memory;
  31. return this;
  32. }
  33. }
  34. Computer computer = new Computer()
  35. .setScreen("高清屏幕")
  36. .setMouse("罗技鼠标")
  37. .setCpu("i7处理器")
  38. .setMainBoard("联想主板")
  39. .setMemory("32G内存")
  40. .setDisk("512G磁盘");

建造者模式:将复杂的构建过程与具体表示相分离、依次来提高高可可扩展性。
再来观察一种创建方式、通过静态内部类的方式构建对象

  1. public class Computer{
  2. private String name;
  3. private String size;
  4. private String action;
  5. private String cpu;
  6. // 通过静态内部类Builder构建
  7. Computer(Builder builder){
  8. this.name = builder.name;
  9. this.size = builder.size;
  10. this.action = builder.action;
  11. this.cpu = builder.cpu;
  12. }
  13. /**
  14. * 静态内部类构造器、用于构建复杂的属性
  15. */
  16. public static class Builder{
  17. private String name;
  18. private String size;
  19. private String action;
  20. private String cpu;
  21. public Builder setName(String name){
  22. this.name = name;
  23. return this;
  24. }
  25. public Builder setSize(String size){
  26. this.size = size;
  27. return this;
  28. }
  29. ......
  30. public Computer builder(){
  31. retunr new Computer(this);
  32. }
  33. }
  34. }
  35. // 构建测试
  36. public class Solution{
  37. Computer computer = new Computer.builder()
  38. .setName()
  39. .setSize()
  40. .setAction()
  41. .setCpu()
  42. .builder();
  43. // 调用对象即可
  44. }

上述建造过程似乎可以满足、但任然无法适应复杂对象的构建

2.建造者构建

将内部类抽离成外部抽象类来构建、并自定义构造器继承构建器

  1. // 需要创建的对象
  2. public class Computer{
  3. private String name;
  4. private String size;
  5. public void setName(String name){
  6. this.name = name;
  7. }
  8. public void setSize(String size){
  9. this.size = size;
  10. }
  11. }
  12. // 抽象构建器
  13. public abstract class Builder{
  14. // 抽象设置名称方法
  15. abstract Builder buildName(String name);
  16. // 抽象设置大小方法
  17. abstract Builer buildSize(String size);
  18. // 抽象构建对象
  19. abstract Computer builder();
  20. }
  21. // 构建自定义构建器继承构建器
  22. public class ProductBuilder extends Builder{
  23. // 实例化对象
  24. private Computer computer = new Computer();
  25. @Override
  26. public Builder buildName(String name){
  27. computer.setName(name);
  28. return this;
  29. }
  30. @Override
  31. public Builder buildSize(String size){
  32. computer.setSize(String size);
  33. return this;
  34. }
  35. // 构建对象
  36. @Override
  37. public Computer builder(){
  38. return computer;
  39. }
  40. }
  41. // 使用构建
  42. public class Solution{
  43. Builder builder = new ProductBuilder();
  44. Computer computer = builder.buildName().buildSize();
  45. }

3.JDK应用场景

StringBuilder类中继承了AbstractStringBuilder抽象构建类(append方法)、就是一种建造者模式
image.png
image.png

6.结构型模式

1.代理模式

通过在服务调用者与服务对象之间增加一层代理对象、 通过代理对象来对目标对象进行扩展操作而不改变原有对象的代码功能。
image.png
静态代理、动态代理

2.静态代理

静态代理指的是通过手动创建Proxy代理对象来对目标方法进行增强的一种代理机制。每个目标对象都需要创建自己的代理对象。
代理过程:
1、创建目标对象接口、创建目标对象接口实现类
2、创建目标代理对象同样实现目标对象接口
3、调用代理对象中的方法接口
特点:
1、静态代理只能代理实现了接口的目标对象
2、静态代理中难以扩展、目标对象和代理对象都要进行修改
代码实现:

  1. // 创建目标对象接口
  2. public interface TargetService{
  3. // 发送信息方法
  4. public String send(String msg);
  5. }
  6. // 创建目标对象接口实现类
  7. public class TargetServiceImpl implements TargetService{
  8. // 实现发送短信方法
  9. @Override
  10. public String send(String msg){
  11. sout(发送消息);
  12. }
  13. }
  14. // 创建代理对象同样实现目标对象接口
  15. public class Proxy implements TargetService{
  16. // 注入目标对象
  17. private TargetService target;
  18. // 构造器注入目标方法
  19. public Proxy(TargetService targetService){
  20. this.target - targetService;
  21. }
  22. // 实现代理方法
  23. @Override
  24. public String send(String msg){
  25. // 调用目标对象前的执行逻辑
  26. sout("调用前增强");
  27. // 调用目标对象方法
  28. target.send(msg);
  29. // 调用目标对象后的执行逻辑
  30. sout("调用后增强");
  31. }
  32. }
  33. // 代理对象测试
  34. public class Solution{
  35. public static void main(String[] args){
  36. // 创建代理对象
  37. Proxy proxy = new Proxy(new TargetServiceImpl());
  38. // 调用代理对象方法
  39. proxy.send(msg);
  40. }
  41. }

3.动态代理

动态代理中不需要为每个目标对象都创建一个代理对象、目标对象也不需要实现接口就可以进行代理。
动态代理类型:
JDK动态代理:适合于代理实现了接口的目标对象或者直接代理接口
CGLIB动态代理:适合代理未实现接口的目标对象进行代理

1.JDK动态代理

JDK代理用于代理那些 实现了接口的目标对象、或者直接代理接口。JDK代理中,有两大核心:
InvocationHandler接口、Proxy类
代理过程:
1、定义目标对象接口及其实现类
2、自定义InvocationHandler、重写invoke()方法、invoke()方法中调用目标方法及其增强逻辑
3、Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocarionHandler h)创建代理对象并调用方法
代码示例:

  1. // 定义目标对象接口及其实现类
  2. public interface TargetService{
  3. public class send(msg);
  4. }
  5. public class TargetServiceImpl implements TargetService{
  6. @Override
  7. public String send(Stirng send){
  8. // 方法体
  9. }
  10. }
  11. // 自定义InvocationHandler对象、重写invoke方法
  12. public MyInvocationHandler implements InvocationHandler{
  13. // 被代理的目标兑现
  14. private Object target;
  15. public MyInvocationHandler(Object target){
  16. this.target = target;
  17. }
  18. /**
  19. * proxy: 动态生成的代理对象
  20. * method:与被代理对象中的方法向对应
  21. * args:当前method方法中的参数
  22. */
  23. @Override
  24. public Object invoke(Object proxy, Method method, Object[] args){
  25. sout("执行方法前增强");
  26. // invoke实际上调用了被代理对象中的方法
  27. Object result = method.invoke(target, args);
  28. sout("执行方法后增强");
  29. return result;
  30. }
  31. }
  32. // 创建代理工厂
  33. public class ProxyFactory{
  34. public static Object getProxy(Object target){
  35. return Proxy.newProxyInstance(
  36. target.getClass().getClassLoader(), // 目标对象加载器
  37. target.getClass().getInterfaces(), // 代理接口
  38. new InvocationHandler(target)
  39. );
  40. }
  41. }
  42. // 使用代理对象
  43. public class Solution{
  44. public static void main(String[] args){
  45. TargetService server = (TargetService) ProxyFactory.getProxy(new TargetServiceImpl());
  46. server.send("jdk动态代理");
  47. }
  48. }

2.CGLIB代理

CGLIB代理是对JDK代理的一种弥补措施、JDK动态代理中只能代理实现了接口的类以及接口。CGLIB代理可以代理未实现接口的类。CGLIB中也包含两大核心类:
MethodInterceptor接口、Enhancer类
代理过程:
1、创建需要代理的目标对象
2、自定义MethodInterceptor、重写intecept()方法、实现增强逻辑
3、通过Enhancer类的create()方法创建代理对象
代码示例:

  1. // 创建目标对象类
  2. public class TargetService{
  3. // 发送短信方法
  4. public String sned(String msg){
  5. sout("发送信息");
  6. }
  7. }
  8. // 自定义MethodInterceptor类实现MethodInterceptor接口
  9. public class MyMethodInteceptor implements MethodInteceptor{
  10. /**
  11. * obj:需求增强的对象
  12. * method:需要增强的方法
  13. * args: 方法入参
  14. * methodProxy:用户调用原始方法
  15. */
  16. @Override
  17. public Obejct intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy){
  18. sout("执行前增强逻辑");
  19. Obejct result = methodProxy.invokeSuper(obj, args);
  20. sout("知心后增强逻辑");
  21. return result;
  22. }
  23. }
  24. // 创建代理工厂
  25. public class CglibFactory{
  26. // 通过类的字节码文件进行代理
  27. public static Object getProxy(Class<?> clazz){
  28. // 创建cglibdialing增强列
  29. Enhancer enhancer = new Enhancer();
  30. // 设置类加载器
  31. enhancer.setClassLoader(clazz.getClassLoader());
  32. // 设置被代理类的字节码对象
  33. enhancer.setSuperclass(clazz);
  34. // 设置拦截器
  35. enhancer.setCallback(new MyInterceptor());
  36. // 返回创建的代理对象
  37. return enhancer.create();
  38. }
  39. }
  40. // 调用测试
  41. public class Solution{
  42. public static void main(String[] args){
  43. // 从代理工厂中获取代理对象
  44. TargetService service = (TargetService) CglibFactory.getProxy(TargetService.class);
  45. service.send("cglib代理测试");
  46. }
  47. }

4.代理差异

1.静态&动态

灵活性:
1、静态代理只能代理实现了接口的类以及接口、需要为每一个类创建代理类;需要同时修改
2、动态代理中两者皆可以实现代理、且代理修改更加灵活
JVM:
静态代理是在编译时就将接口、 实现类、代理类就转变为class文件。
动态代理是在运行时动态生成类字节码、并加载到JVM中。

2.JDK&CGLIB

扩展性:
JDK代理可代理接口、及其实现了接口的类、且效率更高
CGLIB代理只能代理未实现任何接口的类;是通过生成被代理类的子类来拦截被代理类的方法调用、因此不能代理被final修改的类及其方法。

7.行为型模式

1.策略模式

策略模式指的是通过内部提供多种策略实现机制实现相同的策略接口,调用者需要在策略环境中通过传入对象类型创建对应的策略模式类,然后再执行不同的策略方法。
使用场景:简化冗余判断、高可扩展性。
设计模式之美 - 图16
代码示例:

  1. /**
  2. 创建策略接口
  3. */
  4. public interface Strategy{
  5. // 接口方法
  6. void method();
  7. }
  8. /**
  9. 策略1-实现策略接口
  10. */
  11. public class StrategyA implements Strategy{
  12. @Override
  13. void method(){
  14. // 代码块
  15. }
  16. }
  17. /**
  18. 策略2-实现策略接口
  19. */
  20. public class StrategyB implements Strategy{
  21. @Override
  22. void method(){
  23. // 代码块
  24. }
  25. }
  26. /**
  27. 策略环境类
  28. */
  29. public class Context{
  30. // 根据不同的类型、使用不同的策略类
  31. public void strategyMethodApi(String type){
  32. Strategy instance;
  33. // 根据对象进行注入
  34. public Context(Strategy st){
  35. this.instance = st;
  36. }
  37. // 调用方法
  38. instance.method();
  39. }
  40. }

总结:
上述模式应用中,虽然通过不同的策略类简化的行为过程、但是还是难以避免使用if-else判断。可以通过简单工厂模式进行优化策略调用。