第十二篇:设计模式——23种模式,本质上就是6种原则 别再背23种模式的UML图了你只需要知道遇到什么问题用什么套路解决写在前面设计模式是面试里**最容易被妖魔化**的一块。很多人觉得设计模式很高深要背23种模式的名字、结构图、适用场景……太痛苦了。但设计模式的本质就一句话针对特定问题的经典解决方案。就像下棋有开局套路一样写代码也有套路。这些套路是前人总结出来的你不需要重新发明轮子只需要知道遇到什么问题用什么套路。这篇文章的核心就一条线6大原则 → 23种模式 → Spring里用了哪些。一、六大设计原则SOLIDSOLID是设计模式的指导思想。所有设计模式都是对这几条原则的具体实现。1.1 单一职责原则Single Responsibility一个类只干一件事。如果一个类既是订单管理器又是日志记录器又是邮件发送器改订单逻辑可能影响邮件发送改邮件配置又可能影响订单。拆成三个类各干各的。违反的例子一个UserService里有createUser()、sendEmail()、logActivity()。应该把发邮件移到EmailService日志移到LogService。1.2 开闭原则Open-Closed对扩展开放对修改关闭。需求变化了尽量加新代码而不是改旧代码。比如你有多种支付方式微信、支付宝、银行卡不要用if-else而是定义Payment接口每种支付方式实现这个接口。新增Apple Pay时写一个新实现类就行不需要改已有的支付逻辑。1.3 里氏替换原则Liskov Substitution子类应该能替换父类程序行为不变。如果父类是Bird子类是Penguin父类有fly()方法企鹅不会飞继承后就破坏了里氏替换。更好的设计Bird作为抽象类有move()方法麻雀是Flyable企鹅是Swimmable用接口区分能力。不要为了让代码复用而强行继承不合理的父类。1.4 接口隔离原则Interface Segregation接口要小而专不要大而全。一个多功能接口包含10个方法实现类被迫实现一堆它用不上的方法。应该拆成多个小接口按需实现。比如Worker接口有work()、eat()、sleep()如果只有Robot机器人不需要eat()和sleep()就该拆成Workable和Eatable。1.5 依赖倒置原则Dependency Inversion依赖抽象不依赖具体。上层模块业务逻辑不应该依赖下层模块具体实现两者都应该依赖抽象接口。通俗说你买了个台灯台灯依赖的是插座接口而不是某个特定型号的插座。换房子了新房子有插座台灯就能用。如果台灯直接焊死在某个墙上插座上搬家就没法用了。Spring的Autowired注入接口而不是实现类就是依赖倒置的体现——你随时可以切换实现类比如从MySQL切换到Oracle上层的Service代码不需要改。1.6 迪米特法则最少知道原则一个对象对其他对象了解越少越好。A调BB调CA不要知道C的存在。A只管跟B打交道B内部怎么调C是B自己的事。如果A直接调CA就绑死了CC的改动会牵连A。二、创建型模式怎么创建对象2.1 单例模式Singleton一个类只有一个实例。面试常问重点是双重检查锁和静态内部类两种写法。懒汉式双重检查锁publicclassSingleton{privatestaticvolatileSingletoninstance;// volatile禁止指令重排privateSingleton(){}publicstaticSingletongetInstance(){if(instancenull){synchronized(Singleton.class){if(instancenull){instancenewSingleton();}}}returninstance;}}为什么用volatile因为new Singleton()不是原子操作分配内存→初始化→引用赋值指令重排可能导致其他线程拿到一个未初始化完成的对象。volatile禁止重排。静态内部类写法推荐publicclassSingleton{privateSingleton(){}privatestaticclassHolder{privatestaticfinalSingletonINSTANCEnewSingleton();}publicstaticSingletongetInstance(){returnHolder.INSTANCE;// 类加载时初始化线程安全}}Spring里所有Bean默认都是单例Scope(singleton)。2.2 工厂模式Factory把创建对象的逻辑封装起来而不是到处new。简单工厂一个工厂类根据参数返回不同产品。工厂方法每个产品有自己的工厂类。抽象工厂创建产品族比如MySQL的Connection、Statement、ResultSet是一族Oracle的是一族。Spring里BeanFactory和ApplicationContext都是工厂模式。你调getBean()Spring帮你把对象创建出来。2.3 建造者模式Builder一步步构建复杂对象。UseruserUser.builder().name(张三).age(25).email(zhangsanemail.com).build();好处不用写一堆构造参数特别是字段很多的时候链式调用可读性强。Lombok的Builder注解底层就是建造者模式。2.4 原型模式Prototype通过拷贝现有对象来创建新对象而不是new。Java里实现Cloneable接口重写clone()方法。Spring里Scope(prototype)就是原型模式每次getBean()都返回一个新实例。三、结构型模式怎么组合类和对象3.1 代理模式Proxy为其他对象提供一种代理以控制对这个对象的访问。静态代理代理类手动编写和目标类实现同一接口。动态代理运行时动态生成代理类。JDK动态代理基于接口和CGLIB基于继承。Spring里AOP底层就是动态代理。Transactional、Cacheable这些注解都是通过代理实现的。调userService.save()实际调的是代理对象代理先开启事务再调原始对象的save()再提交事务。场景远程代理RPC、虚拟代理延迟加载、保护代理权限控制。3.2 适配器模式Adapter把一个接口转换成另一个接口让不兼容的类能一起工作。生活比喻你的手机是Type-C接口充电头是USB-A口中间加一个转换头适配器。Spring里HandlerAdapter。Spring MVC里的Controller有多种实现方式Controller注解、实现Controller接口、实现HttpRequestHandler接口Spring通过不同的HandlerAdapter把不同类型的Controller统一成handle()调用。3.3 装饰器模式Decorator不改变原有类动态添加新功能。生活比喻你买了一杯奶茶可以加珍珠、加布丁、加椰果——每一种配料都是对奶茶的装饰奶茶本身没变但功能口感增强了。Java里BufferedInputStream装饰FileInputStream给文件读取增加了缓冲功能。BufferedReader装饰FileReader增加了按行读取的方法。3.4 外观模式Facade给复杂的子系统提供一个统一的简化接口。生活比喻你办护照不用自己去跑公安局、使馆、银行全部交给旅行社外观旅行社帮你搞定一切。Spring里JdbcTemplate就是外观模式封装了获取Connection、创建Statement、处理ResultSet、关闭资源等一系列复杂操作你只需要写SQL和映射逻辑。四、行为型模式怎么处理算法和职责分配4.1 模板方法模式Template Method父类定义骨架模板子类实现具体细节。生活比喻做菜有个固定流程洗菜→切菜→炒菜→装盘。但炒菜这个步骤不同菜有不同的做法。父类定义cook()模板子类重写heat()方法。publicabstractclassAbstractService{publicfinalvoidexecute(){before();// 公共逻辑doBusiness();// 子类实现after();// 公共逻辑}protectedabstractvoiddoBusiness();}Spring里JdbcTemplate的execute()定义了获取连接→执行SQL→处理结果→释放连接的骨架具体SQL由你传入。RestTemplate也是模板方法模式的应用。4.2 观察者模式Observer对象状态变化时通知所有依赖它的对象。生活比喻你订阅了某个UP主UP主发新视频状态变化B站通知所有订阅者观察者。Spring里ApplicationListener和ApplicationEvent。发布一个OrderCreatedEvent所有监听器发邮件、扣库存、加积分自动收到通知执行。这就是观察者模式。4.3 策略模式Strategy定义一系列算法让它们可以互相替换。生活比喻去机场可以选地铁、打车、大巴。不同交通方式策略都能到机场你可以根据需要选。publicinterfacePaymentStrategy{voidpay(intamount);}ServicepublicclassPaymentService{AutowiredprivateMapString,PaymentStrategystrategyMap;publicvoidpay(Stringtype,intamount){strategyMap.get(type).pay(amount);}}Spring里依赖注入到MapString, Handler里根据type选择不同的处理器。这在写业务代码时极其常见是一种优雅的替代大规模if-else的方式。4.4 责任链模式Chain of Responsibility多个对象有机会处理同一个请求形成一条链。生活比喻请假审批。组长能批3天经理能批7天总监能批30天。你的请假申请从组长开始传谁有权限谁批。Spring里HandlerInterceptor拦截器链。一个请求经过多个拦截器的preHandle()任何一个返回false就中断。Spring Security的过滤器链也是责任链。五、Spring里用的设计模式面试高频面试官常问Spring里用了哪些设计模式设计模式Spring里的体现单例模式所有Bean默认单例工厂模式BeanFactory和ApplicationContext代理模式AOP动态代理模板方法模式JdbcTemplate、RestTemplate观察者模式ApplicationEventApplicationListener适配器模式HandlerAdapter适配不同Controller装饰器模式BeanWrapper、HttpServletRequestWrapper策略模式依赖注入到MapString, Handler根据类型选择责任链模式拦截器链、过滤器链六、面试官真正想听什么场景1“你项目中用了哪些设计模式”不要说我用了单例这太初级了。说业务场景加分回答“我们有个支付模块支持微信、支付宝、银行卡等多种方式。我用了策略模式定义PaymentStrategy接口每种支付方式一个实现类然后注入一个MapString, PaymentStrategy根据前端传的payType动态选择策略类。这样新增一种支付方式时只需要加一个实现类不用改原有代码符合开闭原则。”“同时支付成功后要通知多个模块订单状态更新、积分增加、消息推送我用了观察者模式Spring的ApplicationEvent。发送一个PaymentSuccessEvent各个EventListener异步处理解耦了支付主流程和后续扩展逻辑。”场景2“策略模式和工厂模式有什么区别”加分回答“工厂模式是为了创建对象策略模式是为了封装算法。简单说工厂解决的是’怎么new出来’策略解决的是’new出来后怎么用’。比如支付场景工厂根据payType创建对应的支付策略对象策略对象内部的pay()方法才是真正执行支付的算法。两者经常配合使用但关注点完全不同。”场景3“知道哪些反模式”加分回答“上帝对象一个类做了所有事违反单一职责面条代码大量if-else嵌套难以维护硬编码配置写死在代码里违背开闭原则。我们Code Review时会特别注意看到超过3层的if-else就提醒用策略模式或状态机重构。”小结核心概念大白话面试考点SOLID原则写代码的六大纪律设计模式的指导思想单例模式一个类只能有一个实例双重检查锁volatile工厂模式封装创建逻辑BeanFactory代理模式不碰原类加增强功能AOP、动态代理适配器模式把A接口转成B接口HandlerAdapter模板方法父类定骨架子类填细节JdbcTemplate观察者模式状态变了通知所有人ApplicationEvent策略模式多种算法互相替换消除if-else责任链多个环节依次处理拦截器链记住一句话设计模式不是目的是手段。代码要维护、要扩展、要别人看懂用模式帮你做到好维护、好扩展、好理解。如果模式增加了复杂度宁可不复用也不硬套。下一篇我们讲系统设计——把前面学的所有技术串起来解决真实业务问题。秒杀系统、短链系统、排行榜……怎么设计怎么回答面试官追问的挂了怎么办下期见。