Android进阶项目踩坑Android开发

编程中的“后悔药”——备忘录模式

2019-03-08  本文已影响2人  程序员丶星霖

《Android源码设计模式解析与实战》读书笔记(十三)
《Android源码设计模式解析与实战》PDF资料下载

一、备忘录模式简介

备忘录模式是一种行为模式,该模式用于保存对象当前状态,并且在之后可以再次恢复到此状态。备忘录模式实现的方式需要保证被保存的对象状态不能被对象从外部访问,目的是为了保护好被保存的这些对象状态的完整性以及内部实现不向外暴露。

1.1、定义

在不破坏封闭的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样,以后就可将该对象恢复到原先保存的状态。

1.2、使用场景

  1. 需要保存一个对象在某一时刻的状态或部分状态。
  2. 如果用一个接口来让其他对象得到这些状态,将会暴露对象的实现细节并破坏对象的封装性,一个对象不希望外界直接访问其内部状态,通过中间对象可以间接访问其内部状态。

二、备忘录模式的简单示例

/**
 * 备忘录类
 */
public class Memoto {
    public int mCheckpoint;
    public int mLifeValue;
    public String mWeapon;

    @Override
    public String toString() {
        return "Memoto{" +
                "mCheckpoint=" + mCheckpoint +
                ", mLifeValue=" + mLifeValue +
                ", mWeapon='" + mWeapon + '\'' +
                '}';
    }
}
/**
 * 使命召唤游戏(数据模型可能不太合理,只做简单演示)
 */
public class CallOfDuty {
    private int mCheckpoint = 1;
    private int mLifeValue = 100;
    private String mWeapon = "沙漠之鹰";

    //玩游戏
    public void play() {
        System.out.println("玩游戏:" + String.format("第%d关", mCheckpoint) + "奋战杀敌中");
        mLifeValue -= 10;
        System.out.println("进度升级了");
        mCheckpoint++;
        System.out.println("到达" + String.format("第%d关",mCheckpoint));
    }

    //退出游戏
    public void quit() {
        System.out.println("------------------------");
        System.out.println("退出前的游戏属性:"+this.toString());
        System.out.println("退出游戏");
        System.out.println("------------------------");
    }

    /**
     * 创建备忘录
     */
    public Memoto createMemoto() {
        Memoto memoto = new Memoto();
        memoto.mCheckpoint = mCheckpoint;
        memoto.mLifeValue = mLifeValue;
        memoto.mWeapon = mWeapon;
        return memoto;
    }

    //恢复游戏
    public void restore(Memoto memoto) {
        this.mCheckpoint = memoto.mCheckpoint;
        this.mLifeValue = memoto.mLifeValue;
        this.mWeapon = memoto.mWeapon;
        System.out.println("恢复后的游戏属性:" + this.toString());
    }

    @Override
    public String toString() {
        return "CallOfDuty{" +
                "mCheckpoint=" + mCheckpoint +
                ", mLifeValue=" + mLifeValue +
                ", mWeapon='" + mWeapon + '\'' +
                '}';
    }
}
/**
 * Caretaker,负责管理Memoto
 */
public class Caretaker {
    Memoto mMemoto;//备忘录

    /**
     * 存档
     */
    public void archive(Memoto memoto) {
        this.mMemoto = memoto;
    }

    /**
     * 获取存档
     */
    public Memoto getmMemoto() {
        return mMemoto;
    }
}
public class Client {
    public static void main(String[] args) {
        //构建游戏对象
        CallOfDuty game = new CallOfDuty();
        //1.打游戏
        game.play();

        Caretaker caretaker = new Caretaker();
        //2.游戏存档
        caretaker.archive(game.createMemoto());
        //3.退出游戏
        game.quit();
        //4.恢复游戏
        CallOfDuty newGame = new CallOfDuty();
        newGame.restore(caretaker.getmMemoto());
    }
}

输出结果:

备忘录模式.png

三、备忘录模式实战

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    >

    <EditText
        android:id="@+id/note_edittext"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:hint="@string/write"
        android:gravity="left"/>
    
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="20dp"
        android:paddingLeft="50dp"
        android:paddingRight="50dp">

        <ImageView
            android:id="@+id/undo_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:src="@mipmap/undo"/>

        <TextView
            android:id="@+id/save_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="保存"
            android:textSize="20sp"
            android:textColor="@color/black"/>

        <ImageView
            android:id="@+id/redo_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:src="@mipmap/redo"/>


    </RelativeLayout>

</LinearLayout>

第一版代码:

public class MainActivity extends AppCompatActivity {
    EditText mNodeEditText;//编辑器
    TextView mSaveTv;//保存按钮
    ImageView mUndoBtn;//撤销按钮
    ImageView mRedoBtn;//重做按钮

    //最大存储数量
    private static final int MAX = 30;
    //存储30条记录
    List<Memoto> mMemotos = new ArrayList<Memoto>(MAX);

    int mIndex = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //初始化视图
        initViews();
    }

    private void initViews() {
        mNodeEditText = (EditText) findViewById(R.id.note_edittext);
        mUndoBtn = (ImageView) findViewById(R.id.undo_btn);
        mUndoBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //返回上一个记录点
                restoreEditText(getPrevMemoto());
                makeToast("撤销:");
            }
        });
        mRedoBtn = (ImageView) findViewById(R.id.redo_btn);
        mRedoBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //恢复状态,恢复到下一个记录点
                restoreEditText(getNextMemoto());
                makeToast("重做:");
            }
        });
        mSaveTv = (TextView) findViewById(R.id.save_btn);
        mSaveTv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                saveMemoto(createMemotoForEditText());
                makeToast("保存笔记:");
            }
        });
    }

    private void makeToast(String msgPrex) {
        Toast.makeText(this, msgPrex + mNodeEditText.getText() + ",光标位置:" + mNodeEditText.getSelectionStart(), Toast.LENGTH_SHORT).show();
    }

    /**
     * 保存备忘录
     */
    public void saveMemoto(Memoto memoto) {
        if (mMemotos.size() > MAX) {
            mMemotos.remove(0);
        }
        mMemotos.add(memoto);
        mIndex = mMemotos.size() - 1;
    }

    //获取上一个存档,相当于撤销功能
    public Memoto getPrevMemoto() {
        mIndex = mIndex > 0 ? --mIndex : mIndex;
        return mMemotos.get(mIndex);
    }

    //获取下一个存档,相当于重做
    public Memoto getNextMemoto() {
        mIndex = mIndex < mMemotos.size() - 1 ? ++mIndex : mIndex;
        return mMemotos.get(mIndex);
    }

    //为编辑器创建Memoto对象
    private Memoto createMemotoForEditText() {
        Memoto memoto = new Memoto();
        memoto.text = mNodeEditText.getText().toString();
        memoto.cursor = mNodeEditText.getSelectionStart();
        return memoto;
    }

    //恢复编辑器状态
    private void restoreEditText(Memoto memoto) {
        mNodeEditText.setText(memoto.text);
        mNodeEditText.setSelection(memoto.cursor);
    }
}
/**
 * 存储EditText的文本与光标位置
 */
public class Memoto {
    public String text;
    public int cursor;
}

重构后:

//负责管理Memoto对象
public class NoteCaretaker {
    //最大存储数量
    private static final int MAX = 30;
    //存储30条记录
    List<Memoto> mMemotos = new ArrayList<>(MAX);

    int mIndex = 0;

    /**
     * 保存备忘录
     */
    public void saveMemoto(Memoto memoto) {
        if (mMemotos.size() > MAX) {
            mMemotos.remove(0);
        }
        mMemotos.add(memoto);
        mIndex = mMemotos.size() - 1;
    }

    //获取上一个存档,相当于撤销功能
    public Memoto getPrevMemoto() {
        mIndex = mIndex > 0 ? --mIndex : mIndex;
        return mMemotos.get(mIndex);
    }

    //获取下一个存档,相当于重做
    public Memoto getNextMemoto() {
        mIndex = mIndex < mMemotos.size() - 1 ? ++mIndex : mIndex;
        return mMemotos.get(mIndex);
    }
}
public class NoteEditText extends android.support.v7.widget.AppCompatEditText {

    public NoteEditText(Context context) {
        super(context);
    }

    public NoteEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public NoteEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    //为编辑器创建Memoto对象
    public Memoto createMemoto() {
        Memoto memoto = new Memoto();
        memoto.text =getText().toString();
        memoto.cursor = getSelectionStart();
        return memoto;
    }

    //恢复编辑器状态
    public void restore(Memoto memoto) {
        setText(memoto.text);
        setSelection(memoto.cursor);
    }
}
public class MainActivity extends AppCompatActivity {
    NoteEditText mNodeEditText;//编辑器
    TextView mSaveTv;//保存按钮
    ImageView mUndoBtn;//撤销按钮
    ImageView mRedoBtn;//重做按钮
    //note备忘录管理器
    NoteCaretaker mCaretaker = new NoteCaretaker();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //初始化视图
        initViews();
    }

    private void initViews() {
        mNodeEditText = (NoteEditText) findViewById(R.id.note_edittext);
        mUndoBtn = (ImageView) findViewById(R.id.undo_btn);
        mUndoBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //返回上一个记录点
                mNodeEditText.restore(mCaretaker.getPrevMemoto());
                makeToast("撤销:");
            }
        });
        mRedoBtn = (ImageView) findViewById(R.id.redo_btn);
        mRedoBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //恢复状态,恢复到下一个记录点
                mNodeEditText.restore(mCaretaker.getNextMemoto());
                makeToast("重做:");
            }
        });
        mSaveTv = (TextView) findViewById(R.id.save_btn);
        mSaveTv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mCaretaker.saveMemoto(mNodeEditText.createMemoto());
                makeToast("保存笔记:");
            }
        });
    }

    private void makeToast(String msgPrex) {
        Toast.makeText(this, msgPrex + mNodeEditText.getText() + ",光标位置:" + mNodeEditText.getSelectionStart(), Toast.LENGTH_SHORT).show();
    }
}

从上述程序代码中可以看到代码量少了很多,也简单了很多,职责更为单一、明确。

重构不是到了某一个阶段才应该实施,而是伴随着开发存在的,当觉得某个类甚至某个函数过于混乱、背负的职责太多时,就要进行重构。

四、总结

备忘录模式是在不破坏封装的条件下,通过备忘录对象(Memoto)存储另外一个对象内部状态的快照,在将来合适的时候把这个对象还原到存储起来的状态。

4.1、优点

4.2、缺点

学海无涯苦作舟

我的微信公众号
上一篇 下一篇

猜你喜欢

热点阅读