Java中的设计模式:23种经典模式在实际项目中的应用案例
目录
- 引言
- 创建型模式
- 单例模式
- 工厂方法模式
- 抽象工厂模式
- 建造者模式
- 原型模式
- 结构型模式
- 适配器模式
- 桥接模式
- 组合模式
- 装饰器模式
- 外观模式
- 享元模式
- 代理模式
- 行为型模式
- 责任链模式
- 命令模式
- 解释器模式
- 迭代器模式
- 中介者模式
- 备忘录模式
- 观察者模式
- 状态模式
- 策略模式
- 模板方法模式
- 访问者模式
- 设计模式在实际项目中的应用
- 总结与最佳实践
引言
设计模式是软件开发中经过验证的、用于解决特定问题的解决方案。它们代表了软件设计领域的最佳实践,是众多开发者经验的结晶。在Java开发中,合理运用设计模式可以提高代码的可读性、可维护性和可扩展性,同时降低系统的复杂度。
本文将详细介绍GoF(Gang of Four,四人帮)提出的23种经典设计模式在Java项目中的实际应用案例,帮助读者理解如何在实际项目中灵活运用这些模式来解决具体问题。
创建型模式
创建型模式关注对象的创建过程,试图将对象的创建与使用分离,使系统更加灵活。
单例模式
定义:确保一个类只有一个实例,并提供一个全局访问点。
实际应用:数据库连接池、线程池、配置管理器等。
代码示例:使用双重检查锁定实现线程安全的单例
public class DatabaseConnectionPool {private static volatile DatabaseConnectionPool instance;private List<Connection> connectionPool;private DatabaseConnectionPool() {// 初始化连接池connectionPool = new ArrayList<>();// 创建初始连接for (int i = 0; i < 10; i++) {connectionPool.add(createNewConnection());}}public static DatabaseConnectionPool getInstance() {if (instance == null) {synchronized (DatabaseConnectionPool.class) {if (instance == null) {instance = new DatabaseConnectionPool();}}}return instance;}private Connection createNewConnection() {// 创建数据库连接的代码return null; // 实际项目中返回真实连接}public synchronized Connection getConnection() {if (connectionPool.isEmpty()) {connectionPool.add(createNewConnection());}Connection connection = connectionPool.remove(connectionPool.size() - 1);return connection;}public synchronized void releaseConnection(Connection connection) {connectionPool.add(connection);}
}
优点:
- 节省系统资源,避免重复创建对象
- 提供统一访问点,便于协调系统整体行为
缺点:
- 单例类职责过重,违背单一职责原则
- 扩展困难,若要扩展单例类,只能修改原有代码
工厂方法模式
定义:定义一个创建对象的接口,但由子类决定实例化的类。工厂方法使一个类的实例化延迟到其子类。
实际应用:日志记录器、数据访问对象(DAO)、UI控件创建等。
代码示例:支付系统中的支付方式工厂
// 支付接口
public interface Payment {void pay(double amount);
}// 具体支付实现:支付宝
public class AliPay implements Payment {@Overridepublic void pay(double amount) {System.out.println("使用支付宝支付:" + amount + "元");// 实际支付逻辑}
}// 具体支付实现:微信支付
public class WeChatPay implements Payment {@Overridepublic void pay(double amount) {System.out.println("使用微信支付:" + amount + "元");// 实际支付逻辑}
}// 支付工厂接口
public interface PaymentFactory {Payment createPayment();
}// 支付宝工厂
public class AliPayFactory implements PaymentFactory {@Overridepublic Payment createPayment() {return new AliPay();}
}// 微信支付工厂
public class WeChatPayFactory implements PaymentFactory {@Overridepublic Payment createPayment() {return new WeChatPay();}
}// 客户端代码
public class PaymentService {public void processPayment(String paymentType, double amount) {PaymentFactory factory = null;if ("alipay".equals(paymentType)) {factory = new AliPayFactory();} else if ("wechat".equals(paymentType)) {factory = new WeChatPayFactory();} else {throw new IllegalArgumentException("不支持的支付方式");}Payment payment = factory.createPayment();payment.pay(amount);}
}
优点:
- 符合开闭原则,新增产品只需添加新的工厂类
- 符合单一职责原则,每个工厂只负责创建对应的产品
缺点:
- 类的个数增加,增加系统复杂度
- 增加了系统的抽象性和理解难度
抽象工厂模式
定义:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
实际应用:跨平台UI组件、不同数据库的数据访问组件、多主题应用等。
代码示例:跨平台UI组件工厂
// UI组件接口
public interface Button {void render();void onClick();
}public interface TextField {void render();void onType();
}// Windows平台组件实现
public class WindowsButton implements Button {@Overridepublic void render() {System.out.println("渲染Windows风格按钮");}@Overridepublic void onClick() {System.out.println("Windows按钮点击效果");}
}public class WindowsTextField implements TextField {@Overridepublic void render() {System.out.println("渲染Windows风格文本框");}@Overridepublic void onType() {System.out.println("Windows文本框输入效果");}
}// Mac平台组件实现
public class MacButton implements Button {@Overridepublic void render() {System.out.println("渲染Mac风格按钮");}@Overridepublic void onClick() {System.out.println("Mac按钮点击效果");}
}public class MacTextField implements TextField {@Overridepublic void render() {System.out.println("渲染Mac风格文本框");}@Overridepublic void onType() {System.out.println("Mac文本框输入效果");}
}// 抽象工厂接口
public interface GUIFactory {Button createButton();TextField createTextField();
}// 具体工厂实现
public class WindowsFactory implements GUIFactory {@Overridepublic Button createButton() {return new WindowsButton();}@Overridepublic TextField createTextField() {return new WindowsTextField();}
}public class MacFactory implements GUIFactory {@Overridepublic Button createButton() {return new MacButton();}@Overridepublic TextField createTextField() {return new MacTextField();}
}// 客户端代码
public class Application {private Button button;private TextField textField;public Application(GUIFactory factory) {button = factory.createButton();textField = factory.createTextField();}public void render() {button.render();textField.render();}public static void main(String[] args) {// 根据操作系统选择工厂GUIFactory factory;String osName = System.getProperty("os.name").toLowerCase();if (osName.contains("windows")) {factory = new WindowsFactory();} else {factory = new MacFactory();}Application app = new Application(factory);app.render();}
}
优点:
- 保证了一系列相关产品的一致性
- 隔离了具体类的生成,使客户端不需要知道什么被创建
缺点:
- 产品族扩展困难,需要修改抽象工厂的接口
- 增加了系统的抽象性和理解难度
建造者模式
定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
实际应用:复杂对象创建、配置对象、API客户端构建等。
代码示例:构建HTTP请求
public class HttpRequest {private final String url;private final String method;private final Map<String, String> headers;private final Map<String, String> parameters;private final String body;private final int timeout;private HttpRequest(Builder builder) {this.url = builder.url;this.method = builder.method;this.headers = builder.headers;this.parameters = builder.parameters;this.body = builder.body;this.timeout = builder.timeout;}// Getters...public static class Builder {// 必需参数private final String url;private String method = "GET";// 可选参数private Map<String, String> headers = new HashMap<>();private Map<String, String> parameters = new HashMap<>();private String body = "";private int timeout = 30000; // 默认30秒public Builder(String url) {this.url = url;}public Builder method(String method) {this.method = method;return this;}public Builder addHeader(String name, String value) {this.headers.put(name, value);return this;}public Builder addParameter(String name, String value) {this.parameters.put(name, value);return this;}public Builder body(String body) {this.body = body;return this;}public Builder timeout(int timeout) {this.timeout = timeout;return this;}public HttpRequest build() {return new HttpRequest(this);}}
}// 客户端代码
public class HttpClient {public static void main(String[] args) {HttpRequest request = new HttpRequest.Builder("https://api.example.com/data").method("POST").addHeader("Content-Type", "application/json").addHeader("Authorization", "Bearer token123").addParameter("id", "123").body("{\"name\":\"John\",\"age\":30}").timeout(5000).build();// 使用request对象发送请求...}
}
优点:
- 分离复杂对象的构建和表示
- 同样的构建过程可以创建不同的产品
- 可以更精细地控制构建过程
缺点:
- 产品必须有足够的共同点,范围受限
- 构建者模式比较复杂,增加了代码量
原型模式
定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
实际应用:对象复制、对象缓存、避免重复创建对象等。
代码示例:文档对象克隆
// 原型接口
public interface Prototype extends Cloneable {Prototype clone();
}// 具体原型:文档类
public class Document implements Prototype {private String title;private String content;private List<String> authors;private Map<String, String> metadata;public Document(String title, String content) {this.title = title;this.content = content;this.authors = new ArrayList<>();this.metadata = new HashMap<>();}// 深拷贝实现@Overridepublic Document clone() {try {Document clone = (Document) super.clone();// 深拷贝集合类型属性clone.authors = new ArrayList<>(this.authors);clone.metadata = new HashMap<>(this.metadata);return clone;} catch (CloneNotSupportedException e) {return null;}}// Getters and Setters...public void addAuthor(String author) {authors.add(author);}public void addMetadata(String key, String value) {metadata.put(key, value);}@Overridepublic String toString() {return "Document{" +"title='" + title + '\'' +", content='" + content + '\'' +", authors=" + authors +", metadata=" + metadata +'}';}
}// 客户端代码
public class DocumentManager {private Map<String, Document> documentRegistry = new HashMap<>();public DocumentManager() {// 初始化常用文档模板Document report = new Document("报告模板", "这是一份标准报告模板");report.addAuthor("系统管理员");report.addMetadata("type", "report");report.addMetadata("version", "1.0");Document letter = new Document("信件模板", "这是一份标准信件模板");letter.addAuthor("系统管理员");letter.addMetadata("type", "letter");// 注册模板documentRegistry.put("report", report);documentRegistry.put("letter", letter);}public Document createDocument(String type) {Document template = documentRegistry.get(type);if (template == null) {throw new IllegalArgumentException("未知的文档类型: " + type);}return template.clone();}public static void main(String[] args) {DocumentManager manager = new DocumentManager();// 基于报告模板创建新文档Document myReport = manager.createDocument("report");myReport.addAuthor("张三");System.out.println(myReport);// 基于信件模板创建新文档Document myLetter = manager.createDocument("letter");myLetter.addAuthor("李四");System.out.println(myLetter);}
}
优点:
- 隐藏了创建对象的复杂性
- 允许在运行时动态添加或删除产品
- 允许复制对象,而不需要知道具体的类
缺点:
- 对象的深拷贝可能很复杂
- 克隆包含循环引用的对象可能会很困难
结构型模式
结构型模式关注如何组合类和对象以形成更大的结构,同时保持这些结构的灵活性和高效性。
适配器模式
定义:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的类可以一起工作。
实际应用:第三方库集成、旧系统接口适配、不同格式数据转换等。
代码示例:支付系统集成
// 目标接口:我们的系统使用的支付接口
public interface PaymentProcessor {void processPayment(double amount);PaymentStatus getPaymentStatus(String paymentId);
}// 已有的第三方支付系统(不兼容我们的接口)
public class ThirdPartyPaymentService {public void makePayment(String merchantId, double amount, String currency) {System.out.println("使用第三方服务处理支付: " + amount + " " + currency);// 处理支付逻辑...}public String checkStatus(String merchantId, String transactionId) {// 检查支付状态...return "COMPLETED";}
}// 支付状态枚举
public enum PaymentStatus {PENDING, COMPLETED, FAILED, REFUNDED
}// 适配器:将ThirdPartyPaymentService适配到PaymentProcessor接口
public class ThirdPartyPaymentAdapter implements PaymentProcessor {private ThirdPartyPaymentService paymentService;private String merchantId;private Map<String, String> paymentTransactions; // 存储支付ID与交易ID的映射public ThirdPartyPaymentAdapter(String merchantId) {this.paymentService = new ThirdPartyPaymentService();this.merchantId = merchantId;this.paymentTransactions = new HashMap<>();}@Overridepublic void processPayment(double amount) {paymentService.makePayment(merchantId, amount, "CNY");String paymentId = UUID.randomUUID().toString();String transactionId = "TXN" + System.currentTimeMillis();paymentTransactions.put(paymentId, transactionId);}@Overridepublic PaymentStatus getPaymentStatus(String paymentId) {String transactionId = paymentTransactions.get(paymentId);if (transactionId == null) {return PaymentStatus.FAILED;}String status = paymentService.checkStatus(merchantId, transactionId);switch (status) {case "COMPLETED":return PaymentStatus.COMPLETED;case "PENDING":return PaymentStatus.PENDING;case "REFUNDED":return PaymentStatus.REFUNDED;default:return PaymentStatus.FAILED;}}
}// 客户端代码
public class PaymentService {private PaymentProcessor paymentProcessor;public PaymentService(PaymentProcessor paymentProcessor) {this.paymentProcessor = paymentProcessor;}public void pay(double amount) {paymentProcessor.processPayment(amount);System.out.println("支付处理完成");}public static void main(String[] args) {// 使用适配器将第三方支付服务适配到我们的系统PaymentProcessor adapter = new ThirdPartyPaymentAdapter("MERCHANT_123");PaymentService paymentService = new PaymentService(adapter);paymentService.pay(100.0);}
}
优点:
- 将接口不兼容的类可以一起工作
- 提高了类的复用性
- 增加了类的透明性
缺点:
- 过多使用适配器会让系统变得混乱
- 可能需要重写一部分原有功能,导致代码冗余
桥接模式
定义:将抽象部分与它的实现部分分离,使它们都可以独立地变化。
实际应用:跨平台应用、多驱动支持、主题和外观分离等。
代码示例:消息通知系统
// 实现部分接口
public interface MessageSender {void sendMessage(String message, String recipient);
}// 具体实现:短信发送器
public class SMSMessageSender implements MessageSender {@Overridepublic void sendMessage(String message, String recipient) {System.out.println("通过短信发送到 " + recipient + ": " + message);// 实际短信发送逻辑...}
}// 具体实现:邮件发送器
public class EmailMessageSender implements MessageSender {@Overridepublic void sendMessage(String message, String recipient) {System.out.println("通过邮件发送到 " + recipient + ": " + message);// 实际邮件发送逻辑...}
}// 具体实现:微信发送器
public class WeChatMessageSender implements MessageSender {@Overridepublic void sendMessage(String message, String recipient) {System.out.println("通过微信发送到 " + recipient + ": " + message);// 实际微信发送逻辑...}
}// 抽象部分
public abstract class Notification {protected MessageSender messageSender;public Notification(MessageSender messageSender) {this.messageSender = messageSender;}public abstract void notify(String message, String recipient);
}// 扩展抽象部分:普通通知
public class NormalNotification extends Notification {public NormalNotification(MessageSender messageSender) {super(messageSender);}@Overridepublic void notify(String message, String recipient) {messageSender.sendMessage(message, recipient);}
}// 扩展抽象部分:紧急通知
public class UrgentNotification extends Notification {public UrgentNotification(MessageSender messageSender) {super(messageSender);}@Overridepublic void notify(String message, String recipient) {String urgentMessage = "[紧急] " + message;messageSender.sendMessage(urgentMessage, recipient);// 可能还会有额外的紧急处理逻辑...}
}// 客户端代码
public class NotificationService {public static void main(String[] args) {// 创建不同的发送器MessageSender smsSender = new SMSMessageSender();MessageSender emailSender = new EmailMessageSender();MessageSender weChatSender = new WeChatMessageSender();// 创建不同的通知,并指定发送器Notification normalSmsNotification = new NormalNotification(smsSender);Notification urgentEmailNotification = new UrgentNotification(emailSender);Notification urgentWeChatNotification = new UrgentNotification(weChatSender);// 发送通知normalSmsNotification.notify("系统维护通知", "13800138000");urgentEmailNotification.notify("服务器宕机警报", "admin@example.com");urgentWeChatNotification.notify("数据库连接异常", "技术支持组");}
}
优点:
- 分离抽象接口及其实现部分
- 提高可扩展性,可以独立扩展实现和抽象
- 实现细节对客户透明
缺点:
- 增加了系统的理解与设计难度
- 需要正确识别出系统中两个独立变化的维度
组合模式
定义:将对象组合成树形结构以表示"部分-整体"的层次结构,使得用户对单个对象和组合对象的使用具有一致性。
实际应用:文件系统、组织架构、GUI组件树等。
代码示例:组织架构管理系统
// 组件接口
public interface OrganizationComponent {String getName();String getDescription();void add(OrganizationComponent component);void remove(OrganizationComponent component);void display(int depth);double getSalaryBudget();
}// 叶子节点:员工
public class Employee implements OrganizationComponent {private String name;private String position;private double salary;public Employee(String name, String position, double salary) {this.name = name;this.position = position;this.salary = salary;}@Overridepublic String getName() {return name;}@Overridepublic String getDescription() {return position;}@Overridepublic void add(OrganizationComponent component) {throw new UnsupportedOperationException("员工不能添加下属");}@Overridepublic void remove(OrganizationComponent component) {throw new UnsupportedOperationException("员工不能移除下属");}@Overridepublic void display(int depth) {StringBuilder indent = new StringBuilder();for (int i = 0; i < depth; i++) {indent.append(" ");}System.out.println(indent + "- " + name + " (" + position + ")");}@Overridepublic double getSalaryBudget() {return salary;}
}// 组合节点:部门
public class Department implements OrganizationComponent {private String name;private String description;private List<OrganizationComponent> components = new ArrayList<>();public Department(String name, String description) {this.name = name;this.description = description;}@Overridepublic String getName() {return name;}@Overridepublic String getDescription() {return description;}@Overridepublic void add(OrganizationComponent component) {components.add(component);}@Overridepublic void remove(OrganizationComponent component) {components.remove(component);}@Overridepublic void display(int depth) {StringBuilder indent = new StringBuilder();for (int i = 0; i < depth; i++) {indent.append(" ");}System.out.println(indent + "+ " + name + " (" + description + ")");// 显示所有子组件for (OrganizationComponent component : components) {component.display(depth + 1);}}@Overridepublic double getSalaryBudget() {double budget = 0;for (OrganizationComponent component : components) {budget += component.getSalaryBudget();}return budget;}
}// 客户端代码
public class OrganizationStructure {public static void main(String[] args) {// 创建公司结构OrganizationComponent company = new Department("ABC科技有限公司", "一家科技公司");// 创建部门OrganizationComponent rdDept = new Department("研发部", "负责产品研发");OrganizationComponent salesDept = new Department("销售部", "负责产品销售");// 创建子部门OrganizationComponent frontEndTeam = new Department("前端组", "负责前端开发");OrganizationComponent backEndTeam = new Department("后端组", "负责后端开发");// 创建员工OrganizationComponent cto = new Employee("张三", "CTO", 30000);OrganizationComponent salesManager = new Employee("李四", "销售经理", 20000);OrganizationComponent frontEndLead = new Employee("王五", "前端负责人", 18000);OrganizationComponent frontEndDev1 = new Employee("赵六", "前端开发", 12000);OrganizationComponent backEndDev1 = new Employee("钱七", "后端开发", 15000);OrganizationComponent salesRep1 = new Employee("孙八", "销售代表", 8000);// 构建组织结构company.add(cto);company.add(rdDept);company.add(salesDept);rdDept.add(frontEndTeam);rdDept.add(backEndTeam);frontEndTeam.add(frontEndLead);frontEndTeam.add(frontEndDev1);backEndTeam.add(backEndDev1);salesDept.add(salesManager);salesDept.add(salesRep1);// 显示组织结构company.display(0);// 计算各部门薪资预算System.out.println("\n薪资预算:");System.out.println("公司总预算: " + company.getSalaryBudget());System.out.println("研发部预算: " + rdDept.getSalaryBudget());System.out.println("销售部预算: " + salesDept.getSalaryBudget());System.out.println("前端组预算: " + frontEndTeam.getSalaryBudget());}
}
优点:
- 定义了包含基本对象和组合对象的层次结构
- 简化客户端代码,客户端可以一致地使用组合结构或单个对象
- 更容易增加新类型的组件
缺点:
- 使设计变得更加抽象
- 很难对组件类型进行限制
装饰器模式
定义:动态地给一个对象添加一些额外的职责,就增加功能来说,装饰器模式比生成子类更为灵活。
实际应用:I/O流包装、UI组件增强、服务功能扩展等。
代码示例:咖啡订单系统
// 组件接口
public interface Beverage {String getDescription();double cost();
}// 具体组件:基础咖啡类型
public class Espresso implements Beverage {@Overridepublic String getDescription() {return "浓缩咖啡";}@Overridepublic double cost() {return 20.0;}
}public class Americano implements Beverage {@Overridepublic String getDescription() {return "美式咖啡";}@Overridepublic double cost() {return 25.0;}
}// 装饰器基类
public abstract class CondimentDecorator implements Beverage {protected Beverage beverage;public CondimentDecorator(Beverage beverage) {this.beverage = beverage;}@Overridepublic abstract String getDescription();
}// 具体装饰器:调料
public class Milk extends CondimentDecorator {public Milk(Beverage beverage) {super(beverage);}@Overridepublic String getDescription() {return beverage.getDescription() + ", 加牛奶";}@Overridepublic double cost() {return beverage.cost() + 5.0;}
}public class Mocha extends CondimentDecorator {public Mocha(Beverage beverage) {super(beverage);}@Overridepublic String getDescription() {return beverage.getDescription() + ", 加摩卡";}@Overridepublic double cost() {return beverage.cost() + 8.0;}
}public class Whip extends CondimentDecorator {public Whip(Beverage beverage) {super(beverage);}@Overridepublic String getDescription() {return beverage.getDescription() + ", 加奶泡";}@Overridepublic double cost() {return beverage.cost() + 3.0;}
}// 客户端代码
public class CoffeeShop {public static void main(String[] args) {// 点一杯浓缩咖啡Beverage beverage1 = new Espresso();System.out.println(beverage1.getDescription() + " ¥" + beverage1.cost());// 点一杯加了双份摩卡和奶泡的美式咖啡Beverage beverage2 = new Americano();beverage2 = new Mocha(beverage2);beverage2 = new Mocha(beverage2);beverage2 = new Whip(beverage2);System.out.println(beverage2.getDescription() + " ¥" + beverage2.cost());// 点一杯加牛奶和摩卡的浓缩咖啡Beverage beverage3 = new Espresso();beverage3 = new Milk(beverage3);beverage3 = new Mocha(beverage3);System.out.println(beverage3.getDescription() + " ¥" + beverage3.cost());}
}
优点:
- 比继承更灵活,可以动态地添加或删除职责
- 可以用多个装饰器组合,创建多层次的功能
- 避免了在层次结构高层的类有太多的特征
缺点:
- 会产生很多小对象,增加系统复杂度
- 装饰器模式比继承更难理解和调试
外观模式
定义:为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
实际应用:复杂系统的简化接口、第三方库封装、子系统整合等。
代码示例:智能家居控制系统
// 子系统类:灯光控制
public class LightSystem {public void turnOn() {System.out.println("灯光已打开");}public void turnOff() {System.out.println("灯光已关闭");}public void dim(int level) {System.out.println("灯光亮度调整为: " + level + "%");}
}// 子系统类:空调控制
public class AirConditionerSystem {public void turnOn() {System.out.println("空调已打开");}public void turnOff() {System.out.println("空调已关闭");}public void setTemperature(int temperature) {System.out.println("空调温度设置为: " + temperature + "°C");}public void setMode(String mode) {System.out.println("空调模式设置为: " + mode);}
}// 子系统类:音响控制
public class MusicSystem {public void turnOn() {System.out.println("音响已打开");}public void turnOff() {System.out.println("音响已关闭");}public void setVolume(int volume) {System.out.println("音量设置为: " + volume);}public void setChannel(String channel) {System.out.println("频道切换为: " + channel);}
}// 子系统类:窗帘控制
public class CurtainSystem {public void open() {System.out.println("窗帘已打开");}public void close() {System.out.println("窗帘已关闭");}public void setPosition(int position) {System.out.println("窗帘位置设置为: " + position + "%");}
}// 外观类:智能家居控制中心
public class SmartHomeFacade {private LightSystem lightSystem;private AirConditionerSystem airConditionerSystem;private MusicSystem musicSystem;private CurtainSystem curtainSystem;public SmartHomeFacade() {lightSystem = new LightSystem();airConditionerSystem = new AirConditionerSystem();musicSystem = new MusicSystem();curtainSystem = new CurtainSystem();}// 简化的接口:回家模式public void homeMode() {System.out.println("激活回家模式...");curtainSystem.open();lightSystem.turnOn();lightSystem.dim(70);airConditionerSystem.turnOn();airConditionerSystem.setTemperature(24);airConditionerSystem.setMode("制冷");musicSystem.turnOn();musicSystem.setChannel("轻音乐");musicSystem.setVolume(30);}// 简化的接口:离家模式public void awayMode() {System.out.println("激活离家模式...");curtainSystem.close();lightSystem.turnOff();airConditionerSystem.turnOff();musicSystem.turnOff();}// 简化的接口:电影模式public void movieMode() {System.out.println("激活电影模式...");curtainSystem.close();lightSystem.dim(30);airConditionerSystem.setTemperature(22);musicSystem.turnOn();musicSystem.setChannel("环绕声");musicSystem.setVolume(60);}// 简化的接口:睡眠模式public void sleepMode() {System.out.println("激活睡眠模式...");curtainSystem.close();lightSystem.turnOff();airConditionerSystem.setTemperature(26);airConditionerSystem.setMode("睡眠");musicSystem.turnOff();}
}// 客户端代码
public class SmartHomeClient {public static void main(String[] args) {SmartHomeFacade smartHome = new SmartHomeFacade();System.out.println("=== 回家场景 ===");smartHome.homeMode();System.out.println("\n=== 准备看电影 ===");smartHome.movieMode();System.out.println("\n=== 准备睡觉 ===");smartHome.sleepMode();System.out.println("\n=== 准备出门 ===");smartHome.awayMode();}
}
优点:
- 简化了客户端与复杂子系统的交互
- 实现了子系统与客户端的松耦合
- 提供了一个简单的接口,隐藏了系统的复杂性
缺点:
- 外观可能成为与所有子系统耦合的上帝类
- 不符合开闭原则,增加新的子系统需要修改外观类
享元模式
定义:运用共享技术有效地支持大量细粒度的对象,避免创建过多对象导致内存溢出。
实际应用:字符串常量池、数据库连接池、缓存池等。
代码示例:文本编辑器中的字符渲染
// 享元接口
public interface Character {void display(int fontSize, String fontFamily, int x, int y);
}// 具体享元类
public class ConcreteCharacter implements Character {private char symbol; // 内部状态,可以共享public ConcreteCharacter(char symbol) {this.symbol = symbol;// 模拟加载字符资源的耗时操作try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("创建字符: " + symbol);}@Overridepublic void display(int fontSize, String fontFamily, int x, int y) {// fontSize, fontFamily, x, y 是外部状态,不可共享System.out.println("显示字符 " + symbol + " [字体: " + fontFamily + ", 大小: " + fontSize + ", 位置: (" + x + "," + y + ")]");}
}// 享元工厂
public class CharacterFactory {private Map<Character, ConcreteCharacter> characterPool = new HashMap<>();public Character getCharacter(char symbol) {// 如果字符已经在池中,则直接返回if (characterPool.containsKey(symbol)) {return characterPool.get(symbol);} else {// 否则创建一个新的字符对象并添加到池中ConcreteCharacter character = new ConcreteCharacter(symbol);characterPool.put(symbol, character);return character;}}public int getPoolSize() {return characterPool.size();}
}// 客户端代码
public class TextEditor {public static void main(String[] args) {CharacterFactory factory = new CharacterFactory();// 模拟文本编辑器显示文本String text = "Hello, World! Welcome to Java Design Patterns.";int x = 0;int y = 0;System.out.println("开始渲染文本...");long startTime = System.currentTimeMillis();for (char c : text.toCharArray()) {Character character = factory.getCharacter(c);// 每个字符的外部状态可能不同character.display(12, "Arial", x, y);x += 10; // 移动到下一个位置if (x > 100) {x = 0;y += 20; // 换行}}long endTime = System.currentTimeMillis();System.out.println("渲染完成,用时: " + (endTime - startTime) + "ms");System.out.println("字符对象池大小: " + factory.getPoolSize() + " (文本中不同字符的数量)");}
}
优点:
- 大大减少了对象的创建,节省内存
- 相同的对象只要保存一份,降低了系统的复杂度
缺点:
- 提高了系统的复杂度,需要分离内部状态和外部状态
- 使得系统逻辑复杂化,增加了程序的复杂性
代理模式
定义:为其他对象提供一种代理以控制对这个对象的访问。
实际应用:远程代理、虚拟代理、保护代理、缓存代理等。
代码示例:图片加载的虚拟代理
// 主题接口
public interface Image {void display();
}// 真实主题:真实图片
public class RealImage implements Image {private String filename;public RealImage(String filename) {this.filename = filename;loadFromDisk();}private void loadFromDisk() {System.out.println("加载图片: " + filename);// 模拟图片加载的耗时操作try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}@Overridepublic void display() {System.out.println("显示图片: " + filename);}
}// 代理:虚拟代理
public class ProxyImage implements Image {private String filename;private RealImage realImage;public ProxyImage(String filename) {this.filename = filename;}@Overridepublic void display() {if (realImage == null) {realImage = new RealImage(filename);}realImage.display();}
}// 客户端代码
public class ImageViewer {public static void main(String[] args) {// 使用代理创建图片对象Image image1 = new ProxyImage("photo1.jpg");Image image2 = new ProxyImage("photo2.jpg");// 第一次调用display()时会加载图片System.out.println("首次显示图片1:");image1.display();System.out.println("\n首次显示图片2:");image2.display();// 第二次调用display()不会重新加载图片System.out.println("\n再次显示图片1:");image1.display();}
}
优点:
- 可以在不修改目标对象的前提下,控制对目标对象的访问
- 将客户端与目标对象分离,降低系统的耦合度
- 可以扩展目标对象的功能
缺点:
- 增加了系统的复杂度
- 可能会导致请求的处理速度变慢
行为型模式
行为型模式关注对象之间的通信,描述类或对象之间怎样相互协作以及怎样分配职责。
责任链模式
定义:避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
实际应用:日志记录、异常处理、请求过滤、权限验证等。
代码示例:企业审批流程
// 请求类
public class PurchaseRequest {private int id;private double amount;private String purpose;public PurchaseRequest(int id, double amount, String purpose) {this.id = id;this.amount = amount;this.purpose = purpose;}public int getId() {return id;}public double getAmount() {return amount;}public String getPurpose() {return purpose;}@Overridepublic String toString() {return "采购申请 #" + id + " [金额: " + amount + ", 用途: " + purpose + "]";}
}// 处理者接口
public abstract class Approver {protected Approver nextApprover;protected String name;public Approver(String name) {this.name = name;}public void setNextApprover(Approver nextApprover) {this.nextApprover = nextApprover;}public abstract void processRequest(PurchaseRequest request);
}// 具体处理者:部门经理
public class DepartmentManager extends Approver {public DepartmentManager(String name) {super(name);}@Overridepublic void processRequest(PurchaseRequest request) {if (request.getAmount() <= 5000) {System.out.println(name + " (部门经理) 已批准 " + request);} else if (nextApprover != null) {System.out.println(name + " (部门经理) 无权批准,转交给上级...");nextApprover.processRequest(request);} else {System.out.println("请求无法批准");}}
}// 具体处理者:副总裁
public class VicePresident extends Approver {public VicePresident(String name) {super(name);}@Overridepublic void processRequest(PurchaseRequest request) {if (request.getAmount() <= 25000) {System.out.println(name + " (副总裁) 已批准 " + request);} else if (nextApprover != null) {System.out.println(name + " (副总裁) 无权批准,转交给上级...");nextApprover.processRequest(request);} else {System.out.println("请求无法批准");}}
}// 具体处理者:总裁
public class President extends Approver {public President(String name) {super(name);}@Overridepublic void processRequest(PurchaseRequest request) {if (request.getAmount() <= 100000) {System.out.println(name + " (总裁) 已批准 " + request);} else if (nextApprover != null) {System.out.println(name + " (总裁) 无权批准,转交给上级...");nextApprover.processRequest(request);} else {System.out.println("请求无法批准,需要董事会审批");}}
}// 具体处理者:董事会
public class BoardOfDirectors extends Approver {public BoardOfDirectors(String name) {super(name);}@Overridepublic void processRequest(PurchaseRequest request) {if (request.getAmount() <= 500000) {System.out.println(name + " (董事会) 已批准 " + request);} else {System.out.println("金额过大," + name + " (董事会) 拒绝了 " + request);}}
}// 客户端代码
public class PurchaseSystem {public static void main(String[] args) {// 创建责任链Approver manager = new DepartmentManager("张经理");Approver vp = new VicePresident("王副总");Approver president = new President("李总");Approver board = new BoardOfDirectors("董事会");// 设置责任链manager.setNextApprover(vp);vp.setNextApprover(president);president.setNextApprover(board);// 创建采购请求PurchaseRequest request1 = new PurchaseRequest(1, 4500, "购买办公设备");PurchaseRequest request2 = new PurchaseRequest(2, 20000, "团队建设活动");PurchaseRequest request3 = new PurchaseRequest(3, 60000, "服务器升级");PurchaseRequest request4 = new PurchaseRequest(4, 300000, "部门扩建");PurchaseRequest request5 = new PurchaseRequest(5, 600000, "公司收购");// 处理请求System.out.println("===== 请求1 =====");manager.processRequest(request1);System.out.println("\n===== 请求2 =====");manager.processRequest(request2);System.out.println("\n===== 请求3 =====");manager.processRequest(request3);System.out.println("\n===== 请求4 =====");manager.processRequest(request4);System.out.println("\n===== 请求5 =====");manager.processRequest(request5);}
}
优点:
- 降低了对象之间的耦合度
- 增强了系统的可扩展性
- 责任分担,每个类只需关注自己的职责
缺点:
- 不能保证请求一定会被处理
- 可能会导致系统性能下降,特别是链条很长的时候
- 调试不方便
命令模式
定义:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。
实际应用:GUI按钮点击、事务管理、命令调度、多级撤销等。
代码示例:智能家居遥控器
// 命令接口
public interface Command {void execute();void undo();
}// 接收者:灯
public class Light {private String location;public Light(String location) {this.location = location;}public void on() {System.out.println(location + " 灯已打开");}public void off() {System.out.println(location + " 灯已关闭");}
}// 接收者:音响
public class Stereo {private String location;public Stereo(String location) {this.location = location;}public void on() {System.out.println(location + " 音响已打开");}public void off() {System.out.println(location + " 音响已关闭");}public void setCD() {System.out.println(location + " 音响已设置为CD模式");}public void setVolume(int volume) {System.out.println(location + " 音响音量设置为 " + volume);}
}// 具体命令:开灯
public class LightOnCommand implements Command {private Light light;public LightOnCommand(Light light) {this.light = light;}@Overridepublic void execute() {light.on();}@Overridepublic void undo() {light.off();}
}// 具体命令:关灯
public class LightOffCommand implements Command {private Light light;public LightOffCommand(Light light) {this.light = light;}@Overridepublic void execute() {light.off();}@Overridepublic void undo() {light.on();}
}// 具体命令:打开音响
public class StereoOnWithCDCommand implements Command {private Stereo stereo;public StereoOnWithCDCommand(Stereo stereo) {this.stereo = stereo;}@Overridepublic void execute() {stereo.on();stereo.setCD();stereo.setVolume(11);}@Overridepublic void undo() {stereo.off();}
}// 具体命令:关闭音响
public class StereoOffCommand implements Command {private Stereo stereo;public StereoOffCommand(Stereo stereo) {this.stereo = stereo;}@Overridepublic void execute() {stereo.off();}@Overridepublic void undo() {stereo.on();stereo.setCD();stereo.setVolume(11);}
}// 空命令(无操作)
public class NoCommand implements Command {@Overridepublic void execute() {// 什么也不做}@Overridepublic void undo() {// 什么也不做}
}// 调用者:遥控器
public class RemoteControl {private Command[] onCommands;private Command[] offCommands;private Command undoCommand;public RemoteControl() {onCommands = new Command[7];offCommands = new Command[7];Command noCommand = new NoCommand();for (int i = 0; i < 7; i++) {onCommands[i] = noCommand;offCommands[i] = noCommand;}undoCommand = noCommand;}public void setCommand(int slot, Command onCommand, Command offCommand) {onCommands[slot] = onCommand;offCommands[slot] = offCommand;}public void onButtonWasPushed(int slot) {onCommands[slot].execute();undoCommand = onCommands[slot];}public void offButtonWasPushed(int slot) {offCommands[slot].execute();undoCommand = offCommands[slot];}public void undoButtonWasPushed() {undoCommand.undo();}@Overridepublic String toString() {StringBuilder sb = new StringBuilder();sb.append("\n------ 遥控器 ------\n");for (int i = 0; i < onCommands.length; i++) {sb.append("[插槽 " + i + "] " + onCommands[i].getClass().getSimpleName() + " " + offCommands[i].getClass().getSimpleName() + "\n");}sb.append("[撤销] " + undoCommand.getClass().getSimpleName() + "\n");return sb.toString();}
}// 客户端代码
public class RemoteControlTest {public static void main(String[] args) {// 创建遥控器RemoteControl remote = new RemoteControl();// 创建设备Light livingRoomLight = new Light("客厅");Light kitchenLight = new Light("厨房");Stereo stereo = new Stereo("客厅");// 创建命令LightOnCommand livingRoomLightOn = new LightOnCommand(livingRoomLight);LightOffCommand livingRoomLightOff = new LightOffCommand(livingRoomLight);LightOnCommand kitchenLightOn = new LightOnCommand(kitchenLight);LightOffCommand kitchenLightOff = new LightOffCommand(kitchenLight);StereoOnWithCDCommand stereoOn = new StereoOnWithCDCommand(stereo);StereoOffCommand stereoOff = new StereoOffCommand(stereo);// 设置命令到遥控器remote.setCommand(0, livingRoomLightOn, livingRoomLightOff);remote.setCommand(1, kitchenLightOn, kitchenLightOff);remote.setCommand(2, stereoOn, stereoOff);// 显示遥控器System.out.println(remote);// 测试按钮System.out.println("--- 按下按钮 ---");remote.onButtonWasPushed(0);remote.offButtonWasPushed(0);remote.onButtonWasPushed(1);remote.offButtonWasPushed(1);remote.onButtonWasPushed(2);remote.offButtonWasPushed(2);// 测试撤销System.out.println("\n--- 测试撤销 ---");remote.onButtonWasPushed(0);remote.undoButtonWasPushed();}
}
优点:
- 将请求发送者和接收者解耦
- 可以将命令对象存储在队列中
- 可以方便地实现撤销和重做功能
- 可以组合命令,实现宏命令
缺点:
- 可能会导致系统中有过多的具体命令类
- 增加了系统的复杂度
解释器模式
定义:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
实际应用:编译器、正则表达式、SQL解析器等。
代码示例:简单的数学表达式解释器
// 抽象表达式
public interface Expression {int interpret();
}// 终结符表达式:数字
public class NumberExpression implements Expression {private int number;public NumberExpression(int number) {this.number = number;}@Overridepublic int interpret() {return number;}
}// 非终结符表达式:加法
public class AddExpression implements Expression {private Expression leftExpression;private Expression rightExpression;public AddExpression(Expression leftExpression, Expression rightExpression) {this.leftExpression = leftExpression;this.rightExpression = rightExpression;}@Overridepublic int interpret() {return leftExpression.interpret() + rightExpression.interpret();}
}// 非终结符表达式:减法
public class SubtractExpression implements Expression {private Expression leftExpression;private Expression rightExpression;public SubtractExpression(Expression leftExpression, Expression rightExpression) {this.leftExpression = leftExpression;this.rightExpression = rightExpression;}@Overridepublic int interpret() {return leftExpression.interpret() - rightExpression.interpret();}
}// 非终结符表达式:乘法
public class MultiplyExpression implements Expression {private Expression leftExpression;private Expression rightExpression;public MultiplyExpression(Expression leftExpression, Expression rightExpression) {this.leftExpression = leftExpression;this.rightExpression = rightExpression;}@Overridepublic int interpret() {return leftExpression.interpret() * rightExpression.interpret();}
}// 解析器:将字符串解析为表达式
public class Parser {public static Expression parse(String expression) {// 这里简化处理,假设输入格式为"数字 运算符 数字"String[] tokens = expression.split(" ");if (tokens.length != 3) {throw new IllegalArgumentException("Invalid expression format");}Expression leftExpression = new NumberExpression(Integer.parseInt(tokens[0]));Expression rightExpression = new NumberExpression(Integer.parseInt(tokens[2]));switch (tokens[1]) {case "+":return new AddExpression(leftExpression, rightExpression);case "-":return new SubtractExpression(leftExpression, rightExpression);case "*":return new MultiplyExpression(leftExpression, rightExpression);default:throw new IllegalArgumentException("Unknown operator: " + tokens[1]);}}
}// 客户端代码
public class Calculator {public static void main(String[] args) {String[] expressions = {"5 + 3", "10 - 4", "7 * 2"};for (String expressionStr : expressions) {Expression expression = Parser.parse(expressionStr);int result = expression.interpret();System.out.println(expressionStr + " = " + result);}}
}
优点:
- 易于改变和扩展文法
- 每一条文法规则都可以表示为一个类,方便维护
- 增加新的解释表达式很容易
缺点:
- 对于复杂的文法,类的数量会急剧增加
- 对于频繁更改的文法,维护困难
- 可能会引起效率问题
迭代器模式
定义:提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。
实际应用:集合遍历、数据库查询结果遍历、目录遍历等。
代码示例:自定义集合遍历
// 迭代器接口
public interface Iterator<T> {boolean hasNext();T next();
}// 集合接口
public interface Collection<T> {Iterator<T> createIterator();void add(T item);int size();
}// 具体集合:书架
public class BookShelf implements Collection<Book> {private List<Book> books;public BookShelf() {books = new ArrayList<>();}@Overridepublic Iterator<Book> createIterator() {return new BookShelfIterator(this);}@Overridepublic void add(Book book) {books.add(book);}@Overridepublic int size() {return books.size();}public Book getBookAt(int index) {return books.get(index);}
}// 具体迭代器:书架迭代器
public class BookShelfIterator implements Iterator<Book> {private BookShelf bookShelf;private int index;public BookShelfIterator(BookShelf bookShelf) {this.bookShelf = bookShelf;this.index = 0;}@Overridepublic boolean hasNext() {return index < bookShelf.size();}@Overridepublic Book next() {if (!hasNext()) {throw new NoSuchElementException();}Book book = bookShelf.getBookAt(index);index++;return book;}
}// 书类
public class Book {private String name;private String author;public Book(String name, String author) {this.name = name;this.author = author;}public String getName() {return name;}public String getAuthor() {return author;}@Overridepublic String toString() {return "《" + name + "》 作者: " + author;}
}// 客户端代码
public class Library {public static void main(String[] args) {BookShelf bookShelf = new BookShelf();// 添加书籍bookShelf.add(new Book("设计模式", "Erich Gamma等"));bookShelf.add(new Book("重构", "Martin Fowler"));bookShelf.add(new Book("代码整洁之道", "Robert C. Martin"));bookShelf.add(new Book("Java编程思想", "Bruce Eckel"));// 使用迭代器遍历System.out.println("=== 图书馆藏书列表 ===");Iterator<Book> iterator = bookShelf.createIterator();while (iterator.hasNext()) {Book book = iterator.next();System.out.println(book);}}
}
优点:
- 支持以不同的方式遍历一个聚合对象
- 迭代器简化了聚合类的接口
- 在同一个聚合上可以有多个遍历
- 增加新的聚合类和迭代器类都很方便
缺点:
- 增加了系统的复杂性
- 对于比较简单的遍历,使用迭代器模式显得小题大做
中介者模式
定义:用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
实际应用:GUI组件交互、聊天室、航空管制系统等。
代码示例:聊天室系统
// 中介者接口
public interface ChatMediator {void sendMessage(String message, User user);void addUser(User user);
}// 具体中介者:聊天室
public class ChatRoom implements ChatMediator {private List<User> users;public ChatRoom() {this.users = new ArrayList<>();}@Overridepublic void sendMessage(String message, User sender) {for (User user : users) {// 消息不发送给发送者自己if (user != sender) {user.receive(message);}}}@Overridepublic void addUser(User user) {users.add(user);System.out.println(user.getName() + " 加入了聊天室");}
}// 抽象同事类:用户
public abstract class User {protected ChatMediator mediator;protected String name;public User(ChatMediator mediator, String name) {this.mediator = mediator;this.name = name;}public String getName() {return name;}public abstract void send(String message);public abstract void receive(String message);
}// 具体同事类:普通用户
public class NormalUser extends User {public NormalUser(ChatMediator mediator, String name) {super(mediator, name);}@Overridepublic void send(String message) {System.out.println(name + " 发送消息: " + message);mediator.sendMessage(message, this);}@Overridepublic void receive(String message) {System.out.println(name + " 收到消息: " + message);}
}// 具体同事类:高级用户
public class PremiumUser extends User {public PremiumUser(ChatMediator mediator, String name) {super(mediator, name);}@Overridepublic void send(String message) {System.out.println("[高级用户] " + name + " 发送消息: " + message);mediator.sendMessage("[高级用户] " + message, this);}@Overridepublic void receive(String message) {System.out.println("[高级用户] " + name + " 收到消息: " + message);}
}// 客户端代码
public class ChatApplication {public static void main(String[] args) {// 创建中介者ChatMediator chatRoom = new ChatRoom();// 创建用户User user1 = new NormalUser(chatRoom, "张三");User user2 = new NormalUser(chatRoom, "李四");User user3 = new PremiumUser(chatRoom, "王五");User user4 = new NormalUser(chatRoom, "赵六");// 添加用户到聊天室chatRoom.addUser(user1);chatRoom.addUser(user2);chatRoom.addUser(user3);chatRoom.addUser(user4);// 用户发送消息user1.send("大家好!");System.out.println();user3.send("你好,我是王五");System.out.println();user2.send("今天天气真好");}
}
优点:
- 降低了对象之间的耦合性,使得对象易于独立地复用
- 将对象间的一对多关联转变为一对一的关联
- 符合迪米特法则
缺点:
- 中介者可能会变得过于复杂
- 可能会导致系统的可维护性变差
备忘录模式
定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。
实际应用:撤销功能、存档功能、事务回滚等。
代码示例:文本编辑器的撤销功能
// 备忘录类
public class TextMemento {private String text;private int cursorPosition;private long timestamp;public TextMemento(String text, int cursorPosition) {this.text = text;this.cursorPosition = cursorPosition;this.timestamp = System.currentTimeMillis();}public String getText() {return text;}public int getCursorPosition() {return cursorPosition;}public long getTimestamp() {return timestamp;}@Overridepublic String toString() {return "备忘点 [" + new Date(timestamp) + "] - 文本长度: " + text.length() + ", 光标位置: " + cursorPosition;}
}// 发起人:文本编辑器
public class TextEditor {private String text;private int cursorPosition;public TextEditor() {this.text = "";this.cursorPosition = 0;}public void write(String text) {String beforeCursor = this.text.substring(0, cursorPosition);String afterCursor = this.text.substring(cursorPosition);this.text = beforeCursor + text + afterCursor;this.cursorPosition += text.length();System.out.println("写入文本: " + text);}public void delete(int length) {if (cursorPosition - length < 0) {length = cursorPosition;}if (length > 0) {String beforeCursor = this.text.substring(0, cursorPosition - length);String afterCursor = this.text.substring(cursorPosition);String deletedText = this.text.substring(cursorPosition - length, cursorPosition);this.text = beforeCursor + afterCursor;this.cursorPosition -= length;System.out.println("删除文本: " + deletedText);}}public void moveCursor(int position) {if (position >= 0 && position <= text.length()) {this.cursorPosition = position;System.out.println("移动光标到位置: " + position);}}public TextMemento save() {return new TextMemento(text, cursorPosition);}public void restore(TextMemento memento) {this.text = memento.getText();this.cursorPosition = memento.getCursorPosition();System.out.println("恢复到之前的状态");}public void display() {System.out.println("当前文本: \"" + text + "\"");System.out.println("光标位置: " + cursorPosition);}
}// 管理者:历史记录
public class History {private List<TextMemento> mementos = new ArrayList<>();private int currentIndex = -1;public void push(TextMemento memento) {// 如果在历史中间进行了新操作,则清除后面的历史if (currentIndex < mementos.size() - 1) {mementos = mementos.subList(0, currentIndex + 1);}mementos.add(memento);currentIndex = mementos.size() - 1;System.out.println("保存当前状态");}public TextMemento undo() {if (currentIndex <= 0) {System.out.println("已经是最早的状态,无法撤销");return mementos.get(0);}currentIndex--;TextMemento memento = mementos.get(currentIndex);System.out.println("撤销到: " + memento);return memento;}public TextMemento redo() {if (currentIndex >= mementos.size() - 1) {System.out.println("已经是最新的状态,无法重做");return mementos.get(currentIndex);}currentIndex++;TextMemento memento = mementos.get(currentIndex);System.out.println("重做到: " + memento);return memento;}public void showHistory() {System.out.println("=== 历史记录 ===");for (int i = 0; i < mementos.size(); i++) {String marker = (i == currentIndex) ? " <- 当前" : "";System.out.println(i + ": " + mementos.get(i) + marker);}}
}// 客户端代码
public class TextEditorApp {public static void main(String[] args) {TextEditor editor = new TextEditor();History history = new History();// 初始状态history.push(editor.save());// 编辑操作editor.write("Hello");editor.display();history.push(editor.save());editor.write(" World");editor.display();history.push(editor.save());editor.write("!");editor.display();history.push(editor.save());editor.moveCursor(5);editor.write(" Beautiful");editor.display();history.push(editor.save());// 显示历史记录history.showHistory();// 撤销操作System.out.println("\n=== 执行撤销 ===");editor.restore(history.undo());editor.display();System.out.println("\n=== 再次执行撤销 ===");editor.restore(history.undo());editor.display();// 重做操作System.out.println("\n=== 执行重做 ===");editor.restore(history.redo());editor.display();// 在历史中间进行新操作System.out.println("\n=== 在历史中间进行新操作 ===");editor.delete(6);editor.display();history.push(editor.save());// 再次显示历史记录history.showHistory();}
}
优点:
- 提供了一种状态恢复的机制
- 实现了信息的封装,客户端不需要关心状态的保存细节
- 提供了可以回到过去某一状态的机制
缺点:
- 如果状态数据较大,或者频繁保存状态,会占用大量内存
- 可能需要额外的存储空间和管理机制
观察者模式
定义:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
实际应用:事件处理系统、消息订阅、数据同步等。
代码示例:股票价格监控系统
// 主题接口
public interface Subject {void registerObserver(Observer observer);void removeObserver(Observer observer);void notifyObservers();
}// 观察者接口
public interface Observer {void update(String stockName, double price);
}// 具体主题:股票
public class Stock implements Subject {private String name;private double price;private List<Observer> observers;public Stock(String name, double price) {this.name = name;this.price = price;this.observers = new ArrayList<>();}@Overridepublic void registerObserver(Observer observer) {if (!observers.contains(observer)) {observers.add(observer);}}@Overridepublic void removeObserver(Observer observer) {observers.remove(observer);}@Overridepublic void notifyObservers() {for (Observer observer : observers) {observer.update(name, price);}}public void setPrice(double price) {System.out.println("\n" + name + " 股票价格更新: " + this.price + " -> " + price);this.price = price;notifyObservers();}public String getName() {return name;}public double getPrice() {return price;}
}// 具体观察者:投资者
public class Investor implements Observer {private String name;private Map<String, Double> stocks;public Investor(String name) {this.name = name;this.stocks = new HashMap<>();}@Overridepublic void update(String stockName, double price) {System.out.println(name + " 收到 " + stockName + " 价格更新: " + price);// 记录关注的股票价格stocks.put(stockName, price);// 根据价格变化做出决策makeDecision(stockName, price);}private void makeDecision(String stockName, double price) {// 简单的决策逻辑if (price > 100) {System.out.println(name + " 决定卖出 " + stockName);} else if (price < 50) {System.out.println(name + " 决定买入 " + stockName);} else {System.out.println(name + " 决定持有 " + stockName);}}public void addInterestedStock(Stock stock) {stock.registerObserver(this);stocks.put(stock.getName(), stock.getPrice());System.out.println(name + " 开始关注股票 " + stock.getName() + ",当前价格: " + stock.getPrice());}public void removeInterestedStock(Stock stock) {stock.removeObserver(this);stocks.remove(stock.getName());System.out.println(name + " 不再关注股票 " + stock.getName());}
}// 具体观察者:股票分析师
public class StockAnalyst implements Observer {private String name;private Map<String, List<Double>> stockHistory;public StockAnalyst(String name) {this.name = name;this.stockHistory = new HashMap<>();}@Overridepublic void update(String stockName, double price) {System.out.println("分析师 " + name + " 收到 " + stockName + " 价格更新: " + price);// 记录股票价格历史stockHistory.putIfAbsent(stockName, new ArrayList<>());stockHistory.get(stockName).add(price);// 分析股票趋势analyzeTrend(stockName);}private void analyzeTrend(String stockName) {List<Double> history = stockHistory.get(stockName);if (history.size() < 2) {System.out.println("分析师 " + name + ": " + stockName + " 数据不足,无法分析趋势");return;}double current = history.get(history.size() - 1);double previous = history.get(history.size() - 2);if (current > previous) {System.out.println("分析师 " + name + ": " + stockName + " 呈上升趋势,预计将继续上涨");} else if (current < previous) {System.out.println("分析师 " + name + ": " + stockName + " 呈下降趋势,预计将继续下跌");} else {System.out.println("分析师 " + name + ": " + stockName + " 价格稳定,预计将维持当前价位");}}public void addInterestedStock(Stock stock) {stock.registerObserver(this);stockHistory.putIfAbsent(stock.getName(), new ArrayList<>());stockHistory.get(stock.getName()).add(stock.getPrice());System.out.println("分析师 " + name + " 开始关注股票 " + stock.getName());}
}// 客户端代码
public class StockMarket {public static void main(String[] args) {// 创建股票Stock appleStock = new Stock("苹果", 150.0);Stock googleStock = new Stock("谷歌", 2800.0);Stock amazonStock = new Stock("亚马逊", 3300.0);// 创建观察者Investor investor1 = new Investor("张三");Investor investor2 = new Investor("李四");StockAnalyst analyst = new StockAnalyst("王五");// 投资者关注股票investor1.addInterestedStock(appleStock);investor1.addInterestedStock(googleStock);investor2.addInterestedStock(appleStock);investor2.addInterestedStock(amazonStock);// 分析师关注股票analyst.addInterestedStock(appleStock);analyst.addInterestedStock(googleStock);analyst.addInterestedStock(amazonStock);// 股票价格变化appleStock.setPrice(145.0);googleStock.setPrice(2850.0);// 投资者不再关注某只股票investor1.removeInterestedStock(googleStock);// 股票价格继续变化amazonStock.setPrice(3250.0);appleStock.setPrice(160.0);}
}
优点:
- 实现了观察者和被观察者的解耦
- 支持广播通信
- 符合开闭原则,增加新的观察者无需修改原有代码
缺点:
- 如果观察者太多,通知所有观察者会花费较多时间
- 如果观察者和被观察者之间有循环依赖,可能导致系统崩溃
- 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的
状态模式
定义:允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。
实际应用:工作流系统、游戏角色状态、订单处理流程等。
代码示例:自动售货机状态管理
// 状态接口
public interface State {void insertCoin();void ejectCoin();void selectProduct();void dispense();
}// 具体状态:没有硬币
public class NoCoinState implements State {private VendingMachine vendingMachine;public NoCoinState(VendingMachine vendingMachine) {this.vendingMachine = vendingMachine;}@Overridepublic void insertCoin() {System.out.println("投入硬币成功");vendingMachine.setState(vendingMachine.getHasCoinState());}@Overridepublic void ejectCoin() {System.out.println("没有硬币,无法退币");}@Overridepublic void selectProduct() {System.out.println("请先投入硬币");}@Overridepublic void dispense() {System.out.println("请先投入硬币并选择商品");}@Overridepublic String toString() {return "等待投币";}
}// 具体状态:有硬币
public class HasCoinState implements State {private VendingMachine vendingMachine;public HasCoinState(VendingMachine vendingMachine) {this.vendingMachine = vendingMachine;}@Overridepublic void insertCoin() {System.out.println("已经有硬币了,无需再投");}@Overridepublic void ejectCoin() {System.out.println("退币成功");vendingMachine.setState(vendingMachine.getNoCoinState());}@Overridepublic void selectProduct() {System.out.println("已选择商品");vendingMachine.setState(vendingMachine.getSoldState());}@Overridepublic void dispense() {System.out.println("请先选择商品");}@Overridepublic String toString() {return "等待选择商品";}
}// 具体状态:售出商品
public class SoldState implements State {private VendingMachine vendingMachine;public SoldState(VendingMachine vendingMachine) {this.vendingMachine = vendingMachine;}@Overridepublic void insertCoin() {System.out.println("请等待,正在出货");}@Overridepublic void ejectCoin() {System.out.println("已选择商品,无法退币");}@Overridepublic void selectProduct() {System.out.println("已选择商品,请等待出货");}@Overridepublic void dispense() {System.out.println("商品已出货,请取走");vendingMachine.releaseProduct();if (vendingMachine.getCount() > 0) {vendingMachine.setState(vendingMachine.getNoCoinState());} else {System.out.println("商品已售罄");vendingMachine.setState(vendingMachine.getSoldOutState());}}@Overridepublic String toString() {return "正在出货";}
}// 具体状态:售罄
public class SoldOutState implements State {private VendingMachine vendingMachine;public SoldOutState(VendingMachine vendingMachine) {this.vendingMachine = vendingMachine;}@Overridepublic void insertCoin() {System.out.println("商品已售罄,不接受投币");ejectCoin();}@Overridepublic void ejectCoin() {System.out.println("没有硬币,无法退币");}@Overridepublic void selectProduct() {System.out.println("商品已售罄,无法选择");}@Overridepublic void dispense() {System.out.println("商品已售罄,无法出货");}@Overridepublic String toString() {return "商品售罄";}
}// 上下文:自动售货机
public class VendingMachine {private State noCoinState;private State hasCoinState;private State soldState;private State soldOutState;private State currentState;private int count;public VendingMachine(int count) {// 初始化状态noCoinState = new NoCoinState(this);hasCoinState = new HasCoinState(this);soldState = new SoldState(this);soldOutState = new SoldOutState(this);this.count = count;if (count > 0) {currentState = noCoinState;} else {currentState = soldOutState;}}public void insertCoin() {currentState.insertCoin();}public void ejectCoin() {currentState.ejectCoin();}public void selectProduct() {currentState.selectProduct();currentState.dispense();}public void releaseProduct() {if (count > 0) {count--;System.out.println("商品已发放");}}public void refill(int count) {this.count += count;System.out.println("补充商品 " + count + " 个,当前库存: " + this.count);if (this.count > 0) {currentState = noCoinState;}}public void setState(State state) {this.currentState = state;System.out.println("售货机状态变为: " + state);}public int getCount() {return count;}// Getters for statespublic State getNoCoinState() {return noCoinState;}public State getHasCoinState() {return hasCoinState;}public State getSoldState() {return soldState;}public State getSoldOutState() {return soldOutState;}
}// 客户端代码
public class VendingMachineTest {public static void main(String[] args) {// 创建售货机,初始有5个商品VendingMachine vendingMachine = new VendingMachine(5);// 测试正常购买流程System.out.println("===== 测试正常购买流程 =====");vendingMachine.insertCoin();vendingMachine.selectProduct();// 测试没有投币就选择商品System.out.println("\n===== 测试没有投币就选择商品 =====");vendingMachine.selectProduct();// 测试投币后退币System.out.println("\n===== 测试投币后退币 =====");vendingMachine.insertCoin();vendingMachine.ejectCoin();// 测试多次投币System.out.println("\n===== 测试多次投币 =====");vendingMachine.insertCoin();vendingMachine.insertCoin();vendingMachine.selectProduct();// 测试售罄情况System.out.println("\n===== 测试售罄情况 =====");// 购买剩余的商品for (int i = 0; i < 3; i++) {vendingMachine.insertCoin();vendingMachine.selectProduct();}// 尝试在售罄状态下购买vendingMachine.insertCoin();vendingMachine.selectProduct();// 补充商品System.out.println("\n===== 补充商品 =====");vendingMachine.refill(3);vendingMachine.insertCoin();vendingMachine.selectProduct();}
}
优点:
- 将状态转换显式化,减少了if-else语句
- 状态对象可以被共享
- 容易增加新的状态和转换
缺点:
- 状态模式的使用必然会增加系统类和对象的个数
- 状态模式的结构与实现都较为复杂
策略模式
定义:定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。策略模式使得算法可独立于使用它的客户而变化。
实际应用:支付方式选择、排序算法选择、压缩算法选择等。
代码示例:支付系统
// 策略接口
public interface PaymentStrategy {void pay(double amount);
}// 具体策略:信用卡支付
public class CreditCardPayment implements PaymentStrategy {private String cardNumber;private String name;private String cvv;private String expiryDate;public CreditCardPayment(String cardNumber, String name, String cvv, String expiryDate) {this.cardNumber = cardNumber;this.name = name;this.cvv = cvv;this.expiryDate = expiryDate;}@Overridepublic void pay(double amount) {System.out.println("使用信用卡支付 " + amount + " 元");System.out.println("信用卡信息: " + name + ", " + "卡号: " + maskCardNumber(cardNumber));}private String maskCardNumber(String cardNumber) {return "xxxx-xxxx-xxxx-" + cardNumber.substring(cardNumber.length() - 4);}
}// 具体策略:支付宝支付
public class AlipayPayment implements PaymentStrategy {private String email;private String password;public AlipayPayment(String email, String password) {this.email = email;this.password = password;}@Overridepublic void pay(double amount) {System.out.println("使用支付宝支付 " + amount + " 元");System.out.println("支付宝账号: " + email);}
}// 具体策略:微信支付
public class WeChatPayment implements PaymentStrategy {private String id;private String qrCode;public WeChatPayment(String id, String qrCode) {this.id = id;this.qrCode = qrCode;}@Overridepublic void pay(double amount) {System.out.println("使用微信支付 " + amount + " 元");System.out.println("微信ID: " + id);System.out.println("扫描二维码: " + qrCode);}
}// 上下文:购物车
public class ShoppingCart {private List<Item> items;public ShoppingCart() {this.items = new ArrayList<>();}public void addItem(Item item) {items.add(item);}public void removeItem(Item item) {items.remove(item);}public double calculateTotal() {return items.stream().mapToDouble(Item::getPrice).sum();}public void pay(PaymentStrategy paymentStrategy) {double amount = calculateTotal();paymentStrategy.pay(amount);}
}// 商品类
public class Item {private String id;private String name;private double price;public Item(String id, String name, double price) {this.id = id;this.name = name;this.price = price;}public String getId() {return id;}public String getName() {return name;}public double getPrice() {return price;}
}// 客户端代码
public class ShoppingCartDemo {public static void main(String[] args) {// 创建购物车并添加商品ShoppingCart cart = new ShoppingCart();cart.addItem(new Item("1", "手机", 5999));cart.addItem(new Item("2", "耳机", 999));cart.addItem(new Item("3", "保护壳", 99));// 显示总金额System.out.println("购物车总金额: " + cart.calculateTotal() + " 元");// 使用不同的支付策略System.out.println("\n===== 使用信用卡支付 =====");cart.pay(new CreditCardPayment("1234567890123456", "张三", "123", "12/25"));System.out.println("\n===== 使用支付宝支付 =====");cart.pay(new AlipayPayment("zhangsan@example.com", "password"));System.out.println("\n===== 使用微信支付 =====");cart.pay(new WeChatPayment("zhangsan", "https://weixin.qq.com/pay/qrcode123456"));}
}
优点:
- 算法可以自由切换
- 避免使用多重条件判断
- 扩展性良好,增加新的策略很方便
缺点:
- 客户端必须知道所有的策略类,并自行决定使用哪一个策略类
- 策略模式将造成产生很多策略类
模板方法模式
定义:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
实际应用:数据导入导出流程、构建流程、测试框架等。
代码示例:数据挖掘应用
// 抽象类,定义算法骨架
public abstract class DataMiner {// 模板方法,定义算法骨架public final void mineData(String filePath) {String rawData = readData(filePath);String processedData = processData(rawData);String analysis = analyzeData(processedData);sendReport(analysis);// 钩子方法,可选的额外处理if (shouldStoreData()) {storeData(processedData);}}// 原始方法 - 由子类实现protected abstract String readData(String filePath);// 原始方法 - 由子类实现protected abstract String processData(String data);// 具体方法 - 所有子类共享的实现protected String analyzeData(String data) {System.out.println("分析数据...");// 通用的数据分析逻辑return "数据分析结果:" + data.substring(0, Math.min(20, data.length())) + "...";}// 具体方法 - 所有子类共享的实现protected void sendReport(String analysis) {System.out.println("发送数据分析报告...");System.out.println(analysis);}// 钩子方法 - 默认实现,子类可以覆盖protected boolean shouldStoreData() {return true;}// 具体方法 - 所有子类共享的实现protected void storeData(String data) {System.out.println("存储处理后的数据到数据库...");}
}// 具体子类 - CSV文件数据挖掘器
public class CSVDataMiner extends DataMiner {@Overrideprotected String readData(String filePath) {System.out.println("从CSV文件读取数据: " + filePath);// 实际应用中,这里会有真正的CSV文件读取逻辑return "id,name,value\n1,A,100\n2,B,200\n3,C,300";}@Overrideprotected String processData(String data) {System.out.println("处理CSV数据...");// 处理CSV特有的数据格式String[] lines = data.split("\n");StringBuilder processed = new StringBuilder();for (int i = 1; i < lines.length; i++) { // 跳过标题行String[] values = lines[i].split(",");processed.append("行 ").append(i).append(": ");processed.append("ID=").append(values[0]).append(", ");processed.append("名称=").append(values[1]).append(", ");processed.append("值=").append(values[2]).append("\n");}return processed.toString();}
}// 具体子类 - JSON文件数据挖掘器
public class JSONDataMiner extends DataMiner {@Overrideprotected String readData(String filePath) {System.out.println("从JSON文件读取数据: " + filePath);// 实际应用中,这里会有真正的JSON文件读取逻辑return "[{\"id\":1,\"name\":\"A\",\"value\":100},{\"id\":2,\"name\":\"B\",\"value\":200},{\"id\":3,\"name\":\"C\",\"value\":300}]";}@Overrideprotected String processData(String data) {System.out.println("处理JSON数据...");// 处理JSON特有的数据格式// 实际应用中,这里会使用JSON解析库String simplified = data.replace("[", "").replace("]", "");String[] objects = simplified.split("\\},\\{");StringBuilder processed = new StringBuilder();for (int i = 0; i < objects.length; i++) {String obj = objects[i].replace("{", "").replace("}", "");processed.append("对象 ").append(i + 1).append(": ");String[] properties = obj.split(",");for (String property : properties) {String[] keyValue = property.split(":");String key = keyValue[0].replace("\"", "");String value = keyValue[1].replace("\"", "");processed.append(key).append("=").append(value).append(", ");}processed.append("\n");}return processed.toString();}@Overrideprotected boolean shouldStoreData() {// 覆盖钩子方法,决定不存储JSON数据return false;}
}// 具体子类 - 数据库数据挖掘器
public class DatabaseDataMiner extends DataMiner {@Overrideprotected String readData(String connectionString) {System.out.println("从数据库读取数据: " + connectionString);// 实际应用中,这里会有真正的数据库查询逻辑return "ID\tNAME\tVALUE\n1\tA\t100\n2\tB\t200\n3\tC\t300";}@Overrideprotected String processData(String data) {System.out.println("处理数据库数据...");// 处理数据库特有的数据格式String[] lines = data.split("\n");StringBuilder processed = new StringBuilder();for (int i = 1; i < lines.length; i++) { // 跳过标题行String[] values = lines[i].split("\t");processed.append("记录 ").append(i).append(": ");processed.append("ID=").append(values[0]).append(", ");processed.append("名称=").append(values[1]).append(", ");processed.append("值=").append(values[2]).append("\n");}return processed.toString();}@Overrideprotected String analyzeData(String data) {// 覆盖父类的方法,提供特殊的数据库数据分析System.out.println("使用特殊算法分析数据库数据...");return "数据库数据分析结果:" + data.substring(0, Math.min(30, data.length())) + "...";}
}// 客户端代码
public class DataMiningApp {public static void main(String[] args) {System.out.println("===== 使用CSV数据挖掘器 =====");DataMiner csvMiner = new CSVDataMiner();csvMiner.mineData("data.csv");System.out.println("\n===== 使用JSON数据挖掘器 =====");DataMiner jsonMiner = new JSONDataMiner();jsonMiner.mineData("data.json");System.out.println("\n===== 使用数据库数据挖掘器 =====");DataMiner dbMiner = new DatabaseDataMiner();dbMiner.mineData("jdbc:mysql://localhost:3306/testdb");}
}
优点:
- 封装不变部分,扩展可变部分
- 提取公共代码,便于维护
- 行为由父类控制,子类实现
缺点:
- 每一个不同的实现都需要一个子类,导致类的个数增加
- 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构
访问者模式
定义:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
实际应用:文档对象模型(DOM)、编译器设计、复杂对象结构的操作等。
代码示例:公司员工薪资报表系统
// 访问者接口
public interface Visitor {void visit(Manager manager);void visit(Developer developer);void visit(Designer designer);
}// 元素接口
public interface Employee {void accept(Visitor visitor);String getName();double getSalary();int getWorkingDays();
}// 具体元素:经理
public class Manager implements Employee {private String name;private double salary;private int workingDays;private int teamSize;public Manager(String name, double salary, int workingDays, int teamSize) {this.name = name;this.salary = salary;this.workingDays = workingDays;this.teamSize = teamSize;}@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}@Overridepublic String getName() {return name;}@Overridepublic double getSalary() {return salary;}@Overridepublic int getWorkingDays() {return workingDays;}public int getTeamSize() {return teamSize;}
}// 具体元素:开发人员
public class Developer implements Employee {private String name;private double salary;private int workingDays;private String programmingLanguage;public Developer(String name, double salary, int workingDays, String programmingLanguage) {this.name = name;this.salary = salary;this.workingDays = workingDays;this.programmingLanguage = programmingLanguage;}@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}@Overridepublic String getName() {return name;}@Overridepublic double getSalary() {return salary;}@Overridepublic int getWorkingDays() {return workingDays;}public String getProgrammingLanguage() {return programmingLanguage;}
}// 具体元素:设计师
public class Designer implements Employee {private String name;private double salary;private int workingDays;private String designTool;public Designer(String name, double salary, int workingDays, String designTool) {this.name = name;this.salary = salary;this.workingDays = workingDays;this.designTool = designTool;}@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}@Overridepublic String getName() {return name;}@Overridepublic double getSalary() {return salary;}@Overridepublic int getWorkingDays() {return workingDays;}public String getDesignTool() {return designTool;}
}// 具体访问者:薪资报表生成器
public class SalaryReportVisitor implements Visitor {private double totalSalary = 0;private Map<String, Double> departmentSalaries = new HashMap<>();@Overridepublic void visit(Manager manager) {double salary = manager.getSalary();// 经理有管理团队的额外奖金double bonus = 1000 * manager.getTeamSize();double totalSalary = salary + bonus;System.out.println("经理 " + manager.getName() + " 的薪资: " + salary + " + 团队奖金: " + bonus + " = " + totalSalary);this.totalSalary += totalSalary;departmentSalaries.put("管理部门", departmentSalaries.getOrDefault("管理部门", 0.0) + totalSalary);}@Overridepublic void visit(Developer developer) {double salary = developer.getSalary();// 开发人员有技术奖金double bonus = 0;String language = developer.getProgrammingLanguage();if ("Java".equalsIgnoreCase(language)) {bonus = 1000;} else if ("Python".equalsIgnoreCase(language)) {bonus = 800;} else if ("C++".equalsIgnoreCase(language)) {bonus = 1200;}double totalSalary = salary + bonus;System.out.println("开发人员 " + developer.getName() + " 的薪资: " + salary + " + " + language + " 技术奖金: " + bonus + " = " + totalSalary);this.totalSalary += totalSalary;departmentSalaries.put("开发部门", departmentSalaries.getOrDefault("开发部门", 0.0) + totalSalary);}@Overridepublic void visit(Designer designer) {double salary = designer.getSalary();// 设计师有创意奖金double bonus = 0;String tool = designer.getDesignTool();if ("Photoshop".equalsIgnoreCase(tool)) {bonus = 800;} else if ("Sketch".equalsIgnoreCase(tool)) {bonus = 1000;} else if ("Figma".equalsIgnoreCase(tool)) {bonus = 900;}double totalSalary = salary + bonus;System.out.println("设计师 " + designer.getName() + " 的薪资: " + salary + " + " + tool + " 创意奖金: " + bonus + " = " + totalSalary);this.totalSalary += totalSalary;departmentSalaries.put("设计部门", departmentSalaries.getOrDefault("设计部门", 0.0) + totalSalary);}public void printReport() {System.out.println("\n===== 薪资报表摘要 =====");System.out.println("总薪资支出: " + totalSalary);System.out.println("\n各部门薪资:");for (Map.Entry<String, Double> entry : departmentSalaries.entrySet()) {System.out.println(entry.getKey() + ": " + entry.getValue());}}
}// 具体访问者:考勤报表生成器
public class AttendanceReportVisitor implements Visitor {private int totalWorkingDays = 0;private int totalEmployees = 0;private Map<String, Integer> departmentAttendance = new HashMap<>();private Map<String, Integer> departmentEmployees = new HashMap<>();@Overridepublic void visit(Manager manager) {int workingDays = manager.getWorkingDays();System.out.println("经理 " + manager.getName() + " 的工作天数: " + workingDays);totalWorkingDays += workingDays;totalEmployees++;departmentAttendance.put("管理部门", departmentAttendance.getOrDefault("管理部门", 0) + workingDays);departmentEmployees.put("管理部门", departmentEmployees.getOrDefault("管理部门", 0) + 1);}@Overridepublic void visit(Developer developer) {int workingDays = developer.getWorkingDays();System.out.println("开发人员 " + developer.getName() + " 的工作天数: " + workingDays);totalWorkingDays += workingDays;totalEmployees++;departmentAttendance.put("开发部门", departmentAttendance.getOrDefault("开发部门", 0) + workingDays);departmentEmployees.put("开发部门", departmentEmployees.getOrDefault("开发部门", 0) + 1);}@Overridepublic void visit(Designer designer) {int workingDays = designer.getWorkingDays();System.out.println("设计师 " + designer.getName() + " 的工作天数: " + workingDays);totalWorkingDays += workingDays;totalEmployees++;departmentAttendance.put("设计部门", departmentAttendance.getOrDefault("设计部门", 0) + workingDays);departmentEmployees.put("设计部门", departmentEmployees.getOrDefault("设计部门", 0) + 1);}public void printReport() {System.out.println("\n===== 考勤报表摘要 =====");System.out.println("员工总数: " + totalEmployees);System.out.println("总工作天数: " + totalWorkingDays);System.out.println("平均出勤率: " + (totalWorkingDays / (totalEmployees * 22.0) * 100) + "%");System.out.println("\n各部门考勤:");for (String department : departmentAttendance.keySet()) {int attendance = departmentAttendance.get(department);int employees = departmentEmployees.get(department);double rate = attendance / (employees * 22.0) * 100;System.out.println(department + ": " + attendance + " 天 / " + employees + " 人 = " + rate + "% 出勤率");}}
}// 对象结构
public class Company {private List<Employee> employees = new ArrayList<>();public void addEmployee(Employee employee) {employees.add(employee);}public void removeEmployee(Employee employee) {employees.remove(employee);}public void accept(Visitor visitor) {for (Employee employee : employees) {employee.accept(visitor);}}
}// 客户端代码
public class CompanyReportSystem {public static void main(String[] args) {// 创建公司结构Company company = new Company();// 添加员工company.addEmployee(new Manager("张三", 20000, 20, 5));company.addEmployee(new Manager("李四", 25000, 22, 3));company.addEmployee(new Developer("王五", 15000, 21, "Java"));company.addEmployee(new Developer("赵六", 16000, 18, "Python"));company.addEmployee(new Developer("钱七", 17000, 22, "C++"));company.addEmployee(new Designer("孙八", 14000, 19, "Photoshop"));company.addEmployee(new Designer("周九", 15000, 21, "Sketch"));// 生成薪资报表System.out.println("===== 生成薪资报表 =====");SalaryReportVisitor salaryVisitor = new SalaryReportVisitor();company.accept(salaryVisitor);salaryVisitor.printReport();// 生成考勤报表System.out.println("\n\n===== 生成考勤报表 =====");AttendanceReportVisitor attendanceVisitor = new AttendanceReportVisitor();company.accept(attendanceVisitor);attendanceVisitor.printReport();}
}
优点:
- 符合单一职责原则,让程序具有优秀的扩展性
- 增加新的访问操作很方便
- 将数据结构与数据操作分离
缺点:
- 具体元素对访问者公布细节,违反了迪米特原则
- 违反了依赖倒置原则,依赖了具体类,没有依赖抽象
- 具体元素变更比较困难
总结与最佳实践
在本文中,我们详细介绍了 23 种经典的 GoF(Gang of Four)设计模式在 Java 中的实现和应用。这些设计模式按照其目的可以分为三类:创建型模式、结构型模式和行为型模式。
设计模式分类总结
创建型模式(5种):
- 单例模式(Singleton):确保一个类只有一个实例,并提供全局访问点。
- 工厂方法模式(Factory Method):定义一个创建对象的接口,让子类决定实例化哪一个类。
- 抽象工厂模式(Abstract Factory):提供一个创建一系列相关或依赖对象的接口,而无需指定它们的具体类。
- 建造者模式(Builder):将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。
- 原型模式(Prototype):用原型实例指定创建对象的种类,并通过复制这些原型创建新对象。
结构型模式(7种):
- 适配器模式(Adapter):将一个类的接口转换成客户希望的另一个接口。
- 桥接模式(Bridge):将抽象部分与实现部分分离,使它们都可以独立变化。
- 组合模式(Composite):将对象组合成树形结构以表示"部分-整体"的层次结构。
- 装饰器模式(Decorator):动态地给一个对象添加一些额外的职责。
- 外观模式(Facade):为子系统中的一组接口提供一个一致的界面。
- 享元模式(Flyweight):运用共享技术有效地支持大量细粒度的对象。
- 代理模式(Proxy):为其他对象提供一种代理以控制对这个对象的访问。
行为型模式(11种):
- 责任链模式(Chain of Responsibility):为请求创建一个接收者对象的链。
- 命令模式(Command):将一个请求封装为一个对象,使得可以用不同的请求对客户进行参数化。
- 解释器模式(Interpreter):给定一个语言,定义它的文法表示,并定义一个解释器。
- 迭代器模式(Iterator):提供一种方法顺序访问一个聚合对象中的各个元素。
- 中介者模式(Mediator):用一个中介对象来封装一系列的对象交互。
- 备忘录模式(Memento):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
- 观察者模式(Observer):定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
- 状态模式(State):允许一个对象在其内部状态改变时改变它的行为。
- 策略模式(Strategy):定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。
- 模板方法模式(Template Method):定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。
- 访问者模式(Visitor):表示一个作用于某对象结构中的各元素的操作。
设计模式的选择与应用
在实际项目中选择和应用设计模式时,应该遵循以下原则:
-
问题驱动:不要为了使用设计模式而使用设计模式。应该先识别问题,然后选择合适的模式来解决问题。
-
简单优先:如果一个简单的解决方案能够满足需求,就不要使用复杂的设计模式。过度设计会增加系统的复杂性。
-
组合使用:设计模式通常不是孤立使用的,多种模式可以组合使用以解决复杂问题。
-
权衡取舍:每种模式都有其优缺点,需要根据具体情况进行权衡。
-
持续重构:随着项目的发展,可能需要重构代码以引入或改变设计模式。
常见设计模式的应用场景
以下是一些常见设计模式在实际 Java 项目中的应用场景:
-
单例模式:数据库连接池、线程池、缓存、日志对象等全局唯一的资源管理。
-
工厂方法和抽象工厂:JDBC 连接不同数据库、创建不同主题的 UI 组件、支持多种格式的文档解析器等。
-
建造者模式:构建复杂对象,如
StringBuilder
、DocumentBuilder
、ORM 框架中的查询构建器等。 -
适配器模式:旧接口适配新接口、第三方库集成、不同数据格式的转换等。
-
装饰器模式:Java I/O 流类库、动态添加功能如日志、事务、缓存等。
-
观察者模式:事件处理系统、MVC 架构中的视图更新、消息订阅发布系统等。
-
策略模式:算法策略选择、支付方式选择、不同的验证规则等。
-
命令模式:任务队列、操作的撤销/重做功能、事务操作等。
-
代理模式:Spring AOP、远程调用(RMI)、延迟加载、访问控制等。
-
模板方法模式:框架中的钩子方法、固定流程的业务逻辑实现等。
设计模式的误区与注意事项
在使用设计模式时,需要避免以下常见误区:
-
过度设计:不要为了使用设计模式而设计,应该根据实际需求选择合适的模式。
-
生搬硬套:不要强行应用不适合的设计模式,这会使代码更加复杂和难以维护。
-
忽略性能影响:某些设计模式可能会对性能产生影响,需要在灵活性和性能之间找到平衡。
-
忽略上下文:设计模式的应用应该考虑项目的具体上下文,包括团队经验、项目规模、维护周期等。
-
缺乏文档:使用设计模式时,应该在代码注释或文档中说明所使用的模式及其理由,以便其他开发人员理解。
结语
设计模式是软件开发中的重要工具,能够帮助我们构建更加灵活、可维护和可扩展的系统。通过学习和应用这些经典的设计模式,我们可以站在巨人的肩膀上,避免重复发明轮子,更高效地解决常见的设计问题。
然而,设计模式并不是银弹,它们是经验的总结,而非教条。在实际应用中,我们需要根据具体问题和场景灵活选择和调整,甚至创造新的模式。真正掌握设计模式的核心在于理解其背后的设计原则和思想,而不仅仅是模式本身的结构和实现。