设计模式理解
一、对设计模式的理解
1. 什么是设计模式?
官方定义:设计模式(Design Pattern)是一套被反复使用、多数人知晓、经过分类编目的、代码设计经验的总结。它描述了在软件设计过程中一些不断重复发生的问题,以及该问题的解决方案的核心。
2. 为什么要学习设计模式?
代码复用性(Reusability):模式提供了已经测试和验证过的设计模板,避免重复造轮子。
代码可扩展性(Extensibility):模式指导我们设计出松耦合的结构,当系统需要增加新功能时,影响最小,符合开闭原则(对扩展开放,对修改关闭)。
代码可维护性(Maintainability):模式使代码结构清晰、层次分明,易于阅读和理解。后人维护时,能快速找到相关模块,降低维护成本。
代码灵活性(Flexibility):通过接口和抽象,使得组件可以方便地被替换和组合,应对多变的需求。
3. 设计模式的六大原则
开闭原则(OCP):对扩展开放,对修改关闭。这是终极目标。通过扩展(如继承、实现接口)来添加新功能,而不是修改已有的源代码。
单一职责原则(SRP):一个类只负责一项职责。避免一个类变得过于庞大和难以修改。
里氏替换原则(LSP):所有引用基类(父类)的地方必须能透明地使用其子类的对象。即子类可以扩展父类的功能,但不能改变父类原有的功能。
依赖倒置原则(DIP):要依赖于抽象,而不是具体实现。
高层模块不应依赖低层模块,二者都应依赖于抽象。
抽象不应依赖于细节,细节应依赖于抽象。
接口隔离原则(ISP):客户端不应该被迫依赖于它不使用的接口。将一个庞大的接口拆分成更小、更具体的接口。
迪米特法则(LoD):又称最少知识原则。一个对象应该对其他对象有最少的了解。只与直接的朋友通信,降低类之间的耦合。
二、在日常开发中运用设计模式
核心思想:重构优于预先设计(Refactoring to Patterns)
切忌为了用模式而用模式。正确的使用姿势是:
先写出可工作的代码:首先,用最直接、最简单的方式实现功能需求。
识别“不同写法的缺点”:在编写或Review代码时,警惕以下“缺点”:
冗长的if-else/switch分支(可能需用策略模式、状态模式)
直接new一个对象,依赖具体类(可能需用工厂方法、抽象工厂)
一个类过于庞大,做了太多事(违反单一职责原则,需拆分类)
多个类有相同的行为,只是实现不同(可能需用模板方法模式)
对象间的依赖关系复杂混乱(可能需用中介者模式)
需要为其他对象提供一种代理以控制对这个对象的访问(可能需用代理模式)
思考能否用模式重构:当发现上述“缺点”时,思考哪种设计模式可以优雅地解决这个问题。
权衡利弊:评估引入模式带来的好处(扩展性、清晰度)和代价(复杂度、类数量)。如果当前需求非常稳定,且简单实现完全够用,就不要过度设计。
e.g.:
最初支付功能可能用
if (type == "wechat") {...} else if (type == "alipay") {...}
。当新增一种支付方式时,你需要修改这个核心方法,违反了开闭原则。
此时识别到“坏味道”,就可以重构为策略模式,将每种支付算法封装成类,未来新增只需添加一个新策略类即可。
第三部分:场景题设计 - 多平台分享功能
场景:在抖音/小红书看到内容,可以分享到微信、QQ、微博等多个平台。
1. 模式选择与分析
核心需求:将“分享”这个行为视为一个算法族。分享到微信、分享到QQ、分享到微博等都是这个行为的不同“策略”或“算法”。
问题:我们希望在运行时能够灵活地选择使用哪一种分享策略,并且避免使用冗长的
if-else
或switch
语句来判断平台。解决方案:使用策略模式。它定义了一系列的算法(策略),并将每一个算法封装起来,使它们可以互相替换。策略模式让算法的变化独立于使用算法的客户端。
2. 策略模式设计
UML 类图:
流程图:
3. 代码实现
// 1. 策略接口:定义分享算法族
public interface ShareStrategy {void share(Content content);
}// 2. 具体策略类:实现不同平台的分享算法
public class WeChatStrategy implements ShareStrategy {@Overridepublic void share(Content content) {// 调用微信SDK的具体APISystem.out.println("[策略] 分享到微信好友: " + content.getSummary());}
}public class WeChatMomentStrategy implements ShareStrategy {@Overridepublic void share(Content content) {// 同样是微信,但可以是分享到朋友圈的不同策略System.out.println("[策略] 分享到微信朋友圈: " + content.getSummary());}
}public class QQStrategy implements ShareStrategy {@Overridepublic void share(Content content) {// 调用QQ SDK的具体APISystem.out.println("[策略] 分享到QQ: " + content.getSummary());}
}public class WeiboStrategy implements ShareStrategy {@Overridepublic void share(Content content) {// 调用微博SDK的具体APISystem.out.println("[策略] 分享到微博: " + content.getSummary());}
}
// 未来新增小红书分享,只需新增一个策略类:XiaohongshuStrategy// 3. 环境上下文类 (Context):负责持有和使用策略
public class ShareContext {// 持有策略接口的引用,而非具体实现private ShareStrategy strategy;// 设置当前需要使用的策略(Setter注入,非常灵活)public void setStrategy(ShareStrategy strategy) {this.strategy = strategy;}// 执行分享操作public void executeShare(Content content) {if (strategy == null) {System.out.println("请先设置分享策略");return;}// 将工作委托给当前设置的策略对象strategy.share(content);}
}// 4. 内容类(不变)
public class Content {private String title;private String image;private String url;public String getSummary() {return title + " - " + url;}// getters and setters...
}// 5. 客户端使用
public class Client {public static void main(String[] args) {// 准备分享内容Content content = new Content();content.setTitle("这个视频太搞笑了!");content.setUrl("https://www.douyin.com/video/456");// 创建分享上下文ShareContext shareContext = new ShareContext();// 用户选择分享到微信shareContext.setStrategy(new WeChatStrategy());shareContext.executeShare(content);// 用户改变主意,要分享到微博shareContext.setStrategy(new WeiboStrategy());shareContext.executeShare(content);// 也可以结合简单工厂模式,避免客户端直接new具体策略String userChoice = "wechat_moment"; // 可以从UI点击事件获取ShareStrategy strategy = StrategyFactory.getStrategy(userChoice);shareContext.setStrategy(strategy);shareContext.executeShare(content);}
}// 可选:策略工厂,进一步解耦客户端与具体策略
class StrategyFactory {public static ShareStrategy getStrategy(String platform) {switch (platform) {case "wechat": return new WeChatStrategy();case "wechat_moment": return new WeChatMomentStrategy();case "qq": return new QQStrategy();case "weibo": return new WeiboStrategy();default: throw new IllegalArgumentException("不支持的平台: " + platform);}}
}
4. 设计优势与对比
符合开闭原则:新增一个分享平台(如小红书)时,只需添加一个新的
XiaohongshuStrategy
类,无需修改任何现有的策略类或上下文类。消除了条件判断:客户端(如UI层)通过设置不同的策略对象来指定行为,避免了在业务代码中出现庞大的
if-else
分支。灵活性极高:可以在运行时动态地改变对象的行为(通过
setStrategy
方法)。