🌈 开篇:设计模式就像乐高说明书
想象你要组装乐高城堡,行为型模式就是教你如何让各个积木块(对象)默契配合的说明书!
行为型模式
主要用于描述对象之间的通信和协作方式,包括算法、责任链、状态等方面。这些模式可以帮助我们更好地组织和管理代码,提高代码的可维护性和可扩展性。
1. 命令模式 Command Pattern:万能遥控器的秘密
命令模式将一个请求封装为一个对象,从而可以用不同的请求对客户端进行参数化。它适用于请求排队、日志记录以及支持撤销操作等场景。
使用场景
- 解耦请求的发送者和接收者。
- 支持命令的撤销(undo)操作。
- 记录请求的历史记录。
- 将简单操作组合成复杂操作。
代码实现
🎮 假设有一个电视机,它具有开机、关机和切换频道等操作
定义命令接口和具体命令类
public interface Command {
void execute();
}
public class TvOnCommand implements Command {
private TV tv;
public TvOnCommand(TV tv) {
this.tv = tv;
}
public void execute() {
tv.on();
}
}
public class TvOffCommand implements Command {
private TV tv;
public TvOffCommand(TV tv) {
this.tv = tv;
}
public void execute() {
tv.off();
}
}
public class TvChangeChannelCommand implements Command {
private TV tv;
private int channel;
public TvChangeChannelCommand(TV tv, int channel) {
this.tv = tv;
this.channel = channel;
}
public void execute() {
tv.changeChannel(channel);
}
}
定义接收者类
public class TV {
public void on() {
System.out.println("TV is on");
}
public void off() {
System.out.println("TV is off");
}
public void changeChannel(int channel) {
System.out.println("TV channel is changed to " + channel);
}
}
定义调用者类
public class RemoteController {
private Command onCommand;
private Command offCommand;
private Command changeChannelCommand;
public RemoteController(Command onCommand, Command offCommand, Command changeChannelCommand) {
this.onCommand = onCommand;
this.offCommand = offCommand;
this.changeChannelCommand = changeChannelCommand;
}
public void turnOn() {
onCommand.execute();
}
public void turnOff() {
offCommand.execute();
}
public void changeChannel() {
changeChannelCommand.execute();
}
}
使用命令模式
TV tv = new TV();
Command onCommand = new TvOnCommand(tv);
Command offCommand = new TvOffCommand(tv);
Command changeChannelCommand = new TvChangeChannelCommand(tv, 2);
RemoteController remoteController = new RemoteController(onCommand, offCommand, changeChannelCommand);
remoteController.turnOn(); // 输出 "TV is on"
remoteController.turnOff(); // 输出 "TV is off"
Command changeChannelCommand2 = new TvChangeChannelCommand(tv, 5);
RemoteController remoteController2 = new RemoteController(onCommand, offCommand, changeChannelCommand2);
remoteController2.changeChannel(); // 输出 "TV channel is changed to 5"
上述代码中,RemoteController 充当了调用者角色,TvOnCommand、TvOffCommand 和 TvChangeChannelCommand 充当了具体命令角色,而 TV 充当了接收者角色。
当 RemoteController 调用某个命令的 execute() 方法时,具体命令将通过接收者TV来执行相应的操作。通过这种方式,调用者和接收者之间的耦合得以解除。
使用小结
- 可以使用命令模式来实现撤销和恢复操作。例如,在编辑器中,可以使用命令模式来实现对文本的撤销和恢复操作。
- 可以使用命令模式来实现日志记录。例如,在Web应用中,可以将每个请求封装成一个命令对象,并将命令对象记录到日志文件中。
- 可以使用命令模式来实现消息队列。例如,在JMS(Java消息服务)中,可以将每个消息封装成一个命令对象,并将命令对象加入到消息队列中。
2. 责任链模式:快递层层转包记
责任链模式通过将请求的处理对象串成一条链,使请求能够沿着这条链传递,直到有一个对象处理它。这种方式解耦了请求的发送者和接收者。
🔗 快递小哥的日常:
你的包裹从分拣中心→省站→市站→快递员,每个环节只处理自己能派送的范围,这就是责任链!
代码实现
// 处理者接口:快递节点
interface DeliveryHandler {
void handle(Package pkg);
}
// 具体处理者:市级站点
class CityStation implements DeliveryHandler {
private DeliveryHandler next;
public void handle(Package pkg) {
if (pkg.getCity().equals("北京")) {
System.out.println("北京站派送中...");
} else {
next.handle(pkg); // 转给下一站
}
}
}
模式亮点:
🔗 动态增减处理节点
🔗 发送者无需知道最终处理者
3. 观察者模式 Observer Pattern:微信订阅号通知
观察者模式定义了一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。
应用场景
1.事件处理机制:Java中的Swing GUI框架就是基于观察者模式实现的,当用户与组件交互时,组件会向注册的监听器发送事件通知,以触发相应的事件处理方法。
2.日志记录:Java中的日志系统也是基于观察者模式实现的,当日志发生变化时,它会通知所有注册的观察者,例如文件输出流、控制台输出流等,从而实现日志的输出和记录。
3.用户界面设计:在Java中,用户界面设计中的许多元素都可以使用观察者模式实现,例如菜单项、按钮、文本框等,当用户与这些元素交互时,它们会向注册的监听器发送事件通知,以触发相应的事件处理方法。
4.多线程编程:在Java中,观察者模式还可以用于多线程编程中,当一个线程发生了某些变化时,它可以向其他线程发送通知,以实现线程间的协作和同步。
👀代码实现:你关注的博主发文→所有粉丝收到推送。观察者模式就是订阅-通知机制!
// 主题(博主)
class WechatOfficial {
private List<Follower> followers = new ArrayList<>();
public void postArticle(String title) {
System.out.println("发布新文章:" + title);
notifyFollowers(title);
}
private void notifyFollowers(String title) {
for (Follower f : followers) {
f.update(title);
}
}
}
// 观察者(粉丝)
class Follower {
public void update(String msg) {
System.out.println("收到通知:" + msg);
}
}
4.状态模式 State Pattern:订单的不同状态
状态模式允许一个对象在其内部状态发生改变时改变其行为。通过将状态封装为对象,使得对象的行为依赖于状态,而不是对象本身。
使用场景
- 对象的行为依赖于状态,并且状态可能在运行时发生改变时。
- 代码中包含大量与状态相关的条件语句时。
🎮代码实现
订单对象,订单状态包括 “新建”、“处理中” 和 “完成” 三种状态,订单状态会随着订单处理的不同而改变。
首先,我们需要定义订单状态的接口:
public interface OrderState {
void processOrder(Order order);
}
然后,我们定义订单状态的具体实现类,分别对应三种不同的状态
public class NewOrder implements OrderState {
public void processOrder(Order order) {
// 处理新建状态下的订单
System.out.println("Processing new order.");
order.setState(new ProcessingOrder());
}
}
public class ProcessingOrder implements OrderState {
public void processOrder(Order order) {
// 处理处理中状态下的订单
System.out.println("Processing order in progress.");
order.setState(new CompletedOrder());
}
}
public class CompletedOrder implements OrderState {
public void processOrder(Order order) {
// 处理完成状态下的订单
System.out.println("Processing completed order.");
}
}
最后,我们定义订单对象,并在订单对象中实现状态的切换:
public class Order {
private OrderState state;
public Order() {
state = new NewOrder();
}
public void setState(OrderState state) {
this.state = state;
}
public void processOrder() {
state.processOrder(this);
}
}
使用上面定义的订单对象和订单状态对象来处理订单了:
Order order = new Order();
order.processOrder(); // Output: Processing new order.
order.processOrder(); // Output: Processing order in progress.
order.processOrder(); // Output: Processing completed order.
优势总结
🔄 状态切换不影响外部调用
🚫 避免大量if-else判断
5. 策略模式 Strategy Pattern:营销方式任你选
策略模式定义了一系列算法,将每个算法封装起来,使它们可以互换,客户端不需要关心算法的实现。
使用场景
- 对象具有多种行为或算法时。
- 需要减少大量的 if/else 语句时。
代码实现
假设有一个电商网站,它需要根据不同的促销策略来计算订单的价格。促销策略包括打折、满减、直降等等。
首先,我们定义一个促销策略接口,其中包含一个计算订单价格的方法
public interface PromotionStrategy {
double calculatePrice(double price);
}
然后,我们实现具体的促销策略,例如打折、满减和直降策略:
public class DiscountPromotionStrategy implements PromotionStrategy {
private double discount;
public DiscountPromotionStrategy(double discount) {
this.discount = discount;
}
public double calculatePrice(double price) {
return price * (1 - discount);
}
}
public class FullReductionPromotionStrategy implements PromotionStrategy {
private double threshold;
private double reduction;
public FullReductionPromotionStrategy(double threshold, double reduction) {
this.threshold = threshold;
this.reduction = reduction;
}
public double calculatePrice(double price) {
return price >= threshold ? price - reduction : price;
}
}
public class DirectReductionPromotionStrategy implements PromotionStrategy {
private double reduction;
public DirectReductionPromotionStrategy(double reduction) {
this.reduction = reduction;
}
public double calculatePrice(double price) {
return price - reduction;
}
}
最后,我们定义一个订单类,其中包含一个 PromotionStrategy 对象和一个 calculatePrice 方法:
public class Order {
private double price;
private PromotionStrategy promotionStrategy;
public Order(double price, PromotionStrategy promotionStrategy) {
this.price = price;
this.promotionStrategy = promotionStrategy;
}
public double calculatePrice() {
return promotionStrategy.calculatePrice(price);
}
}
创建一个订单,并指定不同的促销策略来计算订单价格:
Order order = new Order(100, new DiscountPromotionStrategy(0.1));
double price = order.calculatePrice(); // 90
order = new Order(200, new FullReductionPromotionStrategy(150, 50));
price = order.calculatePrice(); // 150
order = new Order(300, new DirectReductionPromotionStrategy(50));
price = order.calculatePrice(); // 250
使用小结
策略模式是 Java 中经常使用的一种设计模式,它可以在很多场景中使用,比如:
1.可以使用策略模式定义多种日志记录方式(例如控制台输出、文件输出、网络传输等)并动态选择使用哪种方式进行日志记录。
2.可以使用策略模式定义多种支付方式(例如微信支付、支付宝支付、银行卡支付等)并让用户动态选择使用哪种支付方式进行支付。
3.可以使用策略模式定义多种加密算法(例如对称加密、非对称加密、哈希算法等)并让用户动态选择使用哪种加密算法进行数据加密。
通过使用策略模式,我们可以更灵活地实现不同的功能需求,并让用户根据实际情况选择最合适的策略进行操作
6. 模板方法模式 Template Method Pattern:奶茶店标准化配方
模板方法模式定义了算法的骨架,将某些步骤延迟到子类中。子类可以在不改变算法结构的情况下重新定义某些步骤。
使用场景:
- 功能部分实现确定,另一部分不确定时使用。(可以把不确定的部分暴露出去,让子类去实现。
换句话说,在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式。
模板方法设计模式是编程中经常用得到的模式。各个框架、类库中都有他的影子,比如常见的有:
- 数据库访问的封装
- Junit 单元测试
- JavaWeb 的 Servlet 中关于 doGet/doPost 方法调用
- Hibernate 中模板程序
- Spring 中 JDBCTemlate、HibernateTemplate 等
📝 代码示例:
网红奶茶制作流程
煮茶→加料→封口→贴标签,固定流程中允许自定义加料!
// 抽象模板
abstract class MilkTeaTemplate {
// 模板方法(final防止篡改)
public final void makeTea() {
brewTea();
addTopping();
seal();
System.out.println("✅ 制作完成!");
}
// 抽象方法:加料由子类实现
protected abstract void addTopping();
// 通用步骤
private void brewTea() {
System.out.println("🫖 煮茶中...");
}
private void seal() {
System.out.println("📦 封口处理");
}
}
// 具体实现:珍珠奶茶
class BubbleTea extends MilkTeaTemplate {
protected void addTopping() {
System.out.println("➕ 加珍珠");
}
}
注意:模板方法通常被申明为final,以防止子类对其进行重写。
以下模式不太常用
过滤器设计模式
允许在不改变原始对象的情况下,动态地添加或删除对象的行为。
解释器模式
给定一个语言,定义它的文法的一种表示,并定义一个解释器,使用该解释器来解释语言中的句子,
迭代器模式
提供一种方法顺序访问一个聚合对象中各个元素,而不需要暴露该对象的内部表示。
中介者模式
用一个中介对象封装一系列的对象交互,使得这些对象不需要显示地相互引用,从而降低耦合度
备忘录模式
在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
实际案例-组合使用
在实际开发中,我们很少只使用单一的设计模式来解决问题,而是将多种设计模式混合使用,以达到更好的效果。
工厂模式 + 单例模式
使用工厂模式来创建对象,通过单例模式来保证该工厂只有一个实例,从而减少创建对象时的开销。
首先,创建一个工厂类,该类使用单例模式来保证只有一个实例,该实例负责创建对象。然后,根据需要创建多个工厂方法,每个方法用于创建不同的对象。
public class SingletonFactory {
private static volatile SingletonFactory instance;
private SingletonFactory() {
// 私有构造方法
}
public static SingletonFactory getInstance() {
if (instance == null) {
synchronized (SingletonFactory.class) {
if (instance == null) {
instance = new SingletonFactory();
}
}
}
return instance;
}
public Object createObject(String type) {
if ("type1".equals(type)) {
return new Type1();
} else if ("type2".equals(type)) {
return new Type2();
} else {
throw new IllegalArgumentException("Unsupported type: " + type);
}
}
}
public class Type1 {
// 类型1实现逻辑
}
public class Type2 {
// 类型2实现逻辑
}
SingletonFactory 类使用双重检查锁定实现了单例模式,同时提供了一个 createObject() 方法,该方法根据输入的参数来创建不同的对象。Type1 和 Type2 类分别代表了不同类型的对象,它们包含了各自的实现逻辑。