2.1.1 概念
备忘录模式(Memento Pattern)又叫做快照模式(Snapshot Pattern)或Token模式,指在不破坏封闭的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
该模式用于保存对象当前状态,并且在之后可以再次恢复到此状态。备忘录模式实现的方式需要保证被保存的对象状态不能被对象从外部访问,
目的是为了保护被保存的这些对象状态的完整性以及内部实现不向外暴露
备忘录模式中的角色:
1.Originator(发起人):负责创建一个备忘录Memento,用以记录当前时刻自身的内部状态,并可使用备忘录恢复内部状态。Originator可以根据需要决定Memento存储自己的哪些内部状态。
2.Memento(备忘录):负责存储Originator对象的内部状态,并可以防止Originator以外的其他对象访问备忘录。备忘录有两个接口:Caretaker只能看到备忘录的窄接口,他只能将备忘录传递给其他对象。Originator却可看到备忘录的宽接口,允许它访问返回到先前状态所需要的所有数据。
3.Caretaker(管理者):负责备忘录Memento,不能对Memento的内容进行访问或者操作
2.1.2 备忘录模式代码实现
/**
* 备忘录
*
* @author wangjie
* @version V1.0
* @date 2020/4/10
*/
public class Memento {
private String state;
public Memento(String state){
this.state = state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
/**
* 管理者
*
* @author wangjie
* @version V1.0
* @date 2020/4/10
*/
public class Caretaker {
private Memento mMemento;
public Memento getMemento(){
return mMemento;
}
public void setMemengto(Memento memento){
this.mMemento = memento;
}
}
/**
* 发起者
*
* @author wangjie
* @version V1.0
* @date 2020/4/10
*/
public class Originator {
private String state;
/**
* 创建一个新的备忘录对象
* */
public Memento createMemento(){
return new Memento(state);
}
/**
* 将发起者的状态恢复到备忘录的状态
* */
public void restore(Memento memento){
this.state = memento.getState();
}
}
2.1.3 备忘录模式总结
备忘录模式实在不破坏封装的条件下,通过备忘录对象(Memento)存储另外一个对象内部状态的快照,在将来合适的时候把这个对象还原到存储起来的状态。
优点:给用户提供了一种可以恢复状态的机制,可以是用户能够方便的回到某个历史的状态
实现了信息的封装,是的用户不需要关心状态的保存细节
缺点:消耗资源,如果累的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。
2.2.1 概念
抽象主题角色(Subject)
具体主题角色(ConcreteSubject)
抽象观察者角色(Observer)
具体观察者角色(ConcreteObserver)
2.2.2 观察者模式代码实现
//抽象观察者角色
public interface People {
void update(News news);
}
//具体观察者角色A
public class PeopleA implements People {
@Override
public void update(News news) {
System.out.println("这个新闻真好看");
}
}
//具体观察者角色B
public class PeopleB implements People {
@Override
public void update(News news) {
System.out.println("这个新闻真无语");
}
}
//具体观察者角色C
public class PeopleC implements People {
@Override
public void update(News news) {
System.out.println("这个新闻真逗");
}
}
//存放事件的信息,非必须的类
public class News {
private String title;
private String content;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
//抽象主题角色
public interface Subject {
List<People> peopleList = new ArrayList<>();
default void add(People people) {
peopleList.add(people);
}
default void remove(People people) {
peopleList.remove(people);
}
void update();
}
//具体主题角色
public class NewsSubject implements Subject{
public void update() {
for (People people : peopleList) {
News news = new News();
news.setContent("今日在大街上,有人躲在草丛中袭击路人,还大喊“德玛西亚万岁”");
news.setTitle("德玛西亚出现了");
people.update(news);
}
}
}
//测试
public class Main {
public static void main(String[] args) {
Subject subject = new NewsSubject();
subject.add(new PeopleA());
subject.add(new PeopleB());
subject.add(new PeopleC());
subject.update();
}
}
2.2.3 观察者模式总结
2.3.1 概念
Context: 环境类
State: 抽象状态类
ConcreteState: 具体状态类
2.3.2 状态模式代码实现
//抽象状态类
public abstract class FlowState {
protected Context context;
public void setContext(Context context) {
this.context = context;
}
abstract void audit();
abstract void pass();
abstract void noPass();
}
//具体状态类Audit
public class Audit extends FlowState{
@Override
void audit() {
System.out.println("审核中...");
}
@Override
void pass() {
super.context.setState(Context.PASS);
super.context.getState().pass();
}
@Override
void noPass() {
super.context.setState(Context.NO_PASS);
super.context.getState().noPass();
}
}
//具体状态类Pass
public class Pass extends FlowState{
@Override
void audit() {
super.context.setState(Context.AUDIT);
super.context.getState().audit();
}
@Override
void pass() {
System.out.println("通过啦...");
}
@Override
void noPass() {
super.context.setState(Context.NO_PASS);
super.context.getState().noPass();
}
}
//具体状态类NoPass
public class NoPass extends FlowState{
@Override
void audit() {
super.context.setState(Context.AUDIT);
super.context.getState().audit();
}
@Override
void pass() {
super.context.setState(Context.PASS);
super.context.getState().pass();
}
@Override
void noPass() {
System.out.println("不通过...");
}
}
//环境类
public class Context {
protected FlowState state;
protected static final Audit AUDIT = new Audit();
protected static final NoPass NO_PASS = new NoPass();
protected static final Pass PASS = new Pass();
public Context(FlowState state) {
setState(state);
}
public FlowState getState() {
return state;
}
public void setState(FlowState state) {
this.state = state;
this.state.setContext(this);
}
void audit(){
this.getState().audit();
}
void pass(){
this.getState().pass();
}
void noPass(){
this.getState().noPass();
}
}
//测试
public class Apply {
public static void main(String[] args){
try {
//申请
System.out.println("提交申请...");
Context context = new Context(new Audit());
context.audit();
Thread.sleep(100);
context.pass();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
2.3.3 状态模式总结
2.4.1 概念
Context: 环境类
Strategy: 抽象策略类
ConcreteStrategy: 具体策略类
2.4.2 策略模式代码实现
//抽象策略类
public interface Strategy {
void doSomething();
}
//具体策略类
public class BlockingWaitStrategy implements Strategy{
@Override
public void doSomething() {
System.out.println(this.getClass().getSimpleName() + "使用锁和条件变量。CPU资源的占用少,延迟大");
}
}
public class BusySpinWaitStrategy implements Strategy{
@Override
public void doSomething() {
System.out.println(this.getClass().getSimpleName() + "自旋等待,类似Linux Kernel使用的自旋锁。低延迟但同时对CPU资源的占用也多");
}
}
public class PhasedBackoffWaitStrategy implements Strategy {
@Override
public void doSomething() {
System.out.println(this.getClass().getSimpleName() + "多种策略的综合,CPU资源的占用少,延迟大");
}
}
public class SleepingWaitStrategy implements Strategy {
@Override
public void doSomething() {
System.out.println(this.getClass().getSimpleName() + "在多次循环尝试不成功后,选择让出CPU,等待下次调度,多次调度后仍不成功,尝试前睡眠一个纳秒级别的时间再尝试。这种策略平衡了延迟和CPU资源占用,但延迟不均匀");
}
}
public class YieldingWaitStrategy implements Strategy {
@Override
public void doSomething() {
System.out.println(this.getClass().getSimpleName() + " 在多次循环尝试不成功后,选择让出CPU,等待下次调。平衡了延迟和CPU资源占用,但延迟也比较均匀");
}
}
public class Context{
private Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
public void getStrategy(){
strategy.doSomething();
}
}
//测试
public class Test {
public static void main(String[] args) {
Context context= new Context(new BlockingWaitStrategy());
context.getStrategy();
}
}
2.4.3 策略模式总结
2.5.1 概念
抽象类(AbstractClass):实现了模板方法,定义了算法的骨架。
具体类(ConcreteClass):实现抽象类中的抽象方法,已完成完整的算法。
2.5.2 模板方法模式代码实现
/**
* 抽象基类,为其他子类提供一个算法框架 提神饮料
*/
public abstract class RefreshBeverage {
/*
* 制备饮料的模板方法 封装了所有子类所遵循的算法框架
*/
public final void prepareBeverageTemplate() {
// 步骤一 将水煮沸
boilWater();
// 步骤二 泡制饮料
brew();
// 步骤三 将饮料倒入杯中
pourInCup();
if (isCustomerWantsCondiments()) {
// 步骤四 加入调味料
addCondiments();
}
}
/*
* Hook 钩子函数,提供一个空的或者默认的实现 子类重写该方法,可以自行决定是否挂钩以及如何挂钩
*/
protected boolean isCustomerWantsCondiments() {
return true;
}
// 因为将水煮沸和把饮料倒入杯中对所有子类是共同的行为,所以没必要向子类过多开放,所以方法定义为private,这样我们在进行子类编码时可以减少复杂度。
// 这样不需要关注细枝末节,我们只需要关注我们特定业务的实现,这就是模板方法模式的好处。可以封装变与不变,将不变的固化在高层,隐藏其细节。
private void boilWater() {
System.out.println("将水煮沸");
}
private void pourInCup() {
System.out.println("将饮料倒入杯中");
}
/*
* 泡制饮料brew()和加入调料品addCondiments()这两个方法我们不知道它们在算法框架中的具体实现,因此定义为抽象方法,
* 我们用protected进行修饰, 在子类中可见便于进行重写。
*/
protected abstract void brew();
protected abstract void addCondiments();
}
/**
* 提供制备咖啡的具体实现子类。 具体子类实现延迟步骤,满足特定的业务需求。
*/
public class Coffee extends RefreshBeverage {
protected void brew() {
System.out.println("步骤二 用沸水冲泡咖啡");
}
protected void addCondiments() {
System.out.println("步骤四 加入糖和牛奶");
}
}
public class Tea extends RefreshBeverage {
protected void brew() {
System.out.println("步骤二 用80度热水浸泡茶叶5分钟");
}
protected void addCondiments() {
System.out.println("步骤四 加入柠檬");
}
protected boolean isCustomerWantsCondiments() {
return false;
}
}
public class Test {
public static void main(String[] args) {
System.out.println("制备咖啡中······");
RefreshBeverage b1 = new Coffee();
b1.prepareBeverageTemplate();
System.out.println("咖啡好了········");
// 制备茶的测试代码
System.out.println("\n*********************************");
System.out.println("制备茶水中······");
RefreshBeverage b2 = new Tea();
b2.prepareBeverageTemplate();
System.out.println("茶水好了······");
}
}
2.5.3 模板方法模式总结
优点:1、模板方法模式通过把不变的行为搬移到超类,去除了子类中的重复代码。
子类实现算法的某些细节,有助于算法的扩展;2、通过一个父类调用子类实现的操作,通过子类扩展增加新的行为,符合“开放-封闭原则”。
缺点:每个不同的实现都需要定义一个子类,这会导致类的个数的增加,设计更加抽象。
适用场景:1、在某些类的算法中,用了相同的方法,造成代码的重复;2、控制子类扩展,子类必须遵守算法规则。
2.6.1 概念
1、Visitor 抽象访问者角色:为该对象结构中具体元素角色声明一个访问操作接口。该操作接口的名字和参数标识了发送访问请求给具体访问者的具体元素角色,这样访问者就可以通过该元素角色的特定接口直接访问它。
2、ConcreteVisitor具体访问者角色:实现Visitor声明的接口。
3、Element抽象受访元素:定义一个接受访问操作(accept()),它以一个访问者(Visitor)作为参数。
4、ConcreteElement 具体受访元素:实现了抽象元素(Element)所定义的接受操作接口。
5、ObjectStructure 结构对象角色:这是使用访问者模式必备的角色。它具备以下特性:能枚举它的元素;可以提供一个高层接口以允许访问者访问它的元素;如有需要,可以设计成一个复合对象或者一个聚集(如一个列表或无序集合)。
2.6.2 访问者模式代码实现
//我们都知道财务都是有账本的,这个账本就可以作为一个对象结构,而它其中的元素有两种,收入和支出,这满足我
//们访问者模式的要求,即元素的个数是稳定的,因为账本中的元素只能是收入和支出。
//而查看账本的人可能有这样几种,比如老板,会计事务所的注会,财务主管,等等。而这些人在看账本的时候显然目
//的和行为是不同的。
//单个单子的接口(相当于Element)
public interface Bill {
void accept(AccountBookViewer viewer);
}
//消费的单子
public class ConsumeBill implements Bill{
private double amount;
private String item;
public ConsumeBill(double amount, String item) {
super();
this.amount = amount;
this.item = item;
}
public void accept(AccountBookViewer viewer) {
viewer.view(this);
}
public double getAmount() {
return amount;
}
public String getItem() {
return item;
}
}
//收入单子
public class IncomeBill implements Bill{
private double amount;
private String item;
public IncomeBill(double amount, String item) {
super();
this.amount = amount;
this.item = item;
}
public void accept(AccountBookViewer viewer) {
viewer.view(this);
}
public double getAmount() {
return amount;
}
public String getItem() {
return item;
}
}
//账单查看者接口(相当于Visitor)
public interface AccountBookViewer {
//查看消费的单子
void view(ConsumeBill bill);
//查看收入的单子
void view(IncomeBill bill);
}
//老板类,查看账本的类之一
public class Boss implements AccountBookViewer{
private double totalIncome;
private double totalConsume;
//老板只关注一共花了多少钱以及一共收入多少钱,其余并不关心
public void view(ConsumeBill bill) {
totalConsume += bill.getAmount();
}
public void view(IncomeBill bill) {
totalIncome += bill.getAmount();
}
public double getTotalIncome() {
System.out.println("老板查看一共收入多少,数目是:" + totalIncome);
return totalIncome;
}
public double getTotalConsume() {
System.out.println("老板查看一共花费多少,数目是:" + totalConsume);
return totalConsume;
}
}
//注册会计师类,查看账本的类之一
public class CPA implements AccountBookViewer{
//注会在看账本时,如果是支出,则如果支出是工资,则需要看应该交的税交了没
public void view(ConsumeBill bill) {
if (bill.getItem().equals("工资")) {
System.out.println("注会查看工资是否交个人所得税。");
}
}
//如果是收入,则所有的收入都要交税
public void view(IncomeBill bill) {
System.out.println("注会查看收入交税了没。");
}
}
//账本类(相当于ObjectStruture)
public class AccountBook {
//单子列表
private List<Bill> billList = new ArrayList<Bill>();
//添加单子
public void addBill(Bill bill){
billList.add(bill);
}
//供账本的查看者查看账本
public void show(AccountBookViewer viewer){
for (Bill bill : billList) {
bill.accept(viewer);
}
}
}
public class Client {
public static void main(String[] args) {
AccountBook accountBook = new AccountBook();
//添加两条收入
accountBook.addBill(new IncomeBill(10000, "卖商品"));
accountBook.addBill(new IncomeBill(12000, "卖广告位"));
//添加两条支出
accountBook.addBill(new ConsumeBill(1000, "工资"));
accountBook.addBill(new ConsumeBill(2000, "材料费"));
AccountBookViewer boss = new Boss();
AccountBookViewer cpa = new CPA();
//两个访问者分别访问账本
accountBook.show(cpa);
accountBook.show(boss);
((Boss) boss).getTotalConsume();
((Boss) boss).getTotalIncome();
}
}
2.6.3 访问者模式总结:
优点:1、使得数据结构和作用于结构上的操作解耦,使得操作集合可以独立变化;2、添加新的操作或者说访问者会非常容易;3、将对各个元素的一组操作集中在一个访问者类当中;4、使得类层次结构不改变的情况下,可以针对各个层次做出不同的操作,而不影响类层次结构的完整性;5、可以跨越类层次结构,访问不同层次的元素类,做出相应的操作。
缺点:1、增加新的元素会非常困难;2、实现起来比较复杂,会增加系统的复杂性;3、破坏封装,如果将访问行为放在各个元素中,则可以不暴露元素的内部结构和状态,但使用访问者模式的时候,为了让访问者能获取到所关心的信息,元素类不得不暴露出一些内部的状态和结构。
适用性:1、数据结构稳定,作用于数据结构的操作经常变化的时候;2、当一个数据结构中,一些元素类需要负责与其不相关的操作的时候,为了将这些操作分离出去,以减少这些元素类的职责时,可以使用访问者模式;3、有时在对数据结构上的元素进行操作的时候,需要区分具体的类型,这时使用访问者模式可以针对不同的类型,在访问者类中定义不同的操作,从而去除掉类型判断。