第一句子网 - 唯美句子、句子迷、好句子大全
第一句子网 > 行为型模式(Behavioral Patterns)

行为型模式(Behavioral Patterns)

时间:2021-03-26 14:58:50

相关推荐

行为型模式(Behavioral Patterns)

行为型模式(Behavioral Patterns)

1、责任链模式(Chain of Responsibility Pattern)

意图

用于将多个处理器对象组成一个链,按照一定的顺序依次处理请求,直到有一个处理器能够处理请求为止。这种模式可以实现请求的发送者和接收者之间的解耦,使得多个处理器可以灵活组合,适用于需要按照一定条件进行处理的场景。

程序实例

现实世界的例子

兽人王向他的军队发出响亮的命令。最先接受到命令的是指挥官,然后是军官,然后是士兵。指挥官、军官和士兵形成责任链。

用简单的话来说

它有助于构建对象链。请求从一端进入,并不断从一个对象到达另一端,直到找到合适的处理程序。

维基百科说

在面向对象设计中,责任链模式是由命令对象源和一系列处理对象组成的设计模式。每个处理对象都包含定义它可以处理的命令对象类型的逻辑;其余的被传递到链中的下一个处理对象。

用上面的兽人例子来实现我们的责任链,首先我们需要有一个Request 类

/*** @author xs*/public class Request {/*** 此请求的类型,由链中的每个项目使用,以查看它们是否应该或可以处理此特定请求*/private final RequestType requestType;/*** 请求的描述*/private final String requestDescription;/*** 指示请求是否被处理。请求只能将状态从未处理切换到已处理,无法“取消处理”请求*/private boolean handled;/*** 创建给定类型和随附描述的新请求** @param requestType 请求类型* @param requestDescription 请求的描述*/public Request(final RequestType requestType, final String requestDescription) {this.requestType = Objects.requireNonNull(requestType);this.requestDescription = Objects.requireNonNull(requestDescription);}/*** 获取请求的描述** @return 可读的请求描述*/public String getRequestDescription() {return requestDescription;}/*** 获取此请求的类型,指挥链中的每个人都使用该类型来查看他们是否应该或可以处理此特定请求** @return The request type*/public RequestType getRequestType() {return requestType;}/*** 将请求标记为已处理*/public void markHandled() {this.handled = true;}/*** 指示该请求是否被处理** @return <tt>true</tt> 请求被处理, <tt>false</tt> 请求没有处理*/public boolean isHandled() {return this.handled;}}

然后在创建一个请求处理程序接口RequestHandler让处理人员OrcCommanderOrcOfficerOrcSoldier分别去实现这个接口

/*** 请求处理程序接口** @author xs*/public interface RequestHandler {/*** 可以处理请求的类型** @param req 请求* @return 是否处理*/boolean canHandleRequest(Request req);/*** 获得优先权** @return 优先级*/int getPriority();/*** 处理请求** @param req 请求*/void handle(Request req);/*** 获取处理请求的人** @return 处理请求的名字*/String name();}/*** 兽人指挥官** @author xs*/@Slf4jpublic class OrcCommander implements RequestHandler {/*** 限制请求处理的类型为DEFEND_CASTLE** @param req 请求 请求* @return 是否可用处理该请求*/@Overridepublic boolean canHandleRequest(Request req) {return req.getRequestType() == RequestType.DEFEND_CASTLE;}/*** 获取处理请求的优先级** @return 优先级*/@Overridepublic int getPriority() {return 2;}/*** 进行处理请求** @param req 请求*/@Overridepublic void handle(Request req) {req.markHandled();log.info("{} 处理请求 \"{}\"", name(), req.getRequestDescription());}/*** 处理请求的人** @return 处理的人*/@Overridepublic String name() {return "兽人指挥官";}}/*** 兽人军官** @author xs*/@Slf4jpublic class OrcOfficer implements RequestHandler {/*** 判断是否是TORTURE_PRISONER** @param req 请求* @return true false*/@Overridepublic boolean canHandleRequest(Request req) {return req.getRequestType() == RequestType.TORTURE_PRISONER;}/*** 获取请求的优先级** @return 优先级*/@Overridepublic int getPriority() {return 3;}/*** 处理请求** @param req 请求*/@Overridepublic void handle(Request req) {req.markHandled();log.info("{} 处理请求 \"{}\"", name(), req.getRequestDescription());}/*** 返回处理请求的人** @return name*/@Overridepublic String name() {return "兽人军官";}}/*** 兽人士兵** @author xs*/@Slf4jpublic class OrcSoldier implements RequestHandler {/*** 固定处理请求的类型为COLLECT_TAX** @param req 请求* @return true false*/@Overridepublic boolean canHandleRequest(Request req) {return req.getRequestType() == RequestType.COLLECT_TAX;}/*** 获取处理请求的优先级** @return 优先级*/@Overridepublic int getPriority() {return 1;}/*** 进行处理请求** @param req 请求*/@Overridepublic void handle(Request req) {req.markHandled();log.info("{} 处理请求 \"{}\"", name(), req.getRequestDescription());}/*** 处理的人员** @return name*/@Overridepublic String name() {return "兽人士兵";}}

我们构建一个兽人王来组织处理人员的责任链,并且处理请求

/*** 兽人王** @author xs*/public class OrcKing {private List<RequestHandler> handlers;/*** 构造函数进行构建责任链*/public OrcKing() {buildChain();}/*** 构建责任链*/private void buildChain() {handlers = Arrays.asList(new OrcCommander(), new OrcOfficer(), new OrcSoldier());}/*** 通过责任链处理请求*/public void makeRequest(Request req) {//根据进来的请求类型匹配对应的处理人员进行处理这个请求handlers.stream().sorted(paring(RequestHandler::getPriority)).filter(handler -> handler.canHandleRequest(req)).findFirst().ifPresent(handler -> handler.handle(req));}}

让兽人王发送三个请求给三个不同的处理人员

/*** @author xs*/public class Main {public static void main(String[] args) {OrcKing king = new OrcKing();//国王发送三道请求给不同的处理人员进行处理king.makeRequest(new Request(RequestType.DEFEND_CASTLE, "保卫城堡"));king.makeRequest(new Request(RequestType.TORTURE_PRISONER, "酷刑囚犯"));king.makeRequest(new Request(RequestType.COLLECT_TAX, "征税"));}}

程序输出

[main] INFO com.xs.designpattern.OrcCommander - 兽人指挥官 处理请求 "保卫城堡"[main] INFO com.xs.designpattern.OrcOfficer - 兽人军官 处理请求 "酷刑囚犯"[main] INFO com.xs.designpattern.OrcSoldier - 兽人士兵 处理请求 "征税"

类图

使用场景

责任链模式适用于以下情况:

当有多个对象可以处理同一类型的请求,且需要按照一定的顺序进行处理时。当需要动态组合处理器,灵活地添加、修改或删除处理器,而不影响客户端代码。当需要避免请求发送者和接收者之间的紧耦合关系。

优点

解耦了请求发送者和接收者,使得处理器可以灵活组合,动态添加和移除处理器。可以在责任链上任意位置添加新的处理器,扩展性好。提高了系统的灵活性和可维护性。

缺点

可能导致请求在责任链上没有被处理,或者被多次处理。处理器链可能会变得很长,影响性能。

2、命令模式(Command Pattern)

意图

旨在将请求或操作封装为一个对象,使得可以将请求参数化,将操作队列化,以及支持可撤销的操作。该模式将发送者和接收者解耦,使得发送者无需知道具体的接收者是谁,只需要知道如何触发请求。

程序实例

现实世界的例子

有一个巫师对妖精施咒。咒语一一对妖精施展。第一个咒语使妖精缩小,第二个咒语使他隐形。然后巫师将咒语一一逆转。这里的每个咒语都是一个可以撤消的命令对象。

用简单的话来说

将请求存储为命令对象允许执行操作或稍后撤消该操作。

维基百科说

在面向对象编程中,命令模式是一种行为设计模式,其中对象用于封装稍后执行操作或触发事件所需的所有信息

以上面的例子来解释命令模式,首先向创建巫师Wizard类以及咒语作用目标类Target以及Target的实现类Goblin

/*** 巫师类 持有两个栈用于记录执行的法术操作和撤销的法术操作** @author xs*/@Slf4jpublic class Wizard {/*** 使用双端队列记录执行的法术操作*/private final Deque<Runnable> undoStack = new LinkedList<>();/*** 使用双端队列记录执行的撤销的法术操作*/private final Deque<Runnable> redoStack = new LinkedList<>();/*** 施放法术操作,同时记录到撤销栈中** @param runnable runnable*/public void castSpell(Runnable runnable) {runnable.run();//在此双端队列的末尾插入指定元素undoStack.offerLast(runnable);}/*** 撤销上一个法术操作,将操作从撤销栈移到重做栈*/public void undoLastSpell() {//想撤销上一个法术操作,需要先判断执行记录的队列中是否有记录存在,有则进入if内部,没有则不做操作if (!undoStack.isEmpty()) {//删除执行队列中的最后一个元素Runnable previousSpell = undoStack.pollLast();//添加到撤销队列中redoStack.offerLast(previousSpell);previousSpell.run();}}/*** 重做上一个撤销的法术操作,将操作从重做栈移到撤销栈*/public void redoLastSpell() {//想重做最后一个咒语需要先判断撤销队列是否有撤销的操作记录,有则进入if内部,没有不做操作if (!redoStack.isEmpty()) {//删除撤销队列中的最后一个操作Runnable previousSpell = redoStack.pollLast();//添加到执行队列中undoStack.offerLast(previousSpell);previousSpell.run();}}}/*** @author xs*/@Setter@Slf4j@Getterpublic abstract class Target {private Size size;private Visibility visibility;/*** 打印状态*/public void printStatus() {log.info("{}, [大小={}] [透明度={}]", this, getSize(), getVisibility());}/*** 更改目标的大小*/public void changeSize() {//更改体型大小,如果为正常的则变小,如果是变小的则恢复正常Size oldSize = getSize() == Size.NORMAL ? Size.SMALL : Size.NORMAL;setSize(oldSize);}/*** 更改目标的可见性*/public void changeVisibility() {//更改可见性,如果为正常的变透明,如果是透明的的则恢复正常Visibility visible = getVisibility() == Visibility.INVISIBLE ? Visibility.VISIBLE : Visibility.INVISIBLE;setVisibility(visible);}}/*** @author xs*/public class Goblin extends Target{/*** 构造函数设置默认的大小和可见性*/public Goblin() {setSize(Size.NORMAL);setVisibility(Visibility.VISIBLE);}@Overridepublic String toString() {return "哥布林";}}

定义出巫术作用体型变小以及作用透明的枚举类

/*** @author xs*/@RequiredArgsConstructorpublic enum Size {/*** 大小类型*/SMALL("缩小的"),NORMAL("正常的");private final String title;@Overridepublic String toString() {return title;}}/*** @author xs*/@RequiredArgsConstructorpublic enum Visibility {/*** 魔法的效果枚举*/VISIBLE("可见的"), INVISIBLE("隐形的");private final String title;@Overridepublic String toString() {return title;}

最后让我们对目标进行施法吧

/*** @author xs*/public class Main {public static void main(String[] args) {Wizard wizard = new Wizard();Goblin goblin = new Goblin();goblin.printStatus();wizard.castSpell(goblin::changeSize);goblin.printStatus();wizard.castSpell(goblin::changeVisibility);goblin.printStatus();wizard.undoLastSpell();goblin.printStatus();wizard.undoLastSpell();goblin.printStatus();wizard.redoLastSpell();goblin.printStatus();wizard.redoLastSpell();goblin.printStatus();}}

程序输出

[main] INFO com.xs.designpattern.Target - 哥布林, [大小=正常的] [透明度=可见的][main] INFO com.xs.designpattern.Target - 哥布林, [大小=缩小的] [透明度=可见的][main] INFO com.xs.designpattern.Target - 哥布林, [大小=缩小的] [透明度=隐形的][main] INFO com.xs.designpattern.Target - 哥布林, [大小=缩小的] [透明度=可见的][main] INFO com.xs.designpattern.Target - 哥布林, [大小=正常的] [透明度=可见的][main] INFO com.xs.designpattern.Target - 哥布林, [大小=缩小的] [透明度=可见的][main] INFO com.xs.designpattern.Target - 哥布林, [大小=缩小的] [透明度=隐形的]

类图

使用场景

命令模式适用于以下情况:

当需要将请求或操作的发送者和接收者解耦,使得可以灵活地对请求进行参数化。当需要将多个操作队列化,支持撤销操作或重做操作时。当需要记录日志、事务管理或实现延迟加载等特性时。

优点

解耦了发送者和接收者,使得可以更容易扩展和维护。支持命令的队列化和延迟加载,提高了系统的灵活性和可扩展性。支持撤销操作和事务管理,提供了更好的用户体验。

缺点

可能会导致系统中产生大量的具体命令类,增加了代码的复杂性。可能需要额外的对象来实现命令对象和接收者之间的关联。

3、解释器模式(Interpreter Pattern)

意图

用于定义一种语言的文法规则,并解析该语言中的表达式,从而将其转化为可执行的操作。这种模式适用于需要解释和执行一些特定规则或语法的场景,通常在编程语言、查询语言、正则表达式等领域有广泛应用

程序实例

现实世界的例子

小孩子们正在学校学习基础数学。它们从最基本的“1 + 1”、“4 - 2”、“5 + 5”等开始。

用简单的话来说

解释器模式以所需的语言解释句子。

维基百科说

在计算机编程中,解释器模式是一种设计模式,指定如何评估语言中的句子。基本思想是用专门的计算机语言为每个符号(终结符或非终结符)建立一个类。语言中句子的语法树是复合模式的一个实例,用于为客户评估(解释)该句子。

我们以上面小朋友的学习数学的例子为例,首先我们需要创建一个抽象的数学表达式类Expression ,然后我们需要实现具体的表达式MinusExpressionMultiplyExpressionNumberExpressionPlusExpression

/*** 抽象的表达式类* @author xs*/public abstract class Expression {/*** 表达式进行执行的逻辑* @return 值*/public abstract int interpret();}/*** 减号表达式* @author xs*/@RequiredArgsConstructorpublic class MinusExpression extends Expression {private final Expression leftExpression;private final Expression rightExpression;@Overridepublic int interpret() {return leftExpression.interpret() - rightExpression.interpret();}@Overridepublic String toString() {return "-";}}/*** 乘法表达式* @author xs*/@RequiredArgsConstructorpublic class MultiplyExpression extends Expression {private final Expression leftExpression;private final Expression rightExpression;@Overridepublic int interpret() {return leftExpression.interpret() * rightExpression.interpret();}@Overridepublic String toString() {return "*";}}/*** 数字表达* @author xs*/@RequiredArgsConstructorpublic class NumberExpression extends Expression {private final int number;public NumberExpression(String s) {this.number = Integer.parseInt(s);}@Overridepublic int interpret() {return number;}@Overridepublic String toString() {return "number";}}/*** 加法表达式* @author xs*/@RequiredArgsConstructorpublic class PlusExpression extends Expression {private final Expression leftExpression;private final Expression rightExpression;@Overridepublic int interpret() {return leftExpression.interpret() + rightExpression.interpret();}@Overridepublic String toString() {return "+";}}

然后让我们来使用一下

/*** @author xs*/@Slf4jpublic class Main {public static void main(String[] args) {// 孩子在学校学习一些基础数学定义我们想要解析的数学字符串final String tokenString = "4 3 2 - 1 + *";// 堆栈保存已解析的表达式Stack<Expression> stack = new Stack<>();// 对字符串进行标记并一一检查它们String[] tokenList = tokenString.split(" ");for (String s : tokenList) {if (isOperator(s)) {// 当遇到运算符时,我们期望可以从堆栈顶部弹出数字Expression rightExpression = stack.pop();Expression leftExpression = stack.pop();log.info("从堆栈左侧弹出: {} right: {}", leftExpression.interpret(), rightExpression.interpret());Expression operator = getOperatorInstance(s, leftExpression, rightExpression);log.info("操作方式: {}", operator);int result = operator.interpret();// 运算结果压入栈顶Expression resultExpression = new NumberExpression(result);stack.push(resultExpression);log.info("将结果压入堆栈: {}", resultExpression.interpret());} else {// 数字被压入栈顶Expression i = new NumberExpression(s);stack.push(i);log.info("压入堆栈: {}", i.interpret());}}// 最后,最终结果位于栈顶log.info("结果: {}", stack.pop().interpret());}/*** 检查输入参数是否为运算符** @param s 输入参数* @return 如果输入参数是运算符,则为 true*/public static boolean isOperator(String s) {return s.equals("+") || s.equals("-") || s.equals("*");}/*** 根据参数返回正确的表达式** @param s输入参数* @param left 左边表达式* @param right 右边表达式* @return 表达式*/public static Expression getOperatorInstance(String s, Expression left, Expression right) {switch (s) {case "+":return new PlusExpression(left, right);case "-":return new MinusExpression(left, right);default:return new MultiplyExpression(left, right);}}}

程序输出

[main] INFO com.xs.designpattern.Main - 压入堆栈: 4[main] INFO com.xs.designpattern.Main - 压入堆栈: 3[main] INFO com.xs.designpattern.Main - 压入堆栈: 2[main] INFO com.xs.designpattern.Main - 从堆栈左侧弹出: 3 right: 2[main] INFO com.xs.designpattern.Main - 操作方式: -[main] INFO com.xs.designpattern.Main - 将结果压入堆栈: 1[main] INFO com.xs.designpattern.Main - 压入堆栈: 1[main] INFO com.xs.designpattern.Main - 从堆栈左侧弹出: 1 right: 1[main] INFO com.xs.designpattern.Main - 操作方式: +[main] INFO com.xs.designpattern.Main - 将结果压入堆栈: 2[main] INFO com.xs.designpattern.Main - 从堆栈左侧弹出: 4 right: 2[main] INFO com.xs.designpattern.Main - 操作方式: *[main] INFO com.xs.designpattern.Main - 将结果压入堆栈: 8[main] INFO com.xs.designpattern.Main - 结果: 8

类图

使用场景

解释器模式适用于以下情况:

当需要解释和执行一些特定规则或语法的表达式时,如编程语言、查询语言、正则表达式等。当表达式的语法比较简单且数量有限时,避免引入复杂的解析器。当希望将语言的规则和执行解释器解耦,实现更好的可扩展性和维护性。

优点

将语法规则和解释器分开,使得添加新的语法规则或扩展解释器更加容易。支持灵活的语法和规则组合,可以创建复杂的表达式。

缺点

针对每种语法规则和表达式都需要编写相应的解释器类,可能会产生大量的类。对于复杂的表达式,可能会导致类的层次结构变得庞大,增加了维护的难度。

4、迭代器模式(Iterator Pattern)

意图

用于提供一种统一的方式来访问集合对象中的元素,而无需暴露集合的内部结构。该模式使得客户端可以遍历集合中的元素,而不必关心集合的具体实现。

程序实例

现实世界的例子

宝箱里有一套魔法物品。有多种类型的物品,例如戒指、药剂和武器。可以使用宝箱提供的迭代器按类型浏览项目。

用简单的话来说

容器可以提供与表示无关的迭代器接口来提供对元素的访问。

维基百科说

在面向对象编程中,迭代器模式是一种设计模式,其中迭代器用于遍历容器并访问容器的元素。

/*** 百宝箱* @author xs*/public class TreasureChest {private final List<Item> items;/*** Constructor.*/public TreasureChest() {items =Arrays.asList(new Item(ItemType.POTION, "Potion of courage"), new Item(ItemType.RING, "Ring of shadows"),new Item(ItemType.POTION, "Potion of wisdom"), new Item(ItemType.POTION, "Potion of blood"),new Item(ItemType.WEAPON, "Sword of silver +1"), new Item(ItemType.POTION, "Potion of rust"),new Item(ItemType.POTION, "Potion of healing"), new Item(ItemType.RING, "Ring of armor"),new Item(ItemType.WEAPON, "Steel halberd"), new Item(ItemType.WEAPON, "Dagger of poison"));}public Iterator<Item> iterator(ItemType itemType) {return new TreasureChestItemIterator(this, itemType);}/*** Get all items.*/public List<Item> getItems() {return new ArrayList<>(items);}}/*** @author xs*/public class Item {private ItemType type;private final String name;public Item(ItemType type, String name) {this.setType(type);this.name = name;}@Overridepublic String toString() {return name;}public ItemType getType() {return type;}public final void setType(ItemType type) {this.type = type;}}/*** @author xs*/public enum ItemType {/*** 物品种类*/ANY, WEAPON, RING, POTION}/*** @author xs*/public interface Iterator<T> {/*** 是否有下一个元素* @return true false*/boolean hasNext();/*** 获取下一个元素* @return 元素*/T next();}

在下面的示例中,我们迭代在箱子中找到的环形物品。

/*** @author xs*/@Slf4jpublic class Main {public static void main(String[] args) {demonstrateTreasureChestIteratorForType(RING);demonstrateTreasureChestIteratorForType(POTION);demonstrateTreasureChestIteratorForType(WEAPON);demonstrateTreasureChestIteratorForType(ANY);demonstrateBstIterator();}private static final TreasureChest TREASURE_CHEST = new TreasureChest();private static void demonstrateTreasureChestIteratorForType(ItemType itemType) {log.info("------------------------");log.info("Item Iterator for ItemType " + itemType + ": ");Iterator<Item> itemIterator = TREASURE_CHEST.iterator(itemType);while (itemIterator.hasNext()) {log.info(itemIterator.next().toString());}}private static void demonstrateBstIterator() {log.info("------------------------");log.info("BST Iterator: ");TreeNode<Integer> root = buildIntegerBst();BstIterator<Integer> bstIterator = new BstIterator<>(root);while (bstIterator.hasNext()) {log.info("Next node: " + bstIterator.next().getVal());}}private static TreeNode<Integer> buildIntegerBst() {TreeNode<Integer> root = new TreeNode<>(8);root.insert(3);root.insert(10);root.insert(1);root.insert(6);root.insert(14);root.insert(4);root.insert(7);root.insert(13);return root;}}

程序输出

[main] INFO com.xs.designpattern.Main - ------------------------[main] INFO com.xs.designpattern.Main - Item Iterator for ItemType RING: [main] INFO com.xs.designpattern.Main - Ring of shadows[main] INFO com.xs.designpattern.Main - Ring of armor[main] INFO com.xs.designpattern.Main - ------------------------[main] INFO com.xs.designpattern.Main - Item Iterator for ItemType POTION: [main] INFO com.xs.designpattern.Main - Potion of courage[main] INFO com.xs.designpattern.Main - Potion of wisdom[main] INFO com.xs.designpattern.Main - Potion of blood[main] INFO com.xs.designpattern.Main - Potion of rust[main] INFO com.xs.designpattern.Main - Potion of healing[main] INFO com.xs.designpattern.Main - ------------------------[main] INFO com.xs.designpattern.Main - Item Iterator for ItemType WEAPON: [main] INFO com.xs.designpattern.Main - Sword of silver +1[main] INFO com.xs.designpattern.Main - Steel halberd[main] INFO com.xs.designpattern.Main - Dagger of poison[main] INFO com.xs.designpattern.Main - ------------------------[main] INFO com.xs.designpattern.Main - Item Iterator for ItemType ANY: [main] INFO com.xs.designpattern.Main - Potion of courage[main] INFO com.xs.designpattern.Main - Ring of shadows[main] INFO com.xs.designpattern.Main - Potion of wisdom[main] INFO com.xs.designpattern.Main - Potion of blood[main] INFO com.xs.designpattern.Main - Sword of silver +1[main] INFO com.xs.designpattern.Main - Potion of rust[main] INFO com.xs.designpattern.Main - Potion of healing[main] INFO com.xs.designpattern.Main - Ring of armor[main] INFO com.xs.designpattern.Main - Steel halberd[main] INFO com.xs.designpattern.Main - Dagger of poison[main] INFO com.xs.designpattern.Main - ------------------------[main] INFO com.xs.designpattern.Main - BST Iterator: [main] INFO com.xs.designpattern.Main - Next node: 1[main] INFO com.xs.designpattern.Main - Next node: 3[main] INFO com.xs.designpattern.Main - Next node: 4[main] INFO com.xs.designpattern.Main - Next node: 6[main] INFO com.xs.designpattern.Main - Next node: 7[main] INFO com.xs.designpattern.Main - Next node: 8[main] INFO com.xs.designpattern.Main - Next node: 10[main] INFO com.xs.designpattern.Main - Next node: 13[main] INFO com.xs.designpattern.Main - Next node: 14

类图

使用场景

迭代器模式适用于以下情况:

当需要遍历集合对象中的元素,而不想暴露集合的内部结构时。当希望提供多种遍历方式,如顺序遍历、逆序遍历等。当希望在不影响集合结构的情况下,增加新的遍历算法。

优点

将遍历集合的责任从客户端转移到了迭代器中,使得客户端代码更加简洁。隐藏了集合的内部结构,提供了更好的封装性和隐私保护。支持多种不同的遍历方式,具有更高的灵活性。

缺点

增加了类的数量,可能会使得代码更加复杂。对于简单的集合,使用迭代器模式可能会过于繁琐。

5、中介者模式(Mediator Pattern)

意图

用于降低多个对象之间的直接耦合,通过引入一个中介者对象来协调这些对象之间的交互。这种模式可以减少对象之间的相互依赖,从而提高系统的可维护性和可扩展性。

程序实例

现实世界的例子

盗贼、巫师、霍比特人和猎人决定联合起来,并在同一个队伍中旅行。为了避免每个成员相互耦合,他们使用聚会接口来相互通信。

用简单的话来说

中介器通过强制类的通信流通过中介对象来解耦一组类

维基百科说

在软件工程中,中介者模式定义了一个对象,该对象封装了一组对象如何交互。该模式被认为是一种行为模式,因为它可以改变程序的运行行为。在面向对象编程中,程序通常由许多类组成。业务逻辑和计算分布在这些类中。然而,随着更多的类被添加到程序中,特别是在维护和/或重构期间,这些类之间的通信问题可能变得更加复杂。这使得程序更难阅读和维护。此外,更改程序可能会变得困难,因为任何更改都可能影响其他几个类中的代码。借助中介模式,沟通对象之间的关系被封装在中介对象中。对象之间不再直接通信,而是通过中介者进行通信。这减少了通信对象之间的依赖性,从而减少了耦合。

根据上面的例子,我们首先需要有PartyMember队伍成员的抽象类 让具体的队伍PartyMemberBase去实现它,以及还需要有每个成员去继承PartyMemberBase

/*** @author xs*/public interface PartyMember {/*** 入党* @param party 组织*/void joinedParty(Party party);/*** 当事人行动* @param action 行动*/void partyAction(Action action);/*** 行动* @param action action*/void act(Action action);}/*** @author xs*/@Slf4jpublic abstract class PartyMemberBase implements PartyMember {protected Party party;@Overridepublic void joinedParty(Party party) {log.info("{} 加入party", this);this.party = party;}@Overridepublic void partyAction(Action action) {log.info("{} {}", this, action.getDescription());}@Overridepublic void act(Action action) {if (party != null) {log.info("{} {}", this, action);party.act(this, action);}}@Overridepublic abstract String toString();}/*** 霍比特人* @author xs*/public class Hobbit extends PartyMemberBase{@Overridepublic String toString() {return "霍比特人";}}/*** 猎人* @author xs*/public class Hunter extends PartyMemberBase{@Overridepublic String toString() {return "猎人";}}/*** 盗贼* @author xs*/public class Rogue extends PartyMemberBase{@Overridepublic String toString() {return "盗贼";}}/*** 巫师* @author xs*/public class Wizard extends PartyMemberBase{@Overridepublic String toString() {return "巫师";}}

我们的中介系统由Party接口及其实现组成。

/*** @author xs*/public interface Party {/*** 添加组织成员* @param member 成员*/void addMember(PartyMember member);/*** 行动* @param actor 成员* @param action 行为*/void act(PartyMember actor, Action action);}/*** @author xs*/public class PartyImpl implements Party {private final List<PartyMember> members;public PartyImpl() {members = new ArrayList<>();}/*** 行为* @param actor actor* @param action action*/@Overridepublic void act(PartyMember actor, Action action) {//遍历组织成员 对当前行为做出反应for (PartyMember member : members) {if (!member.equals(actor)) {member.partyAction(action);}}}@Overridepublic void addMember(PartyMember member) {members.add(member);member.joinedParty(this);}}

展示了中介者模式的实际应用

/*** @author xs*/public class Main {public static void main(String[] args) {// 创建政党和成员Party party = new PartyImpl();PartyMember hobbit = new Hobbit();PartyMember wizard = new Wizard();PartyMember rogue = new Rogue();PartyMember hunter = new Hunter();// 添加党员party.addMember(hobbit);party.addMember(wizard);party.addMember(rogue);party.addMember(hunter);// 执行行动 -> 该组织成员通知其他组织成员hobbit.act(Action.ENEMY);wizard.act(Action.TALE);rogue.act(Action.GOLD);hunter.act(Action.HUNT);}}

程序输出

[main] INFO com.xs.designpattern.PartyMemberBase - 霍比特人 加入party[main] INFO com.xs.designpattern.PartyMemberBase - 巫师 加入party[main] INFO com.xs.designpattern.PartyMemberBase - 盗贼 加入party[main] INFO com.xs.designpattern.PartyMemberBase - 猎人 加入party[main] INFO com.xs.designpattern.PartyMemberBase - 霍比特人 发现的敌人[main] INFO com.xs.designpattern.PartyMemberBase - 巫师 奔跑寻找掩护[main] INFO com.xs.designpattern.PartyMemberBase - 盗贼 奔跑寻找掩护[main] INFO com.xs.designpattern.PartyMemberBase - 猎人 奔跑寻找掩护[main] INFO com.xs.designpattern.PartyMemberBase - 巫师 讲一个故事[main] INFO com.xs.designpattern.PartyMemberBase - 霍比特人 来听[main] INFO com.xs.designpattern.PartyMemberBase - 盗贼 来听[main] INFO com.xs.designpattern.PartyMemberBase - 猎人 来听[main] INFO com.xs.designpattern.PartyMemberBase - 盗贼 发现金子[main] INFO com.xs.designpattern.PartyMemberBase - 霍比特人 获得他的那份黄金[main] INFO com.xs.designpattern.PartyMemberBase - 巫师 获得他的那份黄金[main] INFO com.xs.designpattern.PartyMemberBase - 猎人 获得他的那份黄金[main] INFO com.xs.designpattern.PartyMemberBase - 猎人 猎杀了一只兔子[main] INFO com.xs.designpattern.PartyMemberBase - 霍比特人 来吃晚饭了[main] INFO com.xs.designpattern.PartyMemberBase - 巫师 来吃晚饭了[main] INFO com.xs.designpattern.PartyMemberBase - 盗贼 来吃晚饭了

类图

使用场景

中介者模式适用于以下情况:

当系统中多个对象之间存在复杂的相互依赖关系,导致对象之间的交互变得复杂而难以维护时。当一个对象的改变需要同时影响其他多个对象的行为时,通过引入中介者来简化对象之间的交互。当希望将对象之间的交互逻辑集中在一个地方进行管理,提高系统的可维护性和可扩展性。

优点

减少了对象之间的直接依赖关系,提高了系统的松耦合性。将对象之间的交互逻辑集中在一个中介者中,使得交互逻辑更易于维护和管理。可以将对象之间的交互逻辑抽象出来,使得系统更加灵活,易于扩展和修改。

缺点

中介者模式可能会导致中介者对象变得复杂,集中了很多对象之间的交互逻辑。引入中介者可能会增加系统的复杂性,需要仔细设计中介者的接口和实现。

6、备忘录模式(Memento Pattern)

意图

用于在不破坏封装性的情况下捕获一个对象的内部状态,并将其保存在一个外部对象中,以便之后可以恢复对象到之前的状态。

程序实例

现实世界的例子

我们正在开发一个占星学应用程序,我们需要分析恒星随时间的变化特性。我们正在使用 Memento 模式创建恒星状态的快照。

用简单的话来说

备忘录模式捕获对象内部状态,使您可以轻松地在任何时间点存储和恢复对象。

维基百科说

备忘录模式是一种软件设计模式,它提供将对象恢复到其先前状态(通过回滚撤消)的能力。

根据上面的例子我们创建一个恒星的枚举类型

/*** @author xs*/public enum StarType {/*** 恒星枚举*/SUN("太阳"), RED_GIANT("红巨星"), WHITE_DWARF("白矮星"), SUPERNOVA("超新星"), DEAD("死星星");private final String title;StarType(String title) {this.title = title;}@Overridepublic String toString() {return title;}}/*** @author xs*/public interface StarMemento {}

/*** 星星纪念品** @author xs*/@Slf4j@AllArgsConstructorpublic class Star {private StarType type;private int ageYears;private int massTons;/*** 时间流逝*/public void timePasses() {ageYears *= 2;massTons *= 8;switch (type) {case RED_GIANT:type = StarType.WHITE_DWARF;break;case SUN:type = StarType.RED_GIANT;break;case SUPERNOVA:type = StarType.DEAD;break;case WHITE_DWARF:type = StarType.SUPERNOVA;break;case DEAD: {ageYears *= 2;massTons = 0;}break;default:log.info("没有该类型的星星!");}}/*** 获取纪念品** @return StarMementoInternal*/StarMemento getMemento() {StarMementoInternal state = new StarMementoInternal();state.setAgeYears(ageYears);state.setMassTons(massTons);state.setType(type);return state;}/*** 设置纪念品** @param memento StarMemento*/void setMemento(StarMemento memento) {StarMementoInternal state = (StarMementoInternal)memento;this.type = state.getType();this.ageYears = state.getAgeYears();this.massTons = state.getMassTons();}@Overridepublic String toString() {return String.format("%s 年龄: %d years 重量: %d 吨", type.toString(), ageYears, massTons);}/*** StarMemento 实现类*/@Getter@Setterprivate static class StarMementoInternal implements StarMemento {private StarType type;private int ageYears;private int massTons;}}

最后,这是我们如何使用纪念品来存储和恢复恒星状态

/*** @author xs*/@Slf4jpublic class Main {public static void main(String[] args) {Stack<StarMemento> states = new Stack<>();Star star = new Star(StarType.SUN, 10000000, 500000);log.info(star.toString());states.add(star.getMemento());star.timePasses();log.info(star.toString());states.add(star.getMemento());star.timePasses();log.info(star.toString());states.add(star.getMemento());star.timePasses();log.info(star.toString());states.add(star.getMemento());star.timePasses();log.info(star.toString());states.add(star.getMemento());log.info("遍历Stack集合->....");while (!states.isEmpty()) {star.setMemento(states.pop());log.info(star.toString());}}}

程序输出

[main] INFO com.xs.designpattern.Main - 太阳 年龄: 10000000 years 重量: 500000 吨[main] INFO com.xs.designpattern.Main - 红巨星 年龄: 20000000 years 重量: 4000000 吨[main] INFO com.xs.designpattern.Main - 白矮星 年龄: 40000000 years 重量: 32000000 吨[main] INFO com.xs.designpattern.Main - 超新星 年龄: 80000000 years 重量: 256000000 吨[main] INFO com.xs.designpattern.Main - 死星星 年龄: 160000000 years 重量: 2048000000 吨[main] INFO com.xs.designpattern.Main - 遍历Stack集合->....[main] INFO com.xs.designpattern.Main - 死星星 年龄: 160000000 years 重量: 2048000000 吨[main] INFO com.xs.designpattern.Main - 超新星 年龄: 80000000 years 重量: 256000000 吨[main] INFO com.xs.designpattern.Main - 白矮星 年龄: 40000000 years 重量: 32000000 吨[main] INFO com.xs.designpattern.Main - 红巨星 年龄: 20000000 years 重量: 4000000 吨[main] INFO com.xs.designpattern.Main - 太阳 年龄: 10000000 years 重量: 500000 吨

类图

使用场景

备忘录模式适用于以下情况:

当需要实现对象的撤销、恢复或版本历史记录功能时。当需要保存对象的历史状态,以便进行状态回滚或对比操作。当希望在不破坏对象封装性的情况下,将状态保存到外部对象中。

优点

提供了一种方便的方式来保存对象的历史状态,支持撤销、恢复等操作。可以在不破坏对象封装性的情况下,将对象的状态保存在外部备忘录中。支持多次保存不同的历史状态,提供了更灵活的恢复和回滚功能。

缺点

如果备忘录对象的状态过于庞大,可能会占用较大的内存空间。需要管理备忘录对象,可能会增加一些复杂性。

7、观察者模式(Observer Pattern)

意图

用于定义对象之间一对多的依赖关系,使得一个对象的状态改变时,所有依赖于它的对象都会得到通知并自动更新。这种模式使得对象之间的关系解耦,提高了系统的灵活性和可维护性。

程序实例

现实世界的例子

在遥远的土地上居住着霍比特人和兽人。两人大部分时间都在户外,所以他们密切关注天气变化。可以说,他们一直在观察天气。

用简单的话来说

注册为观察者以接收对象中的状态变化。

维基百科说

观察者模式是一种软件设计模式,其中一个对象(称为主体)维护其依赖项(称为观察者)的列表,并通常通过调用其中一个方法来自动通知它们任何状态更改。

一上面观察天气为例子,创建天气观察者WeatherObserver和对应的种族

/*** 抽象的天气观察者* @author xs*/public interface WeatherObserver {/*** 更新天气情况** @param currentWeather 当前的天气情况*/void update(WeatherType currentWeather);}/*** 霍比特人* @author xs*/@Slf4jpublic class Hobbits implements WeatherObserver {@Overridepublic void update(WeatherType currentWeather) {log.info("霍比特人现在面临 {} 天气", currentWeather.getDescription());}}/*** 兽人* @author xs*/@Slf4jpublic class Orcs implements WeatherObserver {@Overridepublic void update(WeatherType currentWeather) {log.info("兽人现在面临 {} 天气", currentWeather.getDescription());}}

然后让这是Weather不断变化

/*** @author xs*/@Slf4jpublic class Weather {private WeatherType currentWeather;private final List<WeatherObserver> observers;public Weather() {observers = new ArrayList<>();currentWeather = WeatherType.SUNNY;}public void addObserver(WeatherObserver obs) {observers.add(obs);}public void removeObserver(WeatherObserver obs) {observers.remove(obs);}/*** 模拟让时间因天气而流逝*/public void timePasses() {WeatherType[] enumValues = WeatherType.values();currentWeather = enumValues[(currentWeather.ordinal() + 1) % enumValues.length];log.info("天气变成了 {}.", currentWeather);//天气改变我们去通知观察员notifyObservers();}private void notifyObservers() {for (WeatherObserver obs : observers) {obs.update(currentWeather);}}}

测试当天气改变后的监听者状态

/*** @author xs*/@Slf4jpublic class Main {public static void main(String[] args) {Weather weather = new Weather();weather.addObserver(new Orcs());weather.addObserver(new Hobbits());weather.timePasses();weather.timePasses();weather.timePasses();weather.timePasses();}}

程序输出

[main] INFO com.xs.designpattern.Weather - 天气变成了 rainy.[main] INFO com.xs.designpattern.Orcs - 兽人现在面临 雨天 天气[main] INFO com.xs.designpattern.Hobbits - 霍比特人现在面临 雨天 天气[main] INFO com.xs.designpattern.Weather - 天气变成了 windy.[main] INFO com.xs.designpattern.Orcs - 兽人现在面临 刮风 天气[main] INFO com.xs.designpattern.Hobbits - 霍比特人现在面临 刮风 天气[main] INFO com.xs.designpattern.Weather - 天气变成了 cold.[main] INFO com.xs.designpattern.Orcs - 兽人现在面临 寒冷的 天气[main] INFO com.xs.designpattern.Hobbits - 霍比特人现在面临 寒冷的 天气[main] INFO com.xs.designpattern.Weather - 天气变成了 sunny.[main] INFO com.xs.designpattern.Orcs - 兽人现在面临 阳光明媚 天气[main] INFO com.xs.designpattern.Hobbits - 霍比特人现在面临 阳光明媚 天气

类图

使用场景

观察者模式适用于以下情况:

当一个对象的改变需要通知其他多个对象,并且不希望这些对象与被观察对象直接耦合时。当一个对象的改变可能导致其他多个对象的状态发生变化,需要实时更新。当希望将观察者和被观察者之间的关系解耦,实现更好的可维护性和扩展性。

优点

实现了对象之间的松耦合,被观察者和观察者之间可以独立演化,不相互影响。支持广播通知机制,一个被观察者状态变化后可以同时通知多个观察者。提高了系统的可维护性和可扩展性,可以随时增加或移除观察者。

缺点

如果观察者过多,通知观察者的过程可能会耗费较多时间。如果观察者和被观察者之间有循环引用,可能导致内存泄漏。

8、状态模式(State Pattern)

意图

用于解决对象在不同状态下的行为变化问题。它允许对象在内部状态发生改变时改变其行为,而不需要使用大量的条件语句。通过将不同状态封装成状态类,并让上下文对象在不同状态之间切换,状态模式可以使代码更清晰、可维护,并且符合开闭原则。

程序实例

现实世界的例子

当在自然栖息地观察猛犸象时,它似乎会根据情况改变其行为。它一开始可能看起来很平静,但随着时间的推移,当它检测到威胁时,它会变得愤怒并对

周围的环境造成危险。

用简单的话来说

状态模式允许对象改变其行为。

维基百科说

状态模式是一种行为软件设计模式,允许对象在其内部状态发生变化时改变其行为。这种模式接近有限状态机的概念。状态模式可以解释为策略模式,它能够通过调用模式接口中定义的方法来切换策略。

创建状态接口以及实现类

/*** 抽象的状态类* @author xs*/public interface State {/*** 抽象的进入状态的接口*/void onEnterState();/*** 抽象的当前状态*/void observe();}/*** 平静状态的类* @author xs*/@Slf4jpublic class PeacefulState implements State{private final Mammoth mammoth;public PeacefulState(Mammoth mammoth) {this.mammoth = mammoth;}@Overridepublic void observe() {log.info("{} 平静祥和.", mammoth);}@Overridepublic void onEnterState() {log.info("{} 平静下来.", mammoth);}}/*** 愤怒状态的类* @author xs*/@Slf4jpublic class AngryState implements State{private final Mammoth mammoth;public AngryState(Mammoth mammoth) {this.mammoth = mammoth;}@Overridepublic void observe() {log.info("{} 很愤怒!", mammoth);}@Overridepublic void onEnterState() {log.info("{} 生气了!", mammoth);}}

创建一个猛犸象的类,定义属于他的状态切换的方法

/*** @author xs*/public class Mammoth {private State state;public Mammoth() {state = new PeacefulState(this);}/*** 模拟猛犸象的状态随着时间流逝改变*/public void timePasses() {if (state.getClass().equals(PeacefulState.class)) {changeStateTo(new AngryState(this));} else {changeStateTo(new PeacefulState(this));}}private void changeStateTo(State newState) {this.state = newState;this.state.onEnterState();}@Overridepublic String toString() {return "猛犸象";}public void observe() {this.state.observe();}}

我们来改变猛犸象的状态

/*** @author xs*/public class Main {public static void main(String[] args) {Mammoth mammoth = new Mammoth();mammoth.observe();mammoth.timePasses();mammoth.observe();mammoth.timePasses();mammoth.observe();}}

程序输出

[main] INFO com.xs.designpattern.PeacefulState - 猛犸象 平静祥和.[main] INFO com.xs.designpattern.AngryState - 猛犸象 生气了![main] INFO com.xs.designpattern.AngryState - 猛犸象 很愤怒![main] INFO com.xs.designpattern.PeacefulState - 猛犸象 平静下来.[main] INFO com.xs.designpattern.PeacefulState - 猛犸象 平静祥和.

类图

使用场景

9、策略模式(Strategy Pattern)

意图

用于定义一组可以互相替换的算法,并将每个算法封装成一个独立的类。这使得算法可以独立于客户端变化,客户端可以在运行时选择不同的算法实现。策略模式通过将算法的变化和使用分离,提高了系统的灵活性和可维护性。

程序实例

现实世界的例子

屠龙是一项危险的工作。有了经验,事情就会变得更容易。经验丰富的屠龙者针对不同类型的龙制定了不同的战斗策略。

用简单的话来说

策略模式允许在运行时选择最适合的算法。

维基百科说

在计算机编程中,策略模式是一种行为软件设计模式,可以在运行时选择算法。

以下我们使用三种方式时间例子的策略模式

创建策略的函数式接口,让不同的策略去实现这个接口

/*** 抽象的屠龙攻略* @author xs*/@FunctionalInterfacepublic interface DragonSlayingStrategy {/*** 抽象的屠龙策略执行接口*/void execute();}/*** 近战策略* @author xs*/@Slf4jpublic class MeleeStrategy implements DragonSlayingStrategy{@Overridepublic void execute() {log.info("你用你的神剑砍下了龙的头!");}}/*** 远程策略* @author xs*/@Slf4jpublic class ProjectileStrategy implements DragonSlayingStrategy{@Overridepublic void execute() {log.info("你用魔法十字弓射杀龙,它倒在地上死了!");}}/*** 法术策略* @author xs*/@Slf4jpublic class SpellStrategy implements DragonSlayingStrategy {@Overridepublic void execute() {log.info("你施展了分解咒语,巨龙就蒸发成了一堆灰尘!");}}

在创建屠龙者,实现可以切换屠龙类型的方法

/*** 屠龙者* @author xs*/@AllArgsConstructorpublic class DragonSlayer {private DragonSlayingStrategy strategy;/*** 改变屠龙策略* @param strategy 屠龙策略*/public void changeStrategy(DragonSlayingStrategy strategy) {this.strategy = strategy;}/*** 执行屠龙策略*/public void goToBattle() {strategy.execute();}}

java8的lambda方法实现

/*** @author xs*/@Slf4jpublic class LambdaStrategy {/*** 枚举来演示策略模式*/@RequiredArgsConstructorpublic enum Strategy implements DragonSlayingStrategy {/*** 屠龙方法枚举*/MELEESTRATEGY(() -> log.info("你用你的神剑砍下了龙的头!")),PROJECTILESTRATEGY(() -> log.info("你用魔法十字弓射杀龙,它倒在地上死了!")),SPELLSTRATEGY(() -> log.info("你施展了分解咒语,巨龙就蒸发成了一堆灰尘!"));private final DragonSlayingStrategy dragonSlayingStrategy;@Overridepublic void execute() {dragonSlayingStrategy.execute();}}}

使用策略模式

/*** @author xs*/@Slf4jpublic class Main {private static final String RED_DRAGON_EMERGES = "红龙现身!";private static final String GREEN_DRAGON_SPOTTED = "前方发现绿龙!";private static final String BLACK_DRAGON_LANDS = "黑龙降临在你面前!";public static void main(String[] args) {//GoF 战略模式log.info(GREEN_DRAGON_SPOTTED);DragonSlayer dragonSlayer = new DragonSlayer(new MeleeStrategy());dragonSlayer.goToBattle();log.info(RED_DRAGON_EMERGES);dragonSlayer.changeStrategy(new ProjectileStrategy());dragonSlayer.goToBattle();log.info(BLACK_DRAGON_LANDS);dragonSlayer.changeStrategy(new SpellStrategy());dragonSlayer.goToBattle();// Java 8 function实现策略模式log.info(GREEN_DRAGON_SPOTTED);dragonSlayer = new DragonSlayer(() -> log.info("你用你的神剑砍下了龙的头!"));dragonSlayer.goToBattle();log.info(RED_DRAGON_EMERGES);dragonSlayer.changeStrategy(() -> log.info("你用魔法十字弓射杀龙,它倒在地上死了!"));dragonSlayer.goToBattle();log.info(BLACK_DRAGON_LANDS);dragonSlayer.changeStrategy(() -> log.info("你施展了分解咒语,巨龙就蒸发成了一堆灰尘!"));dragonSlayer.goToBattle();// Java 8 lambda 实现与枚举策略模式log.info(GREEN_DRAGON_SPOTTED);dragonSlayer.changeStrategy(LambdaStrategy.Strategy.MELEESTRATEGY);dragonSlayer.goToBattle();log.info(RED_DRAGON_EMERGES);dragonSlayer.changeStrategy(LambdaStrategy.Strategy.PROJECTILESTRATEGY);dragonSlayer.goToBattle();log.info(BLACK_DRAGON_LANDS);dragonSlayer.changeStrategy(LambdaStrategy.Strategy.SPELLSTRATEGY);dragonSlayer.goToBattle();}}

程序输出

[main] INFO com.xs.designpattern.Main - 前方发现绿龙![main] INFO com.xs.designpattern.MeleeStrategy - 你用你的神剑砍下了龙的头![main] INFO com.xs.designpattern.Main - 红龙现身![main] INFO com.xs.designpattern.ProjectileStrategy - 你用魔法十字弓射杀龙,它倒在地上死了![main] INFO com.xs.designpattern.Main - 黑龙降临在你面前![main] INFO com.xs.designpattern.SpellStrategy - 你施展了分解咒语,巨龙就蒸发成了一堆灰尘![main] INFO com.xs.designpattern.Main - 前方发现绿龙![main] INFO com.xs.designpattern.Main - 你用你的神剑砍下了龙的头![main] INFO com.xs.designpattern.Main - 红龙现身![main] INFO com.xs.designpattern.Main - 你用魔法十字弓射杀龙,它倒在地上死了![main] INFO com.xs.designpattern.Main - 黑龙降临在你面前![main] INFO com.xs.designpattern.Main - 你施展了分解咒语,巨龙就蒸发成了一堆灰尘![main] INFO com.xs.designpattern.Main - 前方发现绿龙![main] INFO com.xs.designpattern.LambdaStrategy - 你用你的神剑砍下了龙的头![main] INFO com.xs.designpattern.Main - 红龙现身![main] INFO com.xs.designpattern.LambdaStrategy - 你用魔法十字弓射杀龙,它倒在地上死了![main] INFO com.xs.designpattern.Main - 黑龙降临在你面前![main] INFO com.xs.designpattern.LambdaStrategy - 你施展了分解咒语,巨龙就蒸发成了一堆灰尘!

类图

使用场景

策略模式适用于以下情况:

当需要在运行时根据不同情况选择不同算法实现时。当一个类有多个行为变体,可以将这些变体抽象成不同的策略类。当希望通过组合而非继承来实现算法的变化时。

优点

使算法的变化独立于客户端,增强了系统的灵活性和可扩展性。可以在运行时动态选择不同的算法实现,提供了更多的选择。提供了一个清晰的分离,使得算法的实现可以独立维护。

缺点

增加了类的数量,引入了更多的类和对象。客户端需要了解不同的策略类,可能会增加一些复杂性。

10、模板模式(Template Method Pattern)

意图

用于定义一个算法的骨架,将算法的具体步骤延迟到子类中实现。模板模式通过将算法的通用部分放在父类中,具体的实现细节留给子类来实现,以实现代码的重用和扩展。

程序实例

现实世界的例子

偷窃物品的一般步骤是相同的。首先,你选择目标,接下来你以某种方式迷惑他,最后,你偷走物品。然而,有很多方法可以实现这些步骤。

用简单的话来说

模板方法模式概述了父类中的一般步骤,并让具体的子实现定义细节。

维基百科说

在面向对象编程中,模板方法是Gamma 等人确定的行为设计模式之一。在《设计模式》一书中。模板方法是超类(通常是抽象超类)中的方法,并根据许多高级步骤定义操作的骨架。这些步骤本身是通过与模板方法相同的类中的其他辅助方法来实现的。

我们首先介绍模板方法类及其具体实现。

为了确保子类不会重写模板方法,应该声明模板方法(在我们的例子中为

method ) ,否则基类中定义的框架可能会在子类中被重写。steal``final,使用final修饰steal避免偷盗方法不被子类重写

/*** 偷盗方法的摸板** @author xs*/@Slf4jpublic abstract class StealingMethod {/*** 抽象的作用目标** @return 目标人物*/protected abstract String pickTarget();/*** 抽象的迷惑目标* @param target 目标人物*/protected abstract void confuseTarget(String target);/*** 抽象的偷窃* @param target 目标人物*/protected abstract void stealTheItem(String target);/*** 偷*/public final void steal() {String target = pickTarget();log.info("目标被选择为 {}.", target);confuseTarget(target);stealTheItem(target);}}/*** 感动的偷取方法** @author xs*/@Slf4jpublic class SubtleMethod extends StealingMethod {@Overrideprotected String pickTarget() {return "店主";}@Overrideprotected void confuseTarget(String target) {log.info("泪流满面地走近 {} 并拥抱他!", target);}@Overrideprotected void stealTheItem(String target) {log.info("密切接触时抓住 {} 的钱包.", target);}}/*** 抢了就跑的方法** @author xs*/@Slf4jpublic class HitAndRunMethod extends StealingMethod {@Overrideprotected String pickTarget() {return "老妖精女人";}@Overrideprotected void confuseTarget(String target) {log.info("从后面接近 {}.", target);}@Overrideprotected void stealTheItem(String target) {log.info("抓住手提包,快速逃跑!");}}

定义个盗贼的类去实行偷盗行为

/*** 盗贼** @author xs*/@AllArgsConstructorpublic class HalflingThief {private StealingMethod method;/*** 偷*/public void steal() {method.steal();}/*** 改变偷取方法** @param method 偷取方法*/public void changeMethod(StealingMethod method) {this.method = method;}}

进行偷取测试

/*** @author xs*/public class Main {public static void main(String[] args) {HalflingThief thief = new HalflingThief(new HitAndRunMethod());thief.steal();thief.changeMethod(new SubtleMethod());thief.steal();}}

程序输出

[main] INFO com.xs.designpattern.StealingMethod - 目标被选择为 老妖精女人.[main] INFO com.xs.designpattern.HitAndRunMethod - 从后面接近 老妖精女人.[main] INFO com.xs.designpattern.HitAndRunMethod - 抓住手提包,快速逃跑![main] INFO com.xs.designpattern.StealingMethod - 目标被选择为 店主.[main] INFO com.xs.designpattern.SubtleMethod - 泪流满面地走近 店主 并拥抱他![main] INFO com.xs.designpattern.SubtleMethod - 密切接触时抓住 店主 的钱包.=

类图

使用场景

模板模式适用于以下情况:

当有一个算法的流程是固定的,但是某些步骤的实现可能不同。当希望在不改变算法的结构的情况下,对某些步骤进行定制化的扩展。当需要在父类中定义一个模板方法,并将某些步骤的实现留给子类进行重写。

优点

提供了一个固定的算法骨架,将通用的代码放在抽象模板中,提高了代码的复用性。允许子类对算法的某些步骤进行定制化的实现,以满足不同的需求。可以通过继承抽象模板和重写抽象方法来扩展和定制算法的实现。

缺点

由于算法的结构已经固定在模板方法中,可能导致代码的灵活性降低。当算法的步骤非常复杂并且存在大量变化时,模板方法可能会变得庞大,难以维护。

11、访问者模式(Visitor Pattern)

意图

用于将数据结构与数据操作分离,以便可以在不改变数据结构的情况下添加新的操作。访问者模式适用于当一个数据结构中的元素类别固定,但对这些元素的操作可能会变化或增加的情况。

程序实例

现实世界的例子

考虑一个包含军队单位的树结构。司令官下辖两名士官,每士官辖下三名士兵。鉴于层次结构实现了访问者模式,我们可以轻松创建与指挥官、中士、士兵或所有人员交互的新对象。

用简单的话来说

访问者模式定义了可以在数据结构的节点上执行的操作。

维基百科说

在面向对象的编程和软件工程中,访问者设计模式是一种将算法与其运行的对象结构分离的方法。这种分离的实际结果是能够向现有对象结构添加新操作而不修改结构。

鉴于上面的军队单位示例,我们首先有 Unit 和 UnitVisitor 基本类型。

/*** @author xs*/@RequiredArgsConstructorpublic abstract class Unit {private final Unit[] children;/*** 接待访客*/public void accept(UnitVisitor visitor) {Arrays.stream(children).forEach(child -> child.accept(visitor));}}/*** 访客参观抽象类* @author xs*/public interface UnitVisitor {/*** 抽象的士兵访问* @param soldier 士兵*/void visit(Soldier soldier);/*** 抽象的军士访问* @param sergeant 军士*/void visit(Sergeant sergeant);/*** 抽象的指挥官访问* @param commander 指挥官*/void visit(Commander commander);}

然后我们去分别实现这个两个类

/*** 指挥官* @author xs*/public class Commander extends Unit{public Commander(Unit... children) {super(children);}/*** 接受访客参观* @param visitor 访客*/@Overridepublic void accept(UnitVisitor visitor) {visitor.visit(this);super.accept(visitor);}@Overridepublic String toString() {return "指挥官";}}/*** 军士* @author xs*/public class Sergeant extends Unit{public Sergeant(Unit... children) {super(children);}/*** 接受参观* @param visitor 访客*/@Overridepublic void accept(UnitVisitor visitor) {visitor.visit(this);super.accept(visitor);}@Overridepublic String toString() {return "军士";}}/*** 士兵* @author xs*/public class Soldier extends Unit{public Soldier(Unit... children) {super(children);}/*** 接受访客* @param visitor 访客*/@Overridepublic void accept(UnitVisitor visitor) {visitor.visit(this);super.accept(visitor);}@Overridepublic String toString() {return "士兵";}}/*** 指挥官访客** @author xs*/@Slf4jpublic class CommanderVisitor implements UnitVisitor {@Overridepublic void visit(Soldier soldier) {// Do nothing}@Overridepublic void visit(Sergeant sergeant) {// Do nothing}/*** 指挥官访客方法** @param commander 被拜访的指挥官*/@Overridepublic void visit(Commander commander) {log.info("很高兴见到你 {}", commander);}}/*** 访客军士* @author xs*/@Slf4jpublic class SergeantVisitor implements UnitVisitor{@Overridepublic void visit(Soldier soldier) {// Do nothing}/*** 军士访客方法* @param sergeant 待拜访的军士*/@Overridepublic void visit(Sergeant sergeant) {log.info("您好 {}", sergeant);}@Overridepublic void visit(Commander commander) {// Do nothing}}/*** 士兵访客** @author xs*/@Slf4jpublic class SoldierVisitor implements UnitVisitor {/*** 士兵访客方法** @param soldier 被拜访的士兵*/@Overridepublic void visit(Soldier soldier) {log.info("问候 {}", soldier);}@Overridepublic void visit(Sergeant sergeant) {// Do nothing}@Overridepublic void visit(Commander commander) {// Do nothing}}

然后我们进行访问

/*** @author xs*/public class Main {public static void main(String[] args) {Unit commander = new Commander(new Sergeant(new Soldier(), new Soldier(), new Soldier()),new Sergeant(new Soldier(), new Soldier(), new Soldier()));commander.accept(new SoldierVisitor());commander.accept(new SergeantVisitor());commander.accept(new CommanderVisitor());}}

程序输出

[main] INFO com.xs.designpattern.SoldierVisitor - 问候 士兵[main] INFO com.xs.designpattern.SoldierVisitor - 问候 士兵[main] INFO com.xs.designpattern.SoldierVisitor - 问候 士兵[main] INFO com.xs.designpattern.SoldierVisitor - 问候 士兵[main] INFO com.xs.designpattern.SoldierVisitor - 问候 士兵[main] INFO com.xs.designpattern.SoldierVisitor - 问候 士兵[main] INFO com.xs.designpattern.SergeantVisitor - 您好 军士[main] INFO com.xs.designpattern.SergeantVisitor - 您好 军士[main] INFO com.manderVisitor - 很高兴见到你 指挥官

类图

使用场景

访问者模式适用于以下情况:

当需要对一个复杂的数据结构中的元素进行不同操作时,可以将这些操作封装成不同的访问者类。当增加新的操作时,不希望修改数据结构的类,可以通过增加新的具体访问者来实现。当数据结构中的元素类别固定,但对这些元素的操作可能会变化或增加时。

优点

将数据结构和操作解耦,使得操作可以独立变化。可以通过增加新的具体访问者来扩展操作,符合开闭原则。使得数据结构可以在不修改的情况下支持新的操作,增强了可维护性和扩展性。

缺点

增加了访问者和元素的类数量,可能使得代码变得复杂。对于简单的数据结构,引入访问者模式可能会增加额外的复杂性。

参考文档

Java Design Patterns

参考的github地址

其他分类

创造型模式

结构型模式

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。