针对Balking设计模式,我们先结合一下现实生活中的例子来认识一下它。我们在餐馆点餐,想好之后,举手示意服务员点菜,于是,看到我举手的服务员就向我走过来,但这时,另一位服务员也看到我举手示意了,但她看到已经有一位服务员走向了我,所以就没有再过来......
如果现在不适合执行这个操作,或者没必要执行这个操作,就停止处理,直接返回-----这就是我们介绍的Balking模式。
我们来看一下Balking模式的简单示例程序,这个程序会定期地将当前数据内容写入文件中。当数据内容被写入时,会完全覆盖上次写入的内容,只有最新的内容才会被保存。另外,当写入内容和上次写入的内容完全相同时,再向文件写入就显得多余了,所以就不再执行写入操作。也就是说,该程序以“数据内容存在不同”作为守护条件,如果数据内容相同,则不执行写入操作,直接返回(Balk)。
在这里我们来思考一下文本工具的“自动保存功能”,所谓自动保存功能,就是为了防止电脑突然死机,而定期地将数据保存到文本中的功能。下面该示例程序可以说是自动保存功能的超级精简版。
先介绍一下用到的一下类:Data表示可以修改并保存的数据的类,对应文本工具的文本内容;SaverThread定期保存数据的内容的类,对应的是执行自动保存的线程;ChangerThread是模仿“进行文本修改并随时保存的用户”,对应着的是修改并保存数据内容的类。
Data类用于表示当前数据。
filename字段是执行保存的文件名称
content字段是表示数据内容的字符串,也就是写入文件的内容。
changed字段用于表示自上次保存之后,Content字段是否又进行了修改,changed字段的值为true时表示进行了修改,为false则为未修改,程序根据changed的值决定要不要执行文件写入。这种表示两种状态的字段和变量,我们通常称之为(flag)。这里,“changed字段标志位true”便是守护条件。Data类中用于修改数据内容的方法(change),以及要求将数据内容保存到文件中的方法(save)。另外还有一个实际执行文件保存的方法(doSave)。
在change方法中,新的数据内容会被赋予到content字段,同时,changed字段会被设置为true,这表示内容进行了修改。
Change和save这两个方法都被声明了synchronized方法,我们都知道,当看到synchronized时我们需要思考一下在保护着什么,在这里,我们保护的是content和changed字段。
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
public class Data {
private final String filename; // 保存的文件名称
private String content; // 数据内容
private Boolean changed;// 修改后的文件尚未保存时,为true
public Data(String filename, String content) {
this.filename = filename;
this.content = content;
this.changed = true;
}
// 修改数据内容
public synchronized void change(String newContent) {
content = newContent;
changed = true; //为了表示content字段内容与文件内容不一致,未修改状态
}
// 若数据内容修改过,则保存到文件中
public synchronized void save() throws IOException {
if (!changed) {
return;
}
doSave();
changed = false; // 为了表示content字段内容与文件内容一致,未修改状态
}
private void doSave() throws IOException {
System.out.println(Thread.currentThread().getName() + "calls doSave,content=" + content);
Writer writer = new FileWriter(filename);
writer.write(content);
writer.close();
}
}SaverThread类用于定期保存数据内容
在run方法中每隔一秒就调用一次Data实例的save方法,如果文件操作发生错误,IOException异常被抛出,该线程就会终止运行。
import java.io.IOException;
import com.balking.pojo.Data;
public class SaverThread extends Thread{
private final Data data;
public SaverThread(String name,Data data) {
super(name);
this.data = data;
}
public void run() {
try {
while (true) {
data.save(); //要求保存数据
Thread.sleep(1000); //休眠约1秒
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
ChangerThread类用于修改数据内容,并执行保存处理
启动的线程以”No.0”,”No.1”,”No.2”, ...字符串为参数,循环调用change方法来修改数据。每修改完一个数据,线程都会“执行点别的操作”(实际上只是使用了sleep方法随机暂停了一段时间),然后调用save方法保存数据。这用来表示“将刚才修改的数据反映到文件中”。
注意Data类的doSave方法每次都是重新创建文件。由于在重新创建之后,文件内容会全部消失,所以该该示例并不能直接用作应用程序的自动保存功能。实际上,实现自动保存功能时,文件必须要进行备份。
import java.io.IOException;
import java.util.Random;
import com.balking.pojo.Data;
public class ChangerThread extends Thread{
private final Data data;
private final Random random = new Random();
public ChangerThread(String name,Data data) {
super(name);
this.data = data;
}
public void run() {
try {
for (int i = 0; true; i++) {
data.change("No."+i); //修改数据
Thread.sleep(random.nextInt(1000)); //执行其他操作
data.save(); //显示地保存
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Main类运行:两个线程都执行了doSave方法。
请注意:不管是哪种情况,content字段的内容都“不是重复保存的”(没有重复的编号),这是因为content内容相同时,线程就会Balk出,不再调用doSave方法。
import com.balking.pojo.Data;
import com.balking.thread.ChangerThread;
import com.balking.thread.SaverThread;
public class Main {
public static void main(String[] args) {
Data data = new Data("data.txt","emtity");
new ChangerThread("ChangerThread", data).start();
new SaverThread("SaverThread",data).start();
}
}
运行结果:
也可参考时序图:
这就是我们介绍的Balking模式---对象本身拥有状态,该对象只有在自身状态合适时才会执行处理,否则便不会执行处理。
我们首先将对象的合适状态表示为守护条件。然后再处理之前,检查守护条件是否成立,只有当守护条件成立时,对象才会继续执行处理,如果守护条件不成立,则不执行处理,立即从方法中返回。