设计模式系列——备忘录模式

Posted 花括号MC

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式系列——备忘录模式相关的知识,希望对你有一定的参考价值。

原创:花括号MC(微信公众号:huakuohao-mc)。关注JAVA基础编程及大数据,注重经验分享及个人成长。

备忘录模式用于备份对象的部分内部状态,在需要的时候,将对象状态恢复到原来的样子。

举个例子

假设日常玩儿游戏的时候,希望自己的角色在打个大boss的时候先保存一下角色状态,如果打boss失败则恢复到原来的状态,以便重新开始。这个时候我们考虑使用备忘录模式。

来看一下UML图 :


看一下具体代码实现

先创建一个备忘录,用于记录需要记录的状态。

public class Memento 
    //需要保存的状态,可以是多个
    private String state;

    public Memento(String state)
        this.state = state;
    

    public String getState()
        return state;
    

定义一个游戏角色,在该角色内部创建备忘录,并将相应状态保存至备忘录。

public class GameRole 
    //游戏状态
    private String state;

    public String getState() 
        return state;
    

    public void setState(String state) 
        this.state = state;
    

    //将状态存入备忘录
    public Memento saveStateToMemento()
        return new Memento(state);
    

    //从备忘录获取状态
    public void getStateFromMemento(Memento memento)
        state = memento.getState();
    

定义一个管理类,用于存储和恢复备忘录中的状态。

public class CareTaker 
    //备份多个状态
    private List<Memento> mementoList = new ArrayList<Memento>();
    
    public void add(Memento memento)
        mementoList.add(memento);
    

    public Memento get(int index)
        return mementoList.get(index);
    

客户端这样使用

public class MementoPatternDemo 

    public static void main(String[] args)
        GameRole gameRole = new GameRole();
        CareTaker careTaker = new CareTaker();

        gameRole.setState("State #1");
        gameRole.setState("State #2");

        careTaker.add(gameRole.saveStateToMemento());

        gameRole.setState("State #3");
        careTaker.add(gameRole.saveStateToMemento());

        gameRole.setState("State #4");
        System.out.println("Current State: " + gameRole.getState());

        gameRole.getStateFromMemento(careTaker.get(0));
        System.out.println("First saved State: " + gameRole.getState());

        gameRole.getStateFromMemento(careTaker.get(1));
        System.out.println("Second saved State: " + gameRole.getState());
    

总结

备忘录模式是行为模式之一,比较适用于功能比较复杂的,但需要维护或记录属性历史的类,或者众多属性中的一小部分。也是日常使用频率比较高的一种设计模式。

本文参考  https://www.tutorialspoint.com/design_pattern/memento_pattern.htm

1. Java并发编程那些事儿(十)——最后的总结

2. 程序员应该掌握的常用网络问题定位工具

3. Awk这件上古神兵你会用了吗

4. 手把手教你搭建一套ELK日志搜索运维平台

·END·
 

设计模式系列 - 行为型模式(下)

行为设计模式是识别对象之间的通信模式,行为模式涉及对象之间的责任分配,或者,将行为封装在对象中并将请求委托给它,也就是对象之间的关系。

涉及:
* 状态模式
中介模式
* 观察者模式
备忘录模式
迭代器模式
命令模式
* 策略模式
* 模板模式
* 访客模式示例
责任链模式

观察者模式

根据GoF定义,observer模式定义了对象之间的一对多依赖关系,当一个对象改变状态时,它的所有依赖关系都会被自动通知和更新。它也被称为发布-订阅模式。
在观察者模式中,有许多观察者(订阅者对象)正在观察特定的主题(发布者对象)。观察者向一个主题注册,以便在该主题内部发生更改时获得通知。
观察者对象可以在任何时间点注册或从主体注销。它有助于使对象对象松散耦合。
关键代码:在抽象类里有一个 ArrayList 存放观察者们

技术图片

Subject.java
public interface Subject 
{
    public void attach(Observer o);
    public void detach(Observer o);
    public void notifyUpdate(Message m);
}

MessagePublisher.java
import java.util.ArrayList;
import java.util.List;
 
public class MessagePublisher implements Subject {
     
    private List<Observer> observers = new ArrayList<>();
 
    @Override
    public void attach(Observer o) {
        observers.add(o);
    }
 
    @Override
    public void detach(Observer o) {
        observers.remove(o);
    }
 
    @Override
    public void notifyUpdate(Message m) {
        for(Observer o: observers) {
            o.update(m);
        }
    }
}
//Observer and ConcreteObservers
Observer.java
public interface Observer 
{
    public void update(Message m);
}
MessageSubscriberOne.java
public class MessageSubscriberOne implements Observer 
{
    @Override
    public void update(Message m) {
        System.out.println("MessageSubscriberOne :: " + m.getMessageContent());
    }
}
MessageSubscriberTwo.java
public class MessageSubscriberTwo implements Observer 
{
    @Override
    public void update(Message m) {
        System.out.println("MessageSubscriberTwo :: " + m.getMessageContent());
    }
}
MessageSubscriberThree.java
public class MessageSubscriberThree implements Observer 
{
    @Override
    public void update(Message m) {
        System.out.println("MessageSubscriberThree :: " + m.getMessageContent());
    }
}
//State object
//This must be an immutable object so that no class can modify it’s content by mistake.

Message.java
public class Message 
{
    final String messageContent;
     
    public Message (String m) {
        this.messageContent = m;
    }
 
    public String getMessageContent() {
        return messageContent;
    }
}
//Now test the communication between publisher and subscribers.
Main.java
public class Main 
{
    public static void main(String[] args) 
    {
        MessageSubscriberOne s1 = new MessageSubscriberOne();
        MessageSubscriberTwo s2 = new MessageSubscriberTwo();
        MessageSubscriberThree s3 = new MessageSubscriberThree();
         
        MessagePublisher p = new MessagePublisher();
         
        p.attach(s1);
        p.attach(s2);
         
        p.notifyUpdate(new Message("First Message"));   //s1 and s2 will receive the update
         
        p.detach(s1);
        p.attach(s3);
         
        p.notifyUpdate(new Message("Second Message")); //s2 and s3 will receive the update
    }
}
Program output:
MessageSubscriberOne :: First Message
MessageSubscriberTwo :: First Message
MessageSubscriberTwo :: Second Message
MessageSubscriberThree :: Second Message


状态模式

根据GoF的定义,状态允许对象在其内部状态改变时改变其行为。对象的每个可能状态都应有一个单独的具体类。每个具体的状态对象都有接受或拒绝状态转换请求的逻辑,该请求基于其当前状态和作为方法参数传递给它的上下文信息。
当我们处理的对象在其生命周期中处于不同的状态,以及它如何基于其当前状态处理传入请求(或进行状态转换)时,我们都可以使用状态模式。
如果我们在这种情况下不使用状态模式,我们最终会得到很多If-else语句,这些语句会使代码基变得难看、不必要的复杂和难以维护。

技术图片

PackageState.java
public interface PackageState 
{
    public void updateState(DeliveryContext ctx);
}
Acknowledged.java
public class Acknowledged implements PackageState 
{
    //Singleton
    private static Acknowledged instance = new Acknowledged();
 
    private Acknowledged() {}
 
    public static Acknowledged instance() {
        return instance;
    }
     
    //Business logic and state transition
    @Override
    public void updateState(DeliveryContext ctx) 
    {
        System.out.println("Package is acknowledged !!");
        ctx.setCurrentState(Shipped.instance());
    }
}
Shipped.java
public class Shipped implements PackageState 
{
    //Singleton
    private static Shipped instance = new Shipped();
 
    private Shipped() {}
 
    public static Shipped instance() {
        return instance;
    }
     
    //Business logic and state transition
    @Override
    public void updateState(DeliveryContext ctx) 
    {
        System.out.println("Package is shipped !!");
        ctx.setCurrentState(InTransition.instance());
    }
}
InTransition.java
public class InTransition implements PackageState 
{
    //Singleton
    private static InTransition instance = new InTransition();
 
    private InTransition() {}
 
    public static InTransition instance() {
        return instance;
    }
     
    //Business logic and state transition
    @Override
    public void updateState(DeliveryContext ctx) 
    {
        System.out.println("Package is in transition !!");
        ctx.setCurrentState(OutForDelivery.instance());
    }
}
OutForDelivery.java
public class OutForDelivery implements PackageState 
{
    //Singleton
    private static OutForDelivery instance = new OutForDelivery();
 
    private OutForDelivery() {}
 
    public static OutForDelivery instance() {
        return instance;
    }
     
    //Business logic and state transition
    @Override
    public void updateState(DeliveryContext ctx) 
    {
        System.out.println("Package is out of delivery !!");
        ctx.setCurrentState(Delivered.instance());
    }
}
Delivered.java
public class Delivered implements PackageState 
{
    //Singleton
    private static Deliveredinstance = new Delivered();
 
    private Delivered() {}
 
    public static Deliveredinstance() {
        return instance;
    }
     
    //Business logic
    @Override
    public void updateState(DeliveryContext ctx) 
    {
        System.out.println("Package is delivered!!");
    }
}

DeliveryContext.java
public class DeliveryContext {
     
    private PackageState currentState;
    private String packageId;
     
    public DeliveryContext(PackageState currentState, String packageId) 
    {
        super();
        this.currentState = currentState;
        this.packageId = packageId;
         
        if(currentState == null) {
            this.currentState = Acknowledged.instance();
        }
    }
 
    public PackageState getCurrentState() {
        return currentState;
    }
 
    public void setCurrentState(PackageState currentState) {
        this.currentState = currentState;
    }
     
    public String getPackageId() {
        return packageId;
    }
 
    public void setPackageId(String packageId) {
        this.packageId = packageId;
    }
 
    public void update() {
        currentState.updateState(this);
    }
}
Now test the code.

Main.java
public class Main 
{
    public static void main(String[] args) 
    {
        DeliveryContext ctx = new DeliveryContext(null, "Test123");
         
        ctx.update();
        ctx.update();
        ctx.update();
        ctx.update();
        ctx.update();
    }
}
Program Output:

Package is acknowledged !!
Package is shipped !!
Package is in transition !!
Package is out of delivery !!
Package is delivered !!

 

策略模式

我们在运行时从多个其他实现中为同一任务选择算法或任务的特定实现。重要的一点是,这些实现是可互换的——基于任务,可以在不干扰应用程序工作流的情况下选择实现。
策略模式包括从其宿主类中移除算法并将其放在单独的类中,以便在相同的编程上下文中可能有不同的算法(即策略),这些算法可以在运行时选择。
策略模式允许客户机代码从一系列相关但不同的算法中进行选择,并提供了一种在运行时根据客户机上下文选择任何算法的简单方法。

关键点:典型的面向接口编程。

技术图片

ISocialMediaStrategy.java

public interface ISocialMediaStrategy 
{
    public void connectTo(String friendName);
}
SocialMediaContext.java

public class SocialMediaContext 
{
    ISocialMediaStrategy smStrategy;
 
    public void setSocialmediaStrategy(ISocialMediaStrategy smStrategy) 
    {
        this.smStrategy = smStrategy;
    }
 
    public void connect(String name) 
    {
        smStrategy.connectTo(name);
    }
}
FacebookStrategy.java
public class FacebookStrategy implements ISocialMediaStrategy {
 
    public void connectTo(String friendName) 
    {
        System.out.println("Connecting with " + friendName + " through Facebook");
    }
}
GooglePlusStrategy.java

public class GooglePlusStrategy implements ISocialMediaStrategy {
 
    public void connectTo(String friendName) 
    {
        System.out.println("Connecting with " + friendName + " through GooglePlus");
    }
}
TwitterStrategy.java
public class TwitterStrategy implements ISocialMediaStrategy {
 
    public void connectTo(String friendName) 
    {
        System.out.println("Connecting with " + friendName + " through Twitter");
    }
}
OrkutStrategy.java
public class OrkutStrategy implements ISocialMediaStrategy {
 
    public void connectTo(String friendName) 
    {
        System.out.println("Connecting with " + friendName + " through Orkut [not possible though :)]");
    }
}

//Demo
//Now see how these strategies can be used in runtime. 
public class Demo {
    public static void main(String[] args) {
 
        // Creating social Media Connect Object for connecting with friend by
        // any social media strategy.
        SocialMediaContext context = new SocialMediaContext();
 
        // Setting Facebook strategy.
        context.setSocialmediaStrategy(new FacebookStrategy());
        context.connect("Lokesh");
 
        System.out.println("====================");
 
        // Setting Twitter strategy.
        context.setSocialmediaStrategy(new TwitterStrategy());
        context.connect("Lokesh");
 
        System.out.println("====================");
 
        // Setting GooglePlus strategy.
        context.setSocialmediaStrategy(new GooglePlusStrategy());
        context.connect("Lokesh");
 
        System.out.println("====================");
 
        // Setting Orkut strategy.
        context.setSocialmediaStrategy(new OrkutStrategy());
        context.connect("Lokesh");
    }
}
Output:

Connecting with Lokesh through Facebook
====================
Connecting with Lokesh through Twitter
====================
Connecting with Lokesh through GooglePlus
====================
Connecting with Lokesh through Orkut [not possible though :)]

模板模式

模板方法设计模式是一种被广泛接受的行为设计模式,用于在编程环境中实施某种算法(固定步骤集)。它定义了执行一个多步骤算法的顺序步骤,还可以提供一个默认实现.
模板法模式的适用性:
当我们有预定义的步骤来实现一些算法时。
当我们想要避免重复代码时,移动基类中的公共实现和步骤。

假设我们需要建造有特定步骤的房子。有些步骤有默认实现,有些步骤没有默认实现。
统一施工的默认实现步骤:

  • 地基施工
  • 屋顶施工

个性化施工的实现的步骤(比如针对混凝土墙、玻璃墙、砖墙等)

  • 墙体施工
  • 门窗施工
  • 绘画
  • 室内装饰

关键代码:部分步骤在抽象类实现,其他步骤在子类实现。

技术图片

House.java
public abstract class House {
    /**
     * This is the template method we are discussing. This method should be
     * final so that other class can‘t re-implement and change the order of the
     * steps.
     */
    public final void buildhouse() {
        constructBase();
        constructRoof();
        constructWalls();
        constructWindows();
        constructDoors();
        paintHouse();
        decorateHouse();
    }
 
    public abstract void decorateHouse();
 
    public abstract void paintHouse();
 
    public abstract void constructDoors();
 
    public abstract void constructWindows();
 
    public abstract void constructWalls();
 
    /**
     * final implementation of constructing roof - final as all type of house
     * Should build roof in same manner.
     */
    private final void constructRoof() {
        System.out.println("Roof has been constructed.");
    }
 
    /**
     * final implementation of constructing base - final as all type of house
     * Should build base/foundation in same manner.
     */
    private final void constructBase() {
        System.out.println("Base has been constructed.");
    }
}
ConcreteWallHouse.java
public class ConcreteWallHouse extends House {
      @Override
      public void decorateHouse() {
            System.out.println(“Decorating Concrete Wall House”);
      }
      @Override
      public void paintHouse() {
            System.out.println(“Painting Concrete Wall House”);
      }
      @Override
      public void constructDoors() {
            System.out.println(“Constructing Doors for Concrete Wall House”);
      }
      @Override
      public void constructWindows() {
            System.out.println(“Constructing Windows for Concrete Wall House”);
      }
      @Override
      public void constructWalls() {
            System.out.println(“Constructing Concrete Wall for my House”);
      }
}
GlassWallHouse.java
public class GlassWallHouse extends House {
    @Override
    public void decorateHouse() {
        System.out.println("Decorating Glass Wall House");
    }
 
    @Override
    public void paintHouse() {
        System.out.println("Painting Glass Wall House");
    }
 
    @Override
    public void constructDoors() {
        System.out.println("Constructing Doors for Glass Wall House");
    }
 
    @Override
    public void constructWindows() {
        System.out.println("Constructing Windows for Glass Wall House");
    }
 
    @Override
    public void constructWalls() {
        System.out.println("Constructing Glass Wall for my House");
    }
}
Demo
public class Demo {
      public static void main(String[] args) {
 
            System.out.println(“Going to build Concrete Wall House”);
 
            House house = new ConcreteWallHouse();
            house.buildhouse();
 
            System.out.println(“Concrete Wall House constructed successfully”);
 
            System.out.println(“********************”);
 
            System.out.println(“Going to build Glass Wall House”);
 
            house = new GlassWallHouse();
            house.buildhouse();
 
            System.out.println(“Glass Wall House constructed successfully”);
      }
}
Output:

Going to build Concrete Wall House
Base has been constructed.
Roof has been constructed.
Constructing Concrete Wall for my House
Constructing Windows for Concrete Wall House
Constructing Doors for Concrete Wall House
Painting Concrete Wall House
Decorating Concrete Wall House
Concrete Wall House constructed successfully

********************

Going to build Glass Wall House
Base has been constructed.
Roof has been constructed.
Constructing Glass Wall for my House
Constructing Windows for Glass Wall House
Constructing Doors for Glass Wall House
Painting Glass Wall House
Decorating Glass Wall House
Glass Wall House constructed successfully

 

访问者模式

访问者设计模式是一种将算法与其操作的对象结构分离的方法。这种分离的一个实际结果是能够在不修改现有对象结构的情况下向现有对象结构添加新操作。这是遵循开放/封闭原则(实体设计原则之一)的一种方法。
上述设计灵活性允许向任何对象层次结构添加方法,而无需修改为层次结构编写的代码。更确切地说,使用双调度机制来实现此功能。Double dispatch是一种特殊的机制,它根据调用中涉及的两个对象的运行时类型,将函数调用分派给不同的具体函数。

关键代码:在数据基础类里面有一个方法接受访问者,将自身引用传入访问者。

技术图片

Router.java
public interface Router 
{
    public void sendData(char[] data);
    public void acceptData(char[] data);
     
    public void accept(RouterVisitor v);
}
DLinkRouter.java
public class DLinkRouter implements Router{
 
    @Override
    public void sendData(char[] data) {
    }
 
    @Override
    public void acceptData(char[] data) {
    }
 
    @Override
    public void accept(RouterVisitor v) {
        v.visit(this);
    }
}
LinkSysRouter.java
public class LinkSysRouter implements Router{
 
    @Override
    public void sendData(char[] data) {
    }
 
    @Override
    public void acceptData(char[] data) {
    }
     
    @Override
    public void accept(RouterVisitor v) {
        v.visit(this);
    }
}
TPLinkRouter.java
public class TPLinkRouter implements Router{
 
    @Override
    public void sendData(char[] data) {
    }
 
    @Override
    public void acceptData(char[] data) {
    }
     
    @Override
    public void accept(RouterVisitor v) {
        v.visit(this);
    }
}
RouterVisitor.java
public interface RouterVisitor {
    public void visit(DLinkRouter router);
    public void visit(TPLinkRouter router);
    public void visit(LinkSysRouter router);
}
LinuxConfigurator.java
public class LinuxConfigurator implements RouterVisitor{
 
    @Override
    public void visit(DLinkRouter router) {
        System.out.println("DLinkRouter Configuration for Linux complete !!");
    }
 
    @Override
    public void visit(TPLinkRouter router) {
        System.out.println("TPLinkRouter Configuration for Linux complete !!");
    }
 
    @Override
    public void visit(LinkSysRouter router) {
        System.out.println("LinkSysRouter Configuration for Linux complete !!");
    }
}
MacConfigurator.java
public class MacConfigurator implements RouterVisitor{
 
    @Override
    public void visit(DLinkRouter router) {
        System.out.println("DLinkRouter Configuration for Mac complete !!");
    }
 
    @Override
    public void visit(TPLinkRouter router) {
        System.out.println("TPLinkRouter Configuration for Mac complete !!");
    }
 
    @Override
    public void visit(LinkSysRouter router) {
        System.out.println("LinkSysRouter Configuration for Mac complete !!");
    }
}

TestVisitorPattern.java
public class TestVisitorPattern extends TestCase
{
    private MacConfigurator macConfigurator;
    private LinuxConfigurator linuxConfigurator;
    private DLinkRouter dlink;
    private TPLinkRouter tplink;
    private LinkSysRouter linksys;
     
    public void setUp()
    {
        macConfigurator = new MacConfigurator();
        linuxConfigurator = new LinuxConfigurator();
         
        dlink = new DLinkRouter();
        tplink = new TPLinkRouter();
        linksys = new LinkSysRouter();
    }
     
    public void testDlink()
    {
        dlink.accept(macConfigurator);
        dlink.accept(linuxConfigurator);
    }
     
    public void testTPLink()
    {
        tplink.accept(macConfigurator);
        tplink.accept(linuxConfigurator);
    }
     
    public void testLinkSys()
    {
        linksys.accept(macConfigurator);
        linksys.accept(linuxConfigurator);
    }
}
 
Output:
 
DLinkRouter Configuration for Mac complete !!
DLinkRouter Configuration for Linux complete !!
LinkSysRouter Configuration for Mac complete !!
LinkSysRouter Configuration for Linux complete !!
TPLinkRouter Configuration for Mac complete !!
TPLinkRouter Configuration for Linux complete !!

 

以上是关于设计模式系列——备忘录模式的主要内容,如果未能解决你的问题,请参考以下文章

设计模式系列 - 行为型模式(下)

Mac 神兵利器 极简软件清单

手撸golang 行为型设计模式 备忘录模式

设计模式备忘录模式

撤销和重做实现-第三部分(备忘录模式)

撤销和重做实现-第三部分(备忘录模式)