行为型设计模式
简而言之
行为型设计模式关心的是对象之间的责任分配。它们与结构模式的不同之处在于,它们不仅指定了结构,而且还概述了它们之间消息传递/通信的模式。换句话说,他们帮助回答“在软件组件中如何管理一个行为?”
维基百科说
在软件工程中,行为设计模式是一种设计模式,用于识别对象之间的公共通信模式并实现这些模式. 这样做,这些设计模式在传递信息的时候增加了灵活性。
? 职责链模式
显示举例
例如,在你的账户中有三种支付方式(
A
,
B
,
C
),每一个都有不同的金额的钱。
A
中有100美元,
B
中有300美元,
C
中有1000美元。选择支付方式的偏好为
A
,
B
和
C
.你尝试购买价值210美元的商品。使用职责链,首先检查
A
能否支持购买,如果可以则进行购买,职责终端。否则,请求转发到账户
B
,如果职责中断,则请求一致转发直到找到符合条件的处理器。在这里,
A
,
B
,
C
是链式连接,整个现象就是职责链。
简而言之
职责链模式帮助构建一个对象链。请求从一个端点进入,一直从一个对象到另一个对象,直到找到合适的处理器。
维基百科说
在面向对象的设计中,责任链模式是由一个命令对象的源和一系列处理对象组成的设计模式。 每个处理对象都包含定义它可以处理的命令对象类型的逻辑;其余的被传递给链中的下一个处理对象。
编程示例
翻译我们上面的帐户示例。首先,我们有一个基本帐户,它有把帐户合并在一起的逻辑,和一些其他帐户。
public abstract class Account {
protected float balance;
private Account successor;
public Account(float balance) {
this.balance = balance;
}
public void setNext(Account account)
{
this.successor = account;
}
public void pay(float amount)
{
if(canPay(amount))
{
System.out.println(String.format("Can pay $%s use %s",amount,this.getClass().getSimpleName()));
}
else
{
System.out.println(String.format("Can not pay,forwarding:%s",successor.getClass().getSimpleName()));
successor.pay(amount);
}
}
public boolean canPay(float amount)
{
return Float.compare(balance,amount) > 0;
}
}
public class Bank extends Account {
public Bank(float balance) {
super(balance);
}
}
public class Paypal extends Account {
public Paypal(float balance) {
super(balance);
}
}
public class Bitcoin extends Account{
public Bitcoin(float balance) {
super(balance);
}
}
现在我们使用上边准备的链式连接。
// 我们准备的支付链如下
// bank->paypal->bitcoin
//
// 首选bank
// 如果 bank不能支付选择 paypal
// 如果 paypal不能支付选择 bit coin
Account bank = new Bank(100);
Account paypay = new Paypal(200);
Account bitcon = new Bitcoin(300);
bank.setNext(paypay);
paypay.setNext(bitcon);
bank.pay(259);
? 命令模式
现实举例
一个典型的例子就是你在餐馆点餐。你(“客户端”)问服务员(调用者,“Invoker”)带一些食物(“命令”,Command),服务员简单地将请求转发给厨师,这个厨师是懂得如何烹饪的人。
另一个例子是你(客户端,”Client”)使用遥控(调用者,”Invoke”)切换电视节目(命令,”Command”)。
简而言之
允许您封装对象中的操作。此模式背后的关键思想是提供将客户端与接收方解耦合的方法。
维基百科说
在面向对象的编程中,命令模式是一种行为设计模式,在该模式中,对象被用来封装执行某个动作所需的所有信息,或者在稍后的时间触发事件。该信息包括方法名称、拥有方法参数的对象以及方法参数的值。
编程示例
首先,我们有一个接收器,它可以执行所有可以执行的动作。
// Receiver(接收者)
public class Bulb {
public void turnOn()
{
System.out.println("Bulb has been lit");
}
public void turnOff()
{
System.out.println("Darkness");
}
}
然后我们有一个接口,每个命令都要实现,另外我们有一组命令。
public interface Command {
void undo();
void redo();
void execute();
}
// Command
public class TurnOffCommand implements Command {
private Bulb bulb;
public TurnOffCommand(Bulb bulb) {
this.bulb = bulb;
}
@Override
public void undo() {
this.bulb.turnOn();
}
@Override
public void redo() {
this.bulb.turnOff();
}
@Override
public void execute() {
this.bulb.turnOff();
}
}
public class TurnOnCommand implements Command {
private Bulb bulb;
public TurnOnCommand(Bulb bulb) {
this.bulb = bulb;
}
@Override
public void undo() {
this.bulb.turnOff();
}
@Override
public void redo() {
this.bulb.turnOn();
}
@Override
public void execute() {
this.bulb.turnOn();
}
}
接着我们有调用者(Invoker
),客户端可以使用调用者来处理任意的命令
// 调用者
public class RemoteControl {
public void submit(Command command)
{
command.execute();
}
}
最后看一下客户端如何使用
Bulb bulb = new Bulb();
Command turnOnCommand = new TurnOnCommand(bulb);
Command turnOffCommand = new TurnOffCommand(bulb);
RemoteControl remoteControl = new RemoteControl();
remoteControl.submit(turnOnCommand);
remoteControl.submit(turnOnCommand);
命令模式还可以用于实现基于事务的系统。当您执行命令的历史时,您将保持它的历史。如果最终的命令被成功执行,那么所有的好方法都只是遍历历史,并在所有执行的命令上执行“撤消”。
➿ 迭代器模式
现实举例
一台旧的收音机将会是一个很好的迭代器的例子,用户可以从某个频道开始,然后使用下一个或之前的按钮来浏览各个频道。 或者举一个MP3播放器或电视机的例子,你可以按下一个和之前的按钮,通过连续的频道。换句话说,它们都提供一个接口,通过各自的频道、歌曲或广播电台进行迭代。
简而言之
它提供了一种方法来访问对象的元素,而不暴露底层的表示。
维基百科说
在面向对象编程中,迭代器模式是一种设计模式,其中迭代器用于遍历容器并访问容器的元素。 迭代器模式将算法从容器中分离出来;在某些情况下,算法必须是特定于容器的,因此不能解耦。
编程举例
在Java中可以结合Iterator接口来实现迭代器模式。翻译上边收音机的例子如下;
public class RadioStation {
private float frequency;
public RadioStation(float frequency) {
this.frequency = frequency;
}
public float getFrequency() {
return frequency;
}
}
我们有我们自己的迭代器
public class StationList implements Iterator<RadioStation> {
private List<RadioStation> stations = new ArrayList<>();
private int counter = 0;
@Override
public boolean hasNext() {
return counter < stations.size();
}
@Override
public RadioStation next() {
return stations.get(counter++);
}
public void add(RadioStation radioStation)
{
stations.add(radioStation);
}
public RadioStation remove(RadioStation radioStation)
{
for(int i = 0; i < stations.size(); i++)
{
if(Float.compare(stations.get(i).getFrequency(),radioStation.getFrequency())==0)
{
return stations.remove(i);
}
}
return null;
}
public int count()
{
return stations.size();
}
public RadioStation current()
{
return stations.get(counter);
}
public int key()
{
return counter;
}
public void rewind()
{
counter=0;
}
}
使用方式如下
StationList stationList = new StationList();
stationList.add(new RadioStation(120.1F));
stationList.add(new RadioStation(99.1F));
stationList.add(new RadioStation(156.7F));
stationList.add(new RadioStation(138.2F));
stationList.add(new RadioStation(89.6F));
stationList.add(new RadioStation(78.9F));
while(stationList.hasNext())
{
System.out.println(stationList.next().getFrequency());
}
? 中介模式
现实举例
一个普通的例子是当你和某人在你的移动电话上交谈时,你和他们之间有一个网络供应商,你的谈话通过它而不是直接发送。在这种情况下,网络供应商是中介。
简而言之
中介模式添加第三方对象(称为中介)来控制两个对象之间的交互(称为同事)。它有助于减少相互通信的类之间的耦合。因为现在他们不需要知道彼此的实现。
维基百科说
在软件工程中,中介模式定义了一个对象,它封装了一组对象如何交互。这种模式被认为是一种行为模式,因为它可以改变程序的运行行为。
编程示例
这里是一个与用户相互发送消息的聊天室最简单的例子。
首先我们有中介,也就是聊天室。
public interface ChatRoomMediator {
void showMessage(User user,String message);
}
// Mediator
public class ChatRoom implements ChatRoomMediator {
private SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Override
public void showMessage(User user, String message) {
String date = sf.format(Calendar.getInstance().getTime());
System.out.println(String.format("%s [%s] says:%s",date,user.getUserName(),message));
}
}
下面是我们的用户
public class User {
private String userName;
private ChatRoomMediator chatRoomMediator;
public User(String userName, ChatRoomMediator chatRoomMediator) {
this.userName = userName;
this.chatRoomMediator = chatRoomMediator;
}
public String getUserName() {
return userName;
}
public void sendMessage(String message)
{
this.chatRoomMediator.showMessage(this,message);
}
}
使用如下
ChatRoomMediator mediator = new ChatRoom();
User john = new User("John",mediator);
User jane = new User("Jane",mediator);
john.sendMessage("Hi there!");
jane.sendMessage("Hey!");
? 备忘录模式
现实举例
以计算器为例,当您执行某些计算时,最后的计算将保存在内存中,这样您就可以返回到它,并使用一些操作按钮(即管理员)恢复它。
简而言之
Memento模式是关于捕获和存储对象的当前状态的方式,它可以在稍后以一种平滑的方式恢复。
维基百科说
memento模式是一种软件设计模式,它提供了将对象恢复到以前状态的能力(通过回滚撤销)。
通常在需要提供某种撤销功能时非常有用。
编程示例
让我们举一个文本编辑器的例子,它可以随时保存状态,如果需要,可以恢复。
首先,我们有一个可以保存编辑器状态的memento对象。
public class EditorMemento {
private String content;
public EditorMemento(String content) {
this.content = content;
}
public String getContent() {
return content;
}
}
界限来我们有一个编辑器来使用memento对象
public class Editor {
private String content="";
public void type(String words)
{
content = String.format("%s %s",content,words);
}
public EditorMemento save()
{
return new EditorMemento(content);
}
public void resotre(EditorMemento memento)
{
this.content = memento.getContent();
}
public String getContent() {
return content;
}
}
使用方式如下
Editor editor = new Editor();
editor.type("This is the first sentence.");
editor.type("This is second.");
EditorMemento memento = editor.save();
editor.type("And this is third.");
editor.resotre(memento);
System.out.println(editor.getContent());
? 观察者模式
现实举例
一个很好的例子就是求职者在招聘网站时,只要有匹配的工作机会,他们就会得到通知。
简而言之
定义对象之间的依赖关系,以便每当对象更改其状态时,都会通知所有依赖对象。
维基百科说
观察者模式是一种软件设计模式,其中一个对象被称为主题,它维护一个被称为观察者的依赖项列表,并通过调用其中一个方法来自动通知它们任何状态变化。
编程示例
翻译我们上边的例子。首先我们有需要被通知招聘职位的求职者。
注:JDK中提供了Observer接口和Observal抽象类来实现观察者模式
public class JobPost {
public String title;
public JobPost(String title) {
this.title = title;
}
public String getTitle() {
return title;
}
}
public class JobSeeker implements Observer {
private String name;
public JobSeeker(String name) {
this.name = name;
}
@Override
public void update(Observable o, Object arg) {
JobPost jobPost = (JobPost)arg;
System.out.println(String.format("Hi,%s!New Job posted:%s",name,jobPost.getTitle()));
}
}
然后,我们会有招聘人员会订阅的招聘信息。
public class JobPostings extends java.util.Observable {
private void notity(JobPost jobPost)
{
super.setChanged();
notifyObservers(jobPost);
}
public void addJob(JobPost jobPost)
{
notity(jobPost);
}
}
使用方式如下
JobSeeker john = new JobSeeker("John");
JobSeeker jane = new JobSeeker("Jane");
JobPostings jobPostings = new JobPostings();
jobPostings.addObserver(john);
jobPostings.addObserver(jane);
jobPostings.addJob(new JobPost("Java Developer"));
? 访问者模式
现实举例
考虑某人去迪拜。 他们只是需要一种方式(即签证)进入迪拜。抵达后,他们可以自行前往迪拜的任何地方,而无需事先征得许可或做一些跑腿的工作,以便参观任何地方,只要让他们知道一个地方,他们就可以去参观。 访问者模式允许你这样做,它可以帮助你添加访问的地方,这样他们就可以尽可能多地访问,而不必做任何跑腿的工作。
简而言之
访问者模式允许您在不修改对象的情况下为对象添加更多操作。
维基百科说
在面向对象编程和软件工程中,访问者设计模式是一种将算法从其操作的对象结构中分离出来的方法。这种分离的实际结果是,在不修改这些结构的情况下,可以向现有的对象结构添加新的操作。这是遵循开放/封闭原则的一种方法。
编程示例
让我们举一个动物园模拟的例子,我们有几种不同种类的动物,我们必须让它们发声。 我们使用访问者模式来翻译一下这个例子。
// Visitee
public interface Animal {
void accept(AnimalOperation operation);
}
// Visitor
public interface AnimalOperation {
void visitMonkey(Monkey monkey);
void visitLoin(Lion lion);
void visitDolphin(Dolphin dolphin);
}
动物的实现
public class Monkey implements Animal {
public void shout()
{
System.out.println("Ooh oo aa aa!");
}
@Override
public void accept(AnimalOperation operation) {
operation.visitMonkey(this);
}
}
public class Dolphin implements Animal {
public void speak()
{
System.out.println("Tuut tuttu tuutt!");
}
@Override
public void accept(AnimalOperation operation) {
operation.visitDolphin(this);
}
}
public class Lion implements Animal {
public void roar()
{
System.out.println("Roaaar");
}
@Override
public void accept(AnimalOperation operation) {
operation.visitLoin(this);
}
}
访问者
public class Speak implements AnimalOperation {
@Override
public void visitMonkey(Monkey monkey) {
monkey.shout();
}
@Override
public void visitLoin(Lion lion) {
lion.roar();
}
@Override
public void visitDolphin(Dolphin dolphin) {
dolphin.speak();
}
}
使用方式如下
Monkey monkey = new Monkey();
Lion lion = new Lion();
Dolphin dolphin = new Dolphin();
Speak speak = new Speak();
monkey.accept(speak);
lion.accept(speak);
dolphin.accept(speak);
我们本可以简单地为动物建立一个继承等级,但如果我们需要向动物添加新动作,我们就必须修改动物。但现在我们不需要改变它们。例如,假设我们被要求将跳转行为添加到动物中,我们可以简单地通过创建一个新的访问者来添加该行为。
public class Jump implements AnimalOperation {
@Override
public void visitMonkey(Monkey monkey) {
System.out.println("Jumped 20 feet high! on to the tree!");
}
@Override
public void visitLoin(Lion lion) {
System.out.println("Jumped 7 feet! Back on the ground!");
}
@Override
public void visitDolphin(Dolphin dolphin) {
System.out.println("Walked on water a little and disappeared");
}
}
使用如下
Monkey monkey = new Monkey();
Lion lion = new Lion();
Dolphin dolphin = new Dolphin();
Speak speak = new Speak();
monkey.accept(speak);
lion.accept(speak);
dolphin.accept(speak);
System.out.println("=======================");
AnimalOperation jump = new Jump();
monkey.accept(jump);
lion.accept(jump);
dolphin.accept(jump);
? 策略模式
现实举例
考虑一下排序的例子,我们实现了冒泡排序,但数据开始增长,而冒泡排序开始变得非常缓慢。为了解决这个问题,我们实现了快速排序。但是现在,尽管快速排序算法在大型数据集上做得更好,但是对于较小的数据集来说,它是非常慢的。为了处理这个问题,我们实现了一个策略,在这个策略中,小数据集,冒泡排序将被使用,并用于更大的,快速排序。
简而言之
策略模式允许您根据情况切换算法或策略。策略模式允许您根据情况切换算法或策略。
维基百科说
在计算机编程中,策略模式(也称为策略模式)是一种行为软件设计模式,它允许在运行时选择算法的行为。
编程示例
翻译我们上面的例子。首先,我们有策略接口和不同的策略实现。
public interface SortStrategy {
int[] sort(int[] arr);
}
public class BubbleSortStrategy implements SortStrategy {
@Override
public int[] sort(int[] arr) {
System.out.println("Sorting using bubble sort");
return arr;
}
}
public class QuickSortStrategy implements SortStrategy{
@Override
public int[] sort(int[] arr) {
System.out.println("Sorting using quick sort");
return arr;
}
}
我们的客户端可以使用任何策略。
public class Sorter {
private SortStrategy sortStrategy;
public Sorter(SortStrategy sortStrategy) {
this.sortStrategy = sortStrategy;
}
public int[] sort(int[] arr)
{
return sortStrategy.sort(arr);
}
}
使用方式如下
int[] arr = {1, 5, 4, 3, 2, 8};
Sorter sorter = new Sorter(new BubbleSortStrategy());
sorter.sort(arr);
System.out.println("=========================");
sorter = new Sorter(new QuickSortStrategy());
sorter.sort(arr);
? 状态模式
现实举例
假设您正在使用一些绘图应用程序,您选择画笔绘制。现在画笔会根据所选的颜色改变它的行为如果你选择了红色,它会画出红色,如果蓝色,它会是蓝色的等等。
简而言之
它允许在状态更改时更改类的行为。
维基百科说
状态模式是一种行为软件设计模式,它以面向对象的方式实现状态机。 在状态模式下,状态机通过实现每个单个状态作为状态模式接口的派生类实现,并通过调用模式父类定义的方法实现状态转换。
状态模式可以解释为一种策略模式,它可以通过在模式接口中定义的方法调用来切换当前策略。
编程示例
让我们举一个文本编辑器的例子,它允许你改变输入的文本的状态,如果你选择了粗体,它开始以粗体输入;如果选择斜体,以斜体输入,等等。
首先我们有状态接口和状态的实现。
public interface WriteState {
void write(String word);
}
public class UpperState implements WriteState {
@Override
public void write(String word) {
System.out.println(word.toUpperCase());
}
}
public class LowerState implements WriteState {
@Override
public void write(String word) {
System.out.println(word.toLowerCase());
}
}
public class DefaultState implements WriteState {
@Override
public void write(String word) {
System.out.println(word);
}
}
然后我们有一个编辑器
public class TextEditor {
private WriteState writeState;
public TextEditor(WriteState writeState) {
this.writeState = writeState;
}
public void setWriteState(WriteState writeState) {
this.writeState = writeState;
}
public void type(String word)
{
this.writeState.write(word);
}
}
使用方式如下
TextEditor editor = new TextEditor(new DefaultState());
editor.type("First line");
editor.setWriteState(new LowerState());
editor.type("Second line");
editor.type("Third line");
editor.setWriteState(new UpperState());
editor.type("Forth line");
editor.type("Fifth line");
? 模版方法模式
现实举例
假设我们要建一些房子。建造的步骤可能是这样的。
- 打地基
- 建墙
- 增加房顶
- 增加其他的楼层
这些步骤的顺序是不可能改变的,例如,在建造墙之前你不能建造屋顶,但是每一个步骤都可以被修改,例如墙壁可以用木头或聚酯或石头做。
简而言之
模板方法定义了如何执行某个算法的骨架,但将这些步骤的实现转移到子类中。
维基百科说
在软件工程中,模板方法模式是一种行为设计模式,它在操作中定义算法的程序骨架,将一些步骤推迟到子类。它让一个人重新定义算法的某些步骤而不改变算法的结构。
Programmatic Example
假设我们有一个构建工具,它可以帮助我们测试、lint、构建、生成构建报告(比如代码覆盖率报告、linting报告等),并在测试服务器上部署我们的应用程序。
首先,我们有一个为构建算法指定骨架的基类。
public abstract class Builder {
public void build() {
test();
lint();
assemble();
deploy();
}
public abstract void test();
public abstract void lint();
public abstract void assemble();
public abstract void deploy();
}
实现
public class AndroidBuilder extends Builder {
@Override
public void test() {
System.out.println("Running android tests");
}
@Override
public void lint() {
System.out.println("Linting the android code");
}
@Override
public void assemble() {
System.out.println("Assembling the android build");
}
@Override
public void deploy() {
System.out.println("Deploying android build to server");
}
}
public class IOSBuilder extends Builder {
@Override
public void test() {
System.out.println("Running ios tests");
}
@Override
public void lint() {
System.out.println("Linting the ios code");
}
@Override
public void assemble() {
System.out.println("Assembling the ios build");
}
@Override
public void deploy() {
System.out.println("Deploying ios build to server");
}
}
使用如下
Builder builder = new AndroidBuilder();
builder.build();
System.out.println("========================");
builder = new IOSBuilder();
builder.build();