技术漫谈 大话设计模式 FANSEA 2024-07-19 2025-03-13 大话设计模式 基础知识 - 设计模式概述 - 《设计模式 Java版本》 - 书栈网 · BookStack 
(Pattern Name)通过一两个词来描述模式的问题、解决方案和效果,以便更好地理解模式并方便开发人员之间的交流,绝大多数模式都是根据其功能或模式结构来命名的(GoF设计模式中没有一个模式用人名命名,微笑);问题(Problem)描述了应该在何时使用模式,它包含了设计中存在的问题以及问题存在的原因;解决方案(Solution)描述了一个设计模式的组成成分,以及这些组成成分之间的相互关系,各自的职责和协作方式,通常解决方案通过UML类图和核心代码来进行描述;效果(Consequences)描述了模式的优缺点以及在使用模式时应权衡的问题。
迪米特法则 
https://www.bilibili.com/video/BV1Ge41157dY/?spm_id_from=333.337.search-card.all.click&vd_source=70a5c913e74574ad96afc2ae210ba3e0 
 
迪米特法则,也称为最少知识原则(Law of Demeter),是面向对象设计中的一个原则,旨在降低对象之间的耦合性,提高系统的可维护性和可扩展性。该原则强调一个类不应该直接与其它不相关的类相互交互,而是通过少数几个密切相关的类来进行通信 。这有助于减少类之间的依赖关系,降低代码的耦合性,使得系统更加灵活和易于维护。
迪米特法则的核心思想可以概括为以下几点:
一个对象应该尽量少地了解其他对象的内部结构和实现。 
一个对象只与其直接朋友(即与其关联最密切的对象)进行交互。 
避免在一个类中引入不必要的依赖关系,尽量保持类之间的解耦。 
 
策略模式 
侧重行为,不同方法解决不同问题,if-else
 
组成:
行为接口 
实现具体的行为类 
定义行为发生的类,该类用于注入并执行行为 
 
第一步:定义行为接口  
1 2 3 public  interface  QuackBehavior  {    void  quack () ; } 
第二步:实现具体的行为  
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public  class  Quack  implements  QuackBehavior  {    @Override      public  void  quack ()  {         System.out.println("Quack" );     } } public  class  Squeak  implements  QuackBehavior  {    @Override      public  void  quack ()  {         System.out.println("Squeak" );     } } public  class  MuteQuack  implements  QuackBehavior  {    @Override      public  void  quack ()  {         System.out.println("<< Silence >>" );     } } 
第三步:定义鸭子类  
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public  class  Duck  {    private  QuackBehavior quackBehavior;     public  Duck (QuackBehavior quackBehavior)  {         this .quackBehavior = quackBehavior;     }     public  void  performQuack ()  {         quackBehavior.quack();     }     public  void  setQuackBehavior (QuackBehavior quackBehavior)  {         this .quackBehavior = quackBehavior;     } } 
第四步:创建测试类  
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public  class  DuckTestDrive  {    public  static  void  main (String[] args)  {         Duck  duck1  =  new  Duck (new  Quack ());         Duck  duck2  =  new  Duck (new  Squeak ());         Duck  duck3  =  new  Duck (new  MuteQuack ());         System.out.println("Duck 1:" );         duck1.performQuack();         System.out.println("\nDuck 2:" );         duck2.performQuack();         System.out.println("\nDuck 3:" );         duck3.performQuack();                  System.out.println("\nChanging behavior for Duck 1:" );         duck1.setQuackBehavior(new  Squeak ());         duck1.performQuack();     } } 
运行结果:  
1 2 3 4 5 6 7 8 9 10 11 Duck 1: Quack Duck 2: Squeak Duck 3: << Silence >> Changing behavior for Duck 1: Squeak 
适配器模式 
多个方法解决同一个问题
将原有的接口变成客户需要的另外一种接口
 
目标接口(target) :需要适配的标准接口。需要适配的类(adaptee) :需要被适配的不兼容对象。适配器对象(adapter) :充当中间转换角色,该对象将源对象转换成目标接口。 
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 interface  Target  {    void  request () ; } class  Adaptee  {    void  specificRequest ()  {         System.out.println("Adaptee's specific request" );     } } class  Adapter  extends  Adaptee  implements  Target  {    @Override      public  void  request ()  {         specificRequest();       } } public  class  Client  {    public  static  void  main (String[] args)  {         Target  target  =  new  Adapter ();         target.request();       } } 
模板方法 
使用场景 
 
当你有个通用的流程需要多个类共享,但某些步骤需要根据具体情况进行定义,可以考虑模板方法设计模式
与策略模式的差异 
 
模板方法:侧重流程(框架)的共用,具体细节的替换 
策略模式:侧重行为的多样性,关注算法的可替换性 
 
样例演示 
 
需要处理不同类型的数据文件(如文本文件、图片文件等)。我们可以定义一个抽象的基类 FileProcessor,该类包含一个模板方法 processFile,该方法定义了处理文件的基本步骤。具体的文件处理逻辑由子类来实现。
首先,我们定义一个抽象的基类 FileProcessor:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public  abstract  class  FileProcessor  {    protected  String fileName;     public  FileProcessor (String fileName)  {         this .fileName = fileName;     }          public  final  void  processFile ()  {         loadFile();         transformData();         saveData();     }          protected  abstract  void  loadFile () ;          protected  abstract  void  transformData () ;          protected  abstract  void  saveData () ; } 
然后,我们定义两个具体的子类来处理不同的文件类型:
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  TextFileProcessor  extends  FileProcessor  {    public  TextFileProcessor (String fileName)  {         super (fileName);     }     @Override      protected  void  loadFile ()  {         System.out.println("Loading text file: "  + fileName);              }     @Override      protected  void  transformData ()  {         System.out.println("Transforming text data" );              }     @Override      protected  void  saveData ()  {         System.out.println("Saving transformed text data" );              } } public  class  ImageFileProcessor  extends  FileProcessor  {    public  ImageFileProcessor (String fileName)  {         super (fileName);     }     @Override      protected  void  loadFile ()  {         System.out.println("Loading image file: "  + fileName);              }     @Override      protected  void  transformData ()  {         System.out.println("Transforming image data" );              }     @Override      protected  void  saveData ()  {         System.out.println("Saving transformed image data" );              } } 
最后,我们可以在主函数中创建不同的处理器对象并调用 processFile 方法:
1 2 3 4 5 6 7 8 9 10 11 public  class  Main  {    public  static  void  main (String[] args)  {                  FileProcessor  textProcessor  =  new  TextFileProcessor ("example.txt" );         textProcessor.processFile();                  FileProcessor  imageProcessor  =  new  ImageFileProcessor ("example.jpg" );         imageProcessor.processFile();     } } 
在这个例子中,FileProcessor 类定义了一个模板方法 processFile,它包含了三个步骤:loadFile、transformData 和 saveData。这些步骤的具体实现由子类 TextFileProcessor 和 ImageFileProcessor 提供。通过这种方式,我们能够保证处理文件的整体流程一致,同时允许不同的子类实现具体的细节。
代理模式 
强调通过代理对象来对对象控制,中心不在于装饰
 
代理模式 :关注控制对象的访问 ,通常与对象的生命周期管理相关。装饰器模式 :关注动态地为对象添加功能 ,通常与对象的行为增强相关。 
装饰器模式 
强调动态的添加新的功能,与代理模式很相似,其实是借鉴了代理模式的思路,
代理模式的与其的不同在于:被代理对象的注入可以通过类动态加载来实现,而不只是注入对象 
 
装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许你动态地给对象添加一些额外的职责,即在不需要通过子类的情况下扩展其功能。这种模式经常被用作替代继承的一个方法,提供比继承更加灵活的替代方案 。
下面是一个简单的Java示例来展示装饰器模式的应用:
假设我们有一个Beverage接口,定义了饮料的基本行为,例如计算价格。
1 2 3 4 public  interface  Beverage  {    String getDescription () ;     double  cost () ; } 
然后我们有一些实现了Beverage接口的具体类,比如HouseBlend:
1 2 3 4 5 6 7 8 9 10 11 public  class  HouseBlend  implements  Beverage  {    @Override      public  String getDescription ()  {         return  "House Blend Coffee" ;     }     @Override      public  double  cost ()  {         return  0.89 ;     } } 
接下来,我们创建一个抽象的装饰器类,它也实现了Beverage接口,并且包含了一个对Beverage类型的引用。这个装饰器可以用来包装任何实现了Beverage接口的对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public  abstract  class  CondimentDecorator  implements  Beverage  {    private  Beverage beverage;     public  CondimentDecorator (Beverage beverage)  {         this .beverage = beverage;     }     @Override      public  String getDescription ()  {         return  beverage.getDescription();     }     @Override      public  double  cost ()  {         return  beverage.cost();     } } 
现在我们可以创建具体的装饰器类来为饮料添加不同的特性,比如加糖浆的装饰器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public  class  Syrup  extends  CondimentDecorator  {    public  Syrup (Beverage beverage)  {         super (beverage);     }     @Override      public  String getDescription ()  {         return  beverage.getDescription() + ", Syrup" ;     }     @Override      public  double  cost ()  {         return  0.15  + beverage.cost();     } } 
最后,我们可以在客户端代码中使用这些类:
1 2 3 4 5 6 7 8 9 10 public  class  TestDecorator  {    public  static  void  main (String[] args)  {         Beverage  beverage  =  new  HouseBlend ();         System.out.println(beverage.getDescription() + " $"  + beverage.cost());                  beverage = new  Syrup (beverage);         System.out.println(beverage.getDescription() + " $"  + beverage.cost());     } } 
在这个例子中,Syrup装饰器可以被用来给任何实现了Beverage接口的对象添加糖浆,而不需要修改原始的Beverage类。这样就提供了很大的灵活性,可以方便地组合不同的装饰器来得到所需的最终对象。
备忘录模式 
备忘录模式(Memento Pattern)是一种行为设计模式,它允许你在不破坏封装性的前提下捕获一个对象的内部状态,并在稍后恢复该对象之前的状态。这种模式常用于实现撤销(Undo)功能,或者当需要保存和恢复对象的状态时。
 
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 class  EditorMemento  {    private  final  String content;     public  EditorMemento (String content)  {         this .content = content;     }     public  String getContent ()  {         return  content;     } } class  Editor  {    private  String content;     public  EditorMemento save ()  {         return  new  EditorMemento (this .content);     }     public  void  restore (EditorMemento memento)  {         this .content = memento.getContent();     }     public  void  setContent (String content)  {         this .content = content;     }     public  String getContent ()  {         return  this .content;     } } class  EditorHistory  {    private  List<EditorMemento> states = new  ArrayList <>();     public  void  push (EditorMemento state)  {         states.add(state);     }     public  EditorMemento pop ()  {         return  states.remove(states.size() - 1 );     } } 
备忘当前实体:
1 2 3 Editor  editor  =  new  Editor ();EditorMemento  editorMemento  =  editor.save();new  EditorHistory ().push(editorMemento);
桥接模式 
它将抽象部分与它的实现部分分离 ,使得它们都可以独立地变化。这种模式可以用来动态地给一个对象添加功能,而不需要通过继承来实现 。
TIP:一个操作对应多个实现,但这个多个实现也可能依赖于多个实现
 
场景 :假设我们需要创建一个图形库,支持不同类型的图形(如圆形、矩形等),并且能够在不同的平台上(如Windows、Linux等)绘制这些图形。这里我们就可以使用桥接模式来解决这个问题。
画图为一个操作、不同形状对应不同的实现 :
shape: 
1 2 3 4 5 6 7 public  abstract  class  Shape  {    protected  GraphicsAPI api;      public  Shape (GraphicsAPI api)  {         this .api = api;     }     public  abstract  void  draw () ; } 
shape的实现: 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public  class  Circle  extends  Shape  {    public  Circle (GraphicsAPI api)  {         super (api);     }     @Override      public  void  draw ()  {         api.draw(this );     } } public  class  Rectangle  extends  Shape  {    public  Rectangle (GraphicsAPI api)  {         super (api);     }     @Override      public  void  draw ()  {         api.draw(this );     } } 
多平台适配总接口: 
1 2 3 public  interface  GraphicsAPI  {    void  draw (Shape shape) ; } 
多平台适配实现: 
1 2 3 4 5 6 7 8 9 10 11 12 13 public  class  WindowsAPI  implements  GraphicsAPI  {    @Override      public  void  draw (Shape shape)  {         System.out.println("Drawing "  + shape.getClass().getSimpleName() + " using Windows API" );     } } public  class  LinuxAPI  implements  GraphicsAPI  {    @Override      public  void  draw (Shape shape)  {         System.out.println("Drawing "  + shape.getClass().getSimpleName() + " using Linux API" );     } } 
使用: 
1 2 3 4 5 6 7 8 9 10 11 12 public  class  Main  {    public  static  void  main (String[] args)  {         GraphicsAPI  winApi  =  new  WindowsAPI ();         GraphicsAPI  linuxApi  =  new  LinuxAPI ();         Shape  circleWin  =  new  Circle (winApi);         Shape  rectLinux  =  new  Rectangle (linuxApi);         circleWin.draw();          rectLinux.draw();      } } 
状态模式 
它允许对象在其内部状态改变时改变其行为。对象看起来似乎修改了它的类。
 
用途: 
当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为。 
控制大量条件语句,当这些条件判断依赖于对象的状态时。 
 
1 假设我们有一个自动售货机,它有三种状态:`等待投币`(WaitingForCoin)、`已经投币`(CoinInserted)、`商品被选中`(ProductSelected)。售货机可以根据用户的操作改变状态,并相应地执行不同的行为。 
观察者模式 
发布订阅模式
 
观察者模式在Java中有广泛的应用,尤其是在事件处理中。以下是一个简单的Java示例来说明观察者模式:
首先,我们定义一个Subject接口,代表被观察的对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import  java.util.ArrayList;import  java.util.List;public  interface  Subject  {         void  registerObserver (Observer o) ;               void  removeObserver (Observer o) ;               void  notifyObservers () ;               void  setState (String state) ;               String getState () ; } 
接下来,我们实现Subject接口的RealSubject类:
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 import  java.util.ArrayList;import  java.util.List;public  class  RealSubject  implements  Subject  {    private  List<Observer> observers;     private  String state;     public  RealSubject ()  {         observers = new  ArrayList <>();     }     @Override      public  void  registerObserver (Observer o)  {         observers.add(o);     }     @Override      public  void  removeObserver (Observer o)  {         int  i  =  observers.indexOf(o);         if  (i >= 0 ) {             observers.remove(i);         }     }     @Override      public  void  notifyObservers ()  {         for  (Observer observer : observers) {             observer.update(this );         }     }     @Override      public  void  setState (String state)  {         this .state = state;         notifyObservers();      }     @Override      public  String getState ()  {         return  state;     } } 
然后,我们定义Observer接口,代表观察者:
1 2 3 4 public  interface  Observer  {         void  update (Subject subject) ; } 
接着,我们实现Observer接口的BinaryObserver类:
1 2 3 4 5 6 7 8 9 10 11 12 13 public  class  BinaryObserver  implements  Observer  {    @Override      public  void  update (Subject subject)  {         System.out.println("BinaryObserver: State is "  + subject.getState());     } } public  class  HexObserver  implements  Observer  {    @Override      public  void  update (Subject subject)  {         System.out.println("HexObserver: State is "  + subject.getState());     } } 
最后,我们编写测试代码来演示观察者模式的工作过程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public  class  ObserverPatternDemo  {    public  static  void  main (String[] args)  {         RealSubject  realSubject  =  new  RealSubject ();         Observer  binaryObserver  =  new  BinaryObserver ();         Observer  hexObserver  =  new  HexObserver ();                   realSubject.registerObserver(binaryObserver);         realSubject.registerObserver(hexObserver);                  realSubject.setState("1101" );         realSubject.setState("1010" );                  realSubject.removeObserver(hexObserver);                  realSubject.setState("1111" );     } } 
在上面的代码中,RealSubject是被观察的对象,BinaryObserver和HexObserver(这里未给出实现)是观察者。当RealSubject的状态改变时,它会调用notifyObservers()方法来通知所有注册的观察者。每个观察者通过实现update()方法来响应状态的改变。
在实际应用中,观察者模式可以用于多种场景,比如GUI编程中的事件监听、数据库连接的监听、用户界面的变化监听等。它使得对象之间的依赖关系解耦,降低了系统的耦合度,提高了代码的可维护性和可扩展性。
责任链设计模式 
适用于流水线任务,对每一个环节进行类的定义,顺序执行,可以保障流程扩展性
 
组成 :
Handler(环节类):包含执行当前环节任务的方法,以及下一个环节的指针 
Factory(工厂类):用于创建环节链表关系,从第一个环节开始执行 
 
代码示例: 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 interface  Handler  {    void  setNext (Handler handler) ;     Object handle (Object request) ; } abstract  class  AbstractHandler  implements  Handler  {    private  Handler next;     @Override      public  void  setNext (Handler handler)  {         this .next = handler;     }     @Override      public  Object handle (Object request)  {         if  (this .next != null ) {             return  this .next.handle(request);         }         return  null ;     } } class  ConcreteHandler1  extends  AbstractHandler  {    @Override      public  Object handle (Object request)  {         if  ((Integer) request < 10 ) {             System.out.println("Request handled in ConcreteHandler1" );         } else  {             super .handle(request);         }         return  request;     } } class  ConcreteHandler2  extends  AbstractHandler  {    @Override      public  Object handle (Object request)  {         if  ((Integer) request >= 10  && (Integer) request < 20 ) {             System.out.println("Request handled in ConcreteHandler2" );         } else  {             super .handle(request);         }         return  request;     } } public  class  ChainOfResponsibilityDemo  {    public  static  void  main (String[] args)  {                  Handler  handler1  =  new  ConcreteHandler1 ();         Handler  handler2  =  new  ConcreteHandler2 ();                  handler1.setNext(handler2);                  for  (int  i  =  0 ; i <= 25 ; i += 5 ) {             handler1.handle(i);         }     } } 
在这个例子中,ConcreteHandler1 和 ConcreteHandler2 分别处理不同范围的请求值。当一个请求进入链后,每个处理者检查是否满足它的条件,如果满足则处理请求,否则将请求传给下一个处理者。这样就形成了一个处理请求的责任链。
注意:在实际应用中,需要确保责任链的末尾处理者能够妥善处理无法识别的请求,以避免无限递归的问题。在上面的例子中,如果没有处理者接受请求,则默认返回null。