设计模式 行为型
行为型模式
- 本文作者: MrBird
- 本文链接: http://mrbird.cc/Java设计模式.html
- 本文仅为个人学习整理转载
模板方法模式
模板方法模式定义了一个流程的骨架,由多个方法组成。并允许子类为一个或多个步骤提供实现。简而言之就是公共的不变的部分由父类统一实现,变化的部分由子类来个性化实现。
优点:
- 提高复用性;
- 提高拓展性;
- 符合开闭原则。
缺点:
- 类的数目增加;
- 增加了系统实现的复杂度;
- 父类添加新的抽象方法,所有子类都要改一遍。
举个模板方法模式的例子。定义一个外卖的接口,包含下单、制作和打包配送三个步骤:
public abstract class Takeaway {
final void order() {
System.out.println("下单");
}
final void packageSend() {
System.out.println("打包派送");
}
protected abstract void make();
protected boolean needTableware() {
return true;
}
final void flow() {
this.order();
this.make();
if (needTableware()) {
System.out.println("赠送一次性餐具");
}
this.packageSend();
}
}
其中下单和打包配送行为是固定的,不同的是制作过程,所以order
和packageSend
方法提供了默认实现,并且由final修饰,子类不可重写。此外,我们还可以通过needTableware
这个钩子方法来控制某些子类的定制化需求。
新增BarbecueTakeaway继承Takeaway:
public class BarbecueTakeaway extends Takeaway {
private final boolean needTableware;
public BarbecueTakeaway(boolean needTableware) {
this.needTableware = needTableware;
}
@Override
protected void make() {
System.out.println("制作烤肉");
}
@Override
protected boolean needTableware() {
return this.needTableware;
}
}
新增FruitTakeaway继承Takeaway:
public class FruitTakeaway extends Takeaway {
@Override
protected void make() {
System.out.println("水果配货");
}
@Override
protected boolean needTableware() {
return false;
}
}
新增个客户端Application测试:
public class Application {
public static void main(String[] args) {
Takeaway barbecue = new BarbecueTakeaway(true);
barbecue.flow();
FruitTakeaway fruit = new FruitTakeaway();
fruit.flow();
}
}
下单
制作烤肉
赠送一次性餐具
打包派送
下单
水果配货
打包派送
UML:
迭代器模式
ignore
策略模式
策略模式定义了算法家族,分别封装起来,让它们之间可以互相替换。此模式让算法的变化不会影响到使用算法的用户。策略模式常用于消除大量的if else代码。
适用场景:
- 系统有很多类,它们的区别仅仅在于行为不同;
- 一个系统需要动态地在几种算法中选择一种;
举个策略模式的例子(促销活动),定义一个促销策略接口:
public interface PromotionStrategy {
void promotion();
}
实现类之一(策略之一),满减促销策略:
public class FullReductionPromotionStrategy implements PromotionStrategy {
public void promotion() {
System.out.println("满1000立减1");
}
}
实现类之一(策略之一),打折促销策略:
public class DiscountPromotionStrategy implements PromotionStrategy {
public void promotion() {
System.out.println("9.9折钜惠");
}
}
创建一个客户端测试:
public class Application {
public static void main(String[] args) {
// 模拟客户端传递的促销策略key
String promotionKey = "fr";
PromotionStrategy strategy;
if ("fr".equals(promotionKey)) {
strategy = new FullReductionPromotionStrategy();
} else if ("ds".equals(promotionKey)) {
strategy = new DiscountPromotionStrategy();
} else {
throw new RuntimeException("暂不支持该促销活动");
}
strategy.promotion();
}
}
输出结果:
满1000立减1
策略模式常结合工厂模式来消除大量的if else代码,我们新建一个促销策略的创建工厂:
public class PromotionStrategyFactory {
private static final Map<String, PromotionStrategy> PROMOTION_STRATEGY_MAP = new HashMap<>();
private static final PromotionStrategy NON_PROMOTION = () -> System.out.println("无促销活动");
static {
PROMOTION_STRATEGY_MAP.put(PromotionKey.FR, new FullReductionPromotionStrategy());
PROMOTION_STRATEGY_MAP.put(PromotionKey.DS, new DiscountPromotionStrategy());
}
private PromotionStrategyFactory() {
}
public static PromotionStrategy getPromotionStrategy(String promotionKey) {
PromotionStrategy strategy = PROMOTION_STRATEGY_MAP.get(promotionKey);
return strategy == null ? NON_PROMOTION : strategy;
}
private interface PromotionKey {
String FR = "fr";
String DS = "ds";
}
}
上面代码中,我们通过Map来装载促销策略,这样可以减少对象的重复创建。如果不希望在static块中一次性初始化所有促销策略,我们可以结合享元模式来推迟对象创建过程。
通过这个工厂方法,上面客户端代码可以简写为:
public class Application {
public static void main(String[] args) {
// 模拟客户端传递的促销策略key
String promotionKey = "fr";
PromotionStrategy promotionStrategy = PromotionStrategyFactory.getPromotionStrategy(promotionKey);
promotionStrategy.promotion();
}
}
解释器模式
用的较少,暂不记录。
观察者模式
观察者模式定义了对象之间的一对多依赖,让多个观察者同时监听某个主题对象,当主体对象发生变化时,它的所有观察者都会收到响应的通知。
优点:
- 观察者和被观察者之间建立一个抽象的耦合;
- 观察者模式支持广播通信。
缺点:
- 观察者之间有过多的细节依赖,提高时间消耗及程序复杂度;
- 应避免循环调用。
JDK对观察者模式提供了支持。下面举个观察者模式的例子。
创建一个博客类:
/**
* 继承 Observable类,Blog为被观察对象
*/
public class Blog extends Observable {
private String title;
public Blog(String title) {
this.title = title;
}
public String getTitle() {
return title;
}
public void comment(Comment comment) {
System.out.println(comment.getNickname() + "评论了<" + this.title + "> ," +
"评论内容:" + comment.getValue());
// 设置标识位 changed = true,表示被观察者发生了改变
setChanged();
// 通知观察者,可以给观察者传递数据
notifyObservers(comment);
}
}
Comment类代码:
public class Comment {
/**
* 评论者昵称
*/
private String nickname;
/**
* 评论内容
*/
private String value;
public Comment(String nickname, String value) {
this.nickname = nickname;
this.value = value;
}
public String getValue() {
return value;
}
public String getNickname() {
return nickname;
}
}
Blog类是被观察者对象,被观察者对象需要继承JDK的Observable类,继承后,被观察者对象包含如下属性和方法:
这些方法都是线程安全方法(加了synchronized同步锁)。
Blog的comment方法中,当博客收到评论时,首先调用父类的setChanged()方法,设置标识位 changed = true,表示被观察者发生了改变;然后调用父类的notifyObservers(Object)方法通知所有观察者。
被观察者对象创建好后,我们接着创建观察者。新建一个Author类:
public class Author implements Observer {
private String name;
public Author(String name) {
this.name = name;
}
/**
* 观察者被通知后,就会调用这个方法
*
* @param o 被观察者对象
* @param arg 被观察者传递过来的数据
*/
@Override
public void update(Observable o, Object arg) {
Blog blog = (Blog) o;
Comment comment = (Comment) arg;
System.out.println("系统感知到" + this.name + "撰写的博文<" +
blog.getTitle() + ">收到了" + comment.getNickname() +
"的评论,评论内容为:" + comment.getValue());
}
}
观察者对象需要实现JDK的Observer类,重写update方法。当被观察者对象调用了notifyObservers方法后,相应的观察者的update方法会被调用。
新建一个客户端测试一下:
public class Application {
public static void main(String[] args) {
Blog blog = new Blog("Java从入门到放弃");
Author author = new Author("MrBird");
// 添加观察者
blog.addObserver(author);
Comment comment = new Comment("Scott",
"感谢楼主的文章,让我及时放弃Java,回家继承了千万家产。");
blog.comment(comment);
}
}
程序输出如下:
Scott评论了<Java从入门到放弃> ,评论内容:感谢楼主的文章,让我及时放弃Java,回家继承了千万家产。
系统感知到MrBird撰写的博文<Java从入门到放弃>收到了Scott的评论,评论内容为:感谢楼主的文章,让我及时放弃Java,回家继承了千万家产。
值得注意的是,观察者的update方法里的逻辑最好进行异步化,这样在并发环境下可以提升程序性能。
备忘录模式
参考:https://www.cnblogs.com/jimoer/p/9537997.html。
命令模式
暂不记录。
中介者模式
暂不记录。
职责链模式
职责链模式为请求创建一个接收此次请求对象的链。
适用于:
- 一个请求的处理需要多个对象当中的一个或几个协作处理;
优点:
- 请求的发送者和接受者(请求的处理)解耦;
- 职责链可以动态的组合。
缺点:
- 职责链太长或者处理时间过长,影响性能;
- 职责链可能过多。
举个字符串校验的例子。新建一个字符串校验抽象类:
public abstract class StringValidator {
protected StringValidator validator;
public void setNextValidator(StringValidator validator) {
this.validator = validator;
}
public abstract void check(String value);
}
StringValidator类包含了一个自身类型的成员变量,这也是该模式的设计核心,以此形成链条。
创建一个校验字符串长度的类StringLengthValidator:
public class StringLengthValidator extends StringValidator {
@Override
public void check(String value) {
if (value != null && value.length() != 0) {
System.out.println("字符串长度合法");
if (validator != null) {
validator.check(value);
}
} else {
System.out.println("字符串长度不合法");
}
}
}
上面代码中,在字符串长度校验合法后,我们判断父类的validator属性是否为空,不为空则调用其check方法继续下一步校验。
接着再新建一个校验字符串内容的类StringValueValidator:
public class StringValueValidator extends StringValidator {
@Override
public void check(String value) {
if (value.contains("fuck")) {
System.out.println("字符串值不合法");
if (validator != null) {
validator.check(value);
}
} else {
System.out.println("字符串值合法");
}
}
}
套路和StringLengthValidator一样。接着创建一个客户端类,演示下如何让校验类形成一个链条:
public class Application {
public static void main(String[] args) {
StringValidator lengthValidator = new StringLengthValidator();
StringValidator valueValidator = new StringValueValidator();
lengthValidator.setNextValidator(valueValidator);
lengthValidator.check("hello");
}
}
上面代码中,通过StringValidator的setNextValidator方法,我们可以指定下一个校验类,以此形成链条,程序输出如下:
字符串长度合法
字符串值合法
访问者模式
暂不记录🌚
状态模式
暂不记录🌚
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律