技能篇:实际开发常用设计模式

开发 前端
单例对象能节约系统资源,一个对象的创建和消亡的开销可能很小。但是日常的服务接口,就算是一般小公司也有十几万的QPS吧。

[[433206]]

创建型

单例模式

单例对象能节约系统资源,一个对象的创建和消亡的开销可能很小。但是日常的服务接口,就算是一般小公司也有十几万的QPS吧。每一次的功能运转都创建新的对象来响应请求,十几万对象的创建和销毁,想想就是一笔大开销,所以 spring 管理构造的 bean 对象一般都是单例。而且单例模式可以更好的解决并发的问题,方便实现数据的同步性

优点

  • 在内存中只有一个对象,节省内存空间
  • 避免频繁的创建销毁对象,可以提高性能
  • 避免对共享资源的多重占用,简化访问
  • 为整个系统提供一个全局访问点

缺点

  • 不适用于变化频繁的对象
  1. //饿汉式 
  2. private static Singleton singleton = new Singleton(); 
  1. //懒汉式 
  2. private static Singleton singleton; 
  3. public static Singleton getSingleton(){ 
  4.     if (singleton == null) { 
  5.         singleton = new Singleton(); //被动创建,在真正需要使用时才去创建 
  6.     } 
  7.     return singleton; 
  1. //双重判断加锁机制 
  2. private volatile static Singleton instance; 
  3. //程序运行时创建一个静态只读的进程辅助对象 
  4. public static Singleton GetInstance() { 
  5.     //先判断是否存在,不存在再加锁处理 
  6.     if (instance == null){ 
  7.         synchronized (Singleton.class){ 
  8.             if(instance == null){ 
  9.                 instance = new Singleton(); 
  10.             } 
  11.         } 
  12.     } 
  13.     return instance; 
  1. //静态初始化 
  2. private static readonly Singleton instance= new Singleton(); 
  3. public static Singleton GetInstance(){ 
  4.     return instance; 

工厂模式

使用者不关心对象的实例化过程,只关心对象的获取。工厂模式使得产品的实例化过程和消费者解耦

优点

  • 一个调用者想创建一个对象,只需通过其名称或其他唯一键值在工厂获取
  • 扩展性高,如果想增加生产一种类型对象,只要扩展工厂类就可以

缺点

  • 工厂类不太理想,因为每增加一产品,都要在工厂类中增加相应的生产判断逻辑,这是违背开闭原则的
  1. public interface Sender{  public void send();  }   
  2. public class MailSender implements Sender {   
  3.     @Override   
  4.     public void send() {   
  5.         System.out.println("this is mailsender!");   
  6.     }   
  7. public class SmsSender implements Sender {   
  8.     @Override   
  9.     public void send() {   
  10.         System.out.println("this is sms sender!");   
  11.     }   
  12. public class SendFactory {   
  13.     public Sender produce(String type) {   
  14.         if ("mail".equals(type)) {   
  15.             return new MailSender();   
  16.         } else if ("sms".equals(type)) {   
  17.             return new SmsSender();   
  18.         } else {   
  19.             return null;   
  20.         }   
  21.     }  
  22.     //若还有其他产品 则在工厂里加对应的 produce 方法 
  23. }   

建造者模式

主要解决在软件系统中一个复杂对象的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定

优点

  • 扩展性好,对象每一个属性的构建相互独立,有利于解耦。
  • 建造者可以对创建过程逐步细化,而不对其它模块产生任何影响,便于控制细节风险

缺点

  • 如果对象建造者发生变化,则建造者也要同步修改,后期维护成本较大
  • 一种建造者对应一种类型建造,一个建造者基本很难建造多种类型对象
  1. @Data 
  2. class Product { 
  3.     private String name
  4.     private String price; 
  5.     // Product 的建造者  Builder 
  6.     public static class Builder{ 
  7.         public static Builder builder(){ 
  8.             Builder builder = Builder(); 
  9.         } 
  10.         private Product product = new Product(); 
  11.         public Builder name(String name){ product.name = namereturn this;} 
  12.         public Builder price(String price){ product.price = price; return this; } 
  13.         //返回产品对象 
  14.         public Product build() { return product; } 
  15.     } 

结构型

适配器模式

连通上下游功能。一般是现有的功能和产品要求的接口不兼容,需要做转换适配。平时见到的 PO,BO,VO,DTO 模型对象之间的相互转换也是一种适配的过程

  • 优点:提高了类的复用,灵活性好
  • 缺点:过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现
  1. //类的适配器模式 
  2. public class Source {   
  3.     public void sayHello() {   
  4.         System.out.println("lwl:hello!");   
  5.     }   
  6. }   
  7. public interface Targetable {   
  8.     /* Source方法相同 */   
  9.     public void sayHello();   
  10.     /* 新增的方法 */   
  11.     public void hi();   
  12. // Source 用 Adapter 适配成 Targetable 
  13. public class Adapter extends Source implements Targetable {   
  14.     @Override   
  15.     public void hi() {   
  16.         System.out.println("csc:hi!");   
  17.     }   
  1. //对象的适配器模式 
  2. public class Source {   
  3.     public void sayHello() {   
  4.         System.out.println("lwl:hello!");   
  5.     }   
  6. }   
  7. public interface Targetable {   
  8.     /* Source方法相同 */   
  9.     public void sayHello();   
  10.     /* 新增的方法 */   
  11.     public void hi();   
  12. //  Source的对象适配成 Targetable 
  13. public class Adapter implements Targetable { 
  14.     private Source source;   
  15.     public Adapter(Source source){ this.source = source; } 
  16.     public void sayHello(){ source.sayHello(); } 
  17.     @Override   
  18.     public void hi() {   
  19.         System.out.println("csc:hi!");   
  20.     }   

装饰器模式

增强对象功能,动态的为一个对象增加功能,而且还能动态撤销。(继承不能做到这一点,继承的功能是静态的,不能动态增删)

  1. public interface Show(){ public void acting(); } 
  2. public class Artist implements Show {   
  3.     public void acting(){ 
  4.         System.out.println("lwl 在唱歌!");   
  5.     } 
  6. }   
  7. public class DecoratorArtist implements Show{ 
  8.     Artist artist; 
  9.     DecoratorArt(Artist artist){ 
  10.         this.artist = artist; 
  11.     } 
  12.     public void acting(){ 
  13.         System.out.println("lwl 在弹钢琴!"); //增强的功能 
  14.         this.artist.acting();  
  15.         System.out.println("表演完毕!"); //增强的功能 
  16.     } 

代理模式

代理类是客户类和委托类的中介,可以通过给代理类增加额外的功能来扩展委托类的功能,这样只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则

  • 和装饰器模式的区别:代理模式着重于增强类功能,且对面屏蔽原对象的创建过程;装饰器模式增强的是对象,且装饰器模式有一个动态传递原对象的步骤
  • 和对象的适配器模式优点像:不过代理模式着重的是对原功能增强,适配器模式着重的是对新功能的兼容
  • 优点-1、职责清晰。2、高扩展性
  1. public class Artist implements Show {   
  2.     public void acting(){ 
  3.         System.out.println("lwl 在唱歌!");   
  4.     } 
  5. public class ProxyArtist implements Show{ 
  6.     Artist artist; 
  7.     ProxyArtist(){  
  8.         this.artist = new Artist();//屏蔽了 artist 对象的创建 
  9.     } 
  10.     public void acting(){ 
  11.         System.out.println("lwl 在弹钢琴!"); //增强的功能 
  12.         this.artist.acting();  
  13.         System.out.println("表演完毕!"); //增强的功能 
  14.     } 
  15. public class Demo { 
  16.     public static void main(String[] arg){ 
  17.         Show show = new ProxyArtist(); 
  18.         show.acting(); 
  19.     } 

桥接模式

桥接模式侧重于功能的抽象,从而基于这些抽象接口构建上层功能。一般的java 项目都会将接口和实现分离原因,就是基于桥接模式。提高了系统的扩展能力,当引用的底层逻辑有不同的设计实现时,继承抽象接口重新实现一套即可,旧的不变,符合代码设计的开闭原则

  • jdbc 的驱动:常用的JDBC 和 DriverManager,JDBC进行连接数据库的时候,在各个数据库之间进行切换,基本不需要动太多的代码,原因就是JDBC提供统一接口,每个数据库提供各自的实现,用一个叫做数据库驱动的程序来桥接
  • Unix 的文件系统:VFS(virtual File System)使得 Unix 系统可以在不同物理介质上的不同文件系统进行读写
  1. public interface FileSystem(){  
  2.   public void open(int file);  
  3.   public String loading(int file);  
  4.   public void store(int file, String data);  
  5. //网络上的文件系统 
  6. public class NetFileSystem implements FileSystem {   
  7.   public void open(int file){ System.out.println(" netfile opening...."); } 
  8.   public String loading(int file) {System.out.println(" net loading ...."); }   
  9.   public void store(int file, String data) {System.out.println(" send to network ...."); }   
  10. //磁盘文件系统 
  11. public class DiskFileSystem implements FileSystem{ 
  12.   public void open(int file){ System.out.println(" disk opening...."); } 
  13.   public String loading(int file) {System.out.println(" disk loading ...."); }  
  14.   public void store(int file, String data) {System.out.println(" write back disk ...."); }    
  15. public class Linux { 
  16.     FileSystem fileSystem;  
  17.     //底层功能提供接口,桥接模式:功能和具体实现分离 
  18.     //可以桥接 NetFileSystem 或者 DiskFileSystem 作为文件系统 
  19.     public void set(FileSystem fileSystem){ this.fileSystem = fileSystem; } 
  20.     //上层功能读数据 
  21.     public String read(int file){ 
  22.         fileSystem.open(file); 
  23.         ... // Linux 自己的系统功能 
  24.         fileSystem.loading(file); 
  25.         ...  
  26.     } 
  27.     //上层功能写数据 
  28.     public String write(int file, String data){ 
  29.         fileSystem.open(file); 
  30.         .... 
  31.         fileSystem.store(file,data); 
  32.     } 

可配合适配器模式使用

享元模式

多个对象共享某些属性。在创建有大量对象时,可能会造成内存溢出,把其中共同的部分抽象出来,如果有相同的请求,直接返回在内存中同一份属性,避免重新创建

  • 如 jdbc 连接池的连接对象,它们会共享池对象的 url、driverClassName、username、password 等属性
  1. public class ConnectionPool {   
  2.     private Vector<Connection> pool;   
  3.     /*公有属性*/   
  4.     private String url = "jdbc:mysql://localhost:3306/test";   
  5.     private String username = "root";   
  6.     private String password = "root";   
  7.     private String driverClassName = "com.mysql.jdbc.Driver";   
  8.     public ConnectionPool() {   
  9.         pool = new Vector<Connection>(poolSize);   
  10.         for (int i = 0; i < poolSize; i++) {   
  11.             Class.forName(driverClassName);   
  12.                 // 每一个 conn 共享了 driverClassName ,url, username, password 等属性 
  13.                 Connection conn = DriverManager.getConnection(url, username, password);   
  14.                 pool.add(conn); 
  15.             } 
  16.     } 
  17.     .... 

外观模式

  • 用多个不同的对象实现一组更复杂的功能。使得类与类之间的关系解耦。如 spring 将使用各个简单的 component、dao 实现复杂的service,就是一种外观模式
  • 功能的组合,组合优于继承
  1. public class DAO {  
  2.     public void queryData(){ 
  3.         System.out.print(" query data "
  4.     } 
  5. public class Deal {   
  6.     public void dealData(){ 
  7.         System.out.print(" dealing data "
  8.     } 
  9. public class Sender {   
  10.     public void send(){ 
  11.         System.out.print(" send data "
  12.     } 
  13. public class Service(){ 
  14.     private DAO dao;   
  15.     private Deal deal;   
  16.     private Sender sender; 
  17.     //封装 DAO,Deal,Sender 的功能,统一对外提供服务 
  18.     public void reponse(){ 
  19.         dao.queryData(); 
  20.         deal.dealData(); 
  21.         sender.send(); 
  22.     } 

行为型

策略模式

策略模式侧重于不同的场景使用不同的策略。在有多种算法相似的情况下,解决 if...else 所带来的复杂和难以维护

和桥接模式的区别:而桥接模式是结构型模式,侧重于分离底层功能的抽象和实现,底层只有一种实现也可以

  1. // 上学的策略 
  2. abstract class Strategy{ 
  3.     private static final Map<Integer,Strategy> strategyMap = new ConcurrentHashMap<>(); 
  4.     public Strategy(){ 
  5.         strategyMap.put(getType(), this); 
  6.     } 
  7.     public static Strategy routing(int type){ 
  8.         return strategyMap.get(type); 
  9.     } 
  10.     abstract int getType(); 
  11.     abstract void method(); //留待子类实现差异 
  12. //跑路去学校 
  13. class RunningStrategy extends Strategy{ 
  14.     int getType() { return 0; } 
  15.     void method() { System.out.println(" Run to school "); } 
  16. //公交去学校 
  17. class BusStrategy extends Strategy{ 
  18.     int getType() { return 1; } 
  19.     void method() { System.out.println(" Go to school by bus "); } 
  20. //飞去学校 
  21. class FlyStrategy extends Strategy{ 
  22.     int getType() { return 2; } 
  23.     void method() { System.out.println(" Fly to school "); } 
  24. class Context{ 
  25.     //使用不同的策略 
  26.     void method(int strategy){ 
  27.         Strategy.routing(strategy).method(); 
  28.     } 

模板方法

和享元模式有一定的相似处,享元模式侧重于属性的共享,而且是结构上的引用,不一定需要继承;而模板方法是共享相同行为,一定有继承行为

区别于策略模式是它有能抽象出来的共同行为,每一个子类再实现有差异细节

  1. abstract class AbstractHandler{ 
  2.     // handle是抽象出来的共同逻辑 
  3.     void handle(String data){ 
  4.         System.out.println("通用逻辑1..."); 
  5.         stepOne(data); 
  6.         System.out.println("通用逻辑2..."); 
  7.         stepTwo(data); 
  8.         System.out.println("通用逻辑3..."); 
  9.     } 
  10.     abstract void stepOne(String data); //留待子类实现差异 
  11.     abstract void stepTwo(String data); //留待子类实现差异 
  12. class HelloHandler extends AbstractHandler{ 
  13.     @Override 
  14.     void stepOne(String data) { 
  15.         System.out.println("hello: "+data); 
  16.     } 
  17.     @Override 
  18.     void stepTwo(String data) { 
  19.         System.out.println("hi: "+data); 
  20.     } 

迭代子模式

循环处理多个相同对象,用来遍历集合或者数组

  1. //迭代的抽象接口 
  2. public interface Iterator {   
  3.     //前移   
  4.     public Object previous();   
  5.     //后移   
  6.     public Object next();   
  7.     public boolean hasNext();    
  8. }  
  9. // 数组的迭代类 
  10. public class ArrayIterator implements Iterator {   
  11.     private Object[] datas;  
  12.     private int cur = 0;   
  13.     public ArrayIterator(Object[] datas){   
  14.         this.datas = datas;   
  15.     }   
  16.     public String previous() {   
  17.         if(cur > 0){ cur--;}   
  18.         return datas[cur];   
  19.     }   
  20.     public Object next() {   
  21.         if(cur < datas.length-1){ cur++;}   
  22.         return datas[cur];   
  23.     }   
  24.     public boolean hasNext() {   
  25.         return pos < datas.length-1 ? true :false
  26.     }   
  27. }   

责任链模式

负责处理上游的传递下来的对象,并传递给下一个处理者

和迭代子模式的区别,责任链模式是多个hander处理同一个data,且 hander 处理具有顺序性,不用全部 hander 处理,可在某一 hander 中断,也可继续传递。 

  1. abstract class Handler<T,R> { 
  2.     private Handler<R,?> next
  3.     abstract R handle(T data); 
  4.     public void setNext(Handler<R, ?> next){ this.next = next; } 
  5.     public void loopHandle(T data){ 
  6.         R result = this.handle(data); 
  7.         if(next!=null && result!=null ) { next.loopHandle(result); } 
  8.     } 
  9. //负责问候 
  10. class HelloHandler extends Handler<String, Integer> { 
  11.     Integer handle(String data) { 
  12.         System.out.println(data + " hello! "); 
  13.         return 10; 
  14.     } 
  15. //负责计数 
  16. class CountHandler extends Handler<IntegerDouble> { 
  17.     Double handle(Integer data) { 
  18.         System.out.println(" it is " + data); 
  19.         return 2.0; 
  20.     } 
  21. public class demo{ 
  22.     public static void main(String[] args){ 
  23.         HelloHandler hello = new HelloHandler(); 
  24.         CountHandler count = new CountHandler(); 
  25.         hello.setNext(count); 
  26.         hello.loopHandle("lwl"); 
  27.     } 

观察者模式

事件通知: 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知。

优点:观察者和被观察者是抽象耦合的

缺点

  • 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间
  • 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃
  1. //观察者 
  2. public abstract class Observer<T> { 
  3.     public abstract void update(T data); 
  4. // 被观察对象 
  5. public class Subject<T> { 
  6.     private List<Observer<T>> observers = new ArrayList<>(); 
  7.     private T state; 
  8.     public void deal() {  
  9.         ....// 逻辑处理 
  10.         //如果修改了 state,通知观察者 
  11.         if(...) notifyAllObservers(); 
  12.     } 
  13.     //增加一个观察观察 
  14.     public void observe(Observer<T> observer) { 
  15.         observers.add(observer); 
  16.     } 
  17.     public void notifyAllObservers() { 
  18.         for (Observer<T> observer : observers) { 
  19.             observer.update(state); 
  20.         } 
  21.     } 

状态机模式

不同的状态不同的响应,实现状态之间的转移

和策略模式的区别

  • 状态机模式是策略模式的孪生兄弟。策略模式可以让用户指定更换的策略算法,而状态机模式是状态在满足一定条件下的自动更换,用户无法指定状态,最多只能设置初始状态
  • 状态机模式重点在各状态之间的切换,从而做不同的事情;而策略模式更侧重于根据具体情况选择策略,并不涉及切换
  1. interface State<T> { 
  2.     //当前状态进行处理数据,并返回下一个状态 
  3.     abstract State<T> action(T data); 
  4. @Data 
  5. class Context<T>{ 
  6.     private State<T> state; 
  7.     public void invoke(T data){ 
  8.         state != null ? state = state.action(data) : System.out.println(" nothing " + data); 
  9.     } 
  10. // HelloState -> HiState 
  11. class HelloState implements State<String>{ 
  12.     public State<String> action(String data) { 
  13.         System.out.println("hello!" + data); 
  14.         return new HiState(); 
  15.     } 
  16. // HiState -> FineState 
  17. class HiState implements State<String>{ 
  18.     public State<String> action(String data) { 
  19.         System.out.println("how are you ?" + data); 
  20.         return new FineState(); 
  21.     } 
  22. //最后的状态 
  23. class FineState implements State<String>{ 
  24.     public State<String> action(String data) { 
  25.         System.out.println("I am  fine!"); 
  26.         return null
  27.     } 
  28. public class demo{ 
  29.     public static void main(String[] args){ 
  30.         Context<String> context = new Context<>(); 
  31.         context.setState(new HelloState()); 
  32.         context.invoke("lwl"); 
  33.         context.invoke("lwl"); 
  34.         context.invoke("lwl"); 
  35.         context.invoke("lwl"); 
  36.     } 

备忘录

记录上一次的状态,方便回滚。很多时候我们是需要记录当前的状态,这样做的目的就是为了允许用户取消不确定或者错误的操作,恢复到原先的状态

缺点:消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存

  1. @Data 
  2. public class Memento {  
  3.     private String state; 
  4.     public Memento(String state){ this.state = state; } 
  5. @Data 
  6. public class Storage {  
  7.     private String value;   
  8.     public void storeMemento(){ 
  9.         return new Memento(value);   
  10.     } 
  11.     public void restoreMemento(Memento memento){   
  12.         this.value = memento.getValue();   
  13.     } 
  14.     public void action(){ System.out.println(" Storage类逻辑运行 ");} 
  15.        
  16. public class MementoPatternDemo {  
  17.     public static void main(String[] args) { 
  18.         Storage storage = new Storage(); 
  19.         storage.setValue(1); 
  20.         storage.storeMemento();//备忘,一下 
  21.         storage.action();//....逻辑运行 
  22.         restoreMemento(Memento memento);//使用备忘录的恢复状态 
  23.     } 

 

 

责任编辑:武晓燕 来源: 潜行前行
相关推荐

2012-10-29 11:16:13

2023-12-26 08:20:40

2020-05-25 10:20:19

享元模式场景

2019-04-25 15:35:37

MySQL数据库运维Linux

2023-05-05 06:39:52

Java工厂设计模式

2019-08-19 14:56:07

设计模式javascript

2009-06-11 17:11:07

J2EE设计模式工厂模式

2024-02-19 08:38:34

建造者模式Android设计模式

2020-03-18 12:47:59

设计模式ERP

2022-05-20 08:09:18

设计模式后端代码

2021-06-13 09:01:47

SedLinux命令

2012-05-09 10:09:18

JavaMEJava

2021-10-27 17:57:35

设计模式场景

2021-08-04 10:36:34

git项目开发

2023-10-12 14:22:45

2009-12-21 09:22:51

SilverlightMVVM模式

2012-02-21 09:37:07

宅男程序员

2018-07-09 11:00:56

软件架构设计模式

2020-10-19 13:05:32

架构模式

2019-04-02 09:23:40

设计模式前端JavaScript
点赞
收藏

51CTO技术栈公众号