美文网首页
命令模式

命令模式

作者: joychic | 来源:发表于2018-04-10 22:09 被阅读0次

定义

将一个请求封装成一个对象,从而让用户使用不同的请求把客户端参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。

理解

命令模式的本质是对命令进行封装,将 发出命令的责任执行命令的责任 分割开。
请求的一方发出请求,要求执行一个操作;
接收的一方收到请求,并执行操作;
请求的一方不必知道接收请求一方的接口,更不必知道请求是怎么被接收、操作是否被执行、何时执行以及如何执行的;
请求本身也是一个对象,这个对象和其他对象一样可以被存储和传递。

模式结构

  • Command 抽象命令类

定义所以具体命令类的抽象接口

  • ConcreteCommand 具体命令类

    实现抽象命令类的接口,实现执行方法excute()来调用接收者对象的相应操作

  • Invoker 调用者

请求的发送者,它通过命令对象来执行请求,它与抽象命令类之间存在关联关系。在程序运行时,将调用具体命令对象的excute()方法,间接调用接收者的相关操作

  • Receiver 接收者

负责具体实现或实施一个请求,是执行具体逻辑的角色

  • Client 客户类

    在客户类中需要创建发送者对象和具体命令类对象,在创建具体命令对象时指定其对应的接收者,发送者和接收者之间通过具体命令对象实现间接调用

实现

public interface IBrush {

  /**
   * 触点接触时
   *
   * @param path 路径对象
   * @param x 当前位置的x坐标
   * @param y 当前位置的y坐标
   */
  void down(Path path, float x, float y);

  /**
   * 触点移动时
   *
   * @param path 路径对象
   * @param x 当前位置的x坐标
   * @param y 当前位置的y坐标
   */
  void move(Path path, float x, float y);

  /**
   * 触点离开时
   *
   * @param path 路径对象
   * @param x 当前位置的x坐标
   * @param y 当前位置的y坐标
   */
  void up(Path path, float x, float y);
}

抽象笔触 抽象接收者 执行命令的抽象

public class CircleBrush implements IBrush {
  @Override public void down(Path path, float x, float y) {

  }

  @Override public void move(Path path, float x, float y) {
    path.addCircle(x, y, 10, Path.Direction.CW);
  }

  @Override public void up(Path path, float x, float y) {

  }
}

圆点线条 具体接收者

public class NormalBrush implements IBrush {
  @Override public void down(Path path, float x, float y) {
    path.moveTo(x, y);
  }

  @Override public void move(Path path, float x, float y) {
    path.lineTo(x, y);
  }

  @Override public void up(Path path, float x, float y) {

  }
}

普通线条 具体接收者

public interface IDraw {

  /**
   * 绘制命令
   *
   * @param canvas 画笔对象
   */
  void draw(Canvas canvas);

  /**
   * 撤销命令
   */
  void undo();
}

抽象命令类

public class DrawPath implements IDraw {

  public Path path;
  public Paint paint;

  @Override public void draw(Canvas canvas) {
    canvas.drawPath(path, paint);
  }

  @Override public void undo() {

  }
}

绘制路径的方法 具体命令类

public class DrawInvoker {

  /**
   * 绘制列表
   */
  private List<DrawPath> drawList = Collections.synchronizedList(new ArrayList<DrawPath>());

  /**
   * 重做列表
   */
  private List<DrawPath> redoList = Collections.synchronizedList(new ArrayList<DrawPath>());

  /**
   * 增加一个命令  设值注入形式
   *
   * @parm command  具体命令类对象
   */
  public void add(DrawPath command) {
    redoList.clear();
    drawList.add(command);
  }

  /**
   * 撤销上一步的操作
   */
  public void undo() {
    if (drawList.size() > 0) {
      DrawPath undo = drawList.get(drawList.size() - 1);
      drawList.remove(drawList.size() - 1);
      undo.undo();
      redoList.add(undo);
    }
  }

  /**
   * 恢复撤销的操作
   */
  public void redo() {
    if (redoList.size() > 0) {
      DrawPath redoCommand = redoList.get(redoList.size() - 1);
      redoList.remove(redoList.size() - 1);
      drawList.add(redoCommand);
    }
  }

  /**
   * 执行命令
   * 业务方法  调用命令类的draw()方法
   */
  public void execute(Canvas canvas) {
    if (drawList != null) {
      for (DrawPath tmp : drawList) {
        tmp.draw(canvas);
      }
    }
  }

  /**
   * 判断是否可以重做
   */
  public boolean canRedo() {
    return redoList.size() > 0;
  }

  /**
   * 判断是否可以撤销
   */
  public boolean canUndo() {
    return drawList.size() > 0;
  }
}

调用者类,对绘制命令的进一步封装,实现撤销和重做方法

public class DrawCanvas extends SurfaceView implements SurfaceHolder.Callback {

  public boolean isDrawing, isRunning;

  private Bitmap mBitmap;
  private DrawInvoker mInvoker;
  private DrawThread mDrawThread;

  public DrawCanvas(Context context, AttributeSet attrs) {
    super(context, attrs);

    mInvoker = new DrawInvoker();
    mDrawThread = new DrawThread();

    getHolder().addCallback(this);
  }

  @Override public void surfaceCreated(SurfaceHolder holder) {
    isRunning = true;
    mDrawThread.start();
  }

  @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
  }

  @Override public void surfaceDestroyed(SurfaceHolder holder) {
    boolean retry = true;
    isRunning = false;
    while (retry) {
      try {
        mDrawThread.join();
        retry = false;
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }

  /**
   * 增加绘制路径
   */
  public void add(DrawPath path) {
    mInvoker.add(path);
  }

  public void redo() {
    isDrawing = true;
    mInvoker.redo();
  }

  public void undo() {
    isDrawing = true;
    mInvoker.undo();
  }

  public boolean canRedo() {
    return mInvoker.canRedo();
  }

  public boolean canUndo() {
    return mInvoker.canUndo();
  }

  private class DrawThread extends Thread {
    @Override public void run() {
      Canvas canvas = null;
      while (isRunning) {
        if (isDrawing) {
          try {
            canvas = getHolder().lockCanvas(null);
            if (mBitmap == null) {
              mBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
            }
            Canvas c = new Canvas(mBitmap);
            c.drawColor(0, PorterDuff.Mode.CLEAR);

            canvas.drawColor(0, PorterDuff.Mode.CLEAR);
            mInvoker.execute(c);
            canvas.drawBitmap(mBitmap, 0, 0, null);
          } finally {
            getHolder().unlockCanvasAndPost(canvas);
          }
          isDrawing = false;
        }
      }
    }
  }

  @Override public boolean performClick() {
    return super.performClick();
  }
}

自定义view ,承担画板功能,真正的执行者画布

public class DrawActivity extends AppCompatActivity implements View.OnClickListener {

    private DrawPath mPath;
    private DrawCanvas mCanvas;
    private Paint mPaint;
    private IBrush mBrush;

    private Button btnRedo, btnUndo;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test2);

        mPaint = new Paint();
        mPaint.setColor(0xFFFFFFFF);
        mPaint.setStrokeWidth(3);

        mBrush = new NormalBrush();

        mCanvas = findViewById(R.id.draw_canvas);
        mCanvas.setOnTouchListener(new DrawTouchListener());

        btnRedo = findViewById(R.id.redo);
        btnUndo = findViewById(R.id.undo);
        btnRedo.setEnabled(false);
        btnUndo.setEnabled(false);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.red:
                mPaint = new Paint();
                mPaint.setColor(0xFFFF0000);
                mPaint.setStrokeWidth(3);
                break;
            case R.id.green:
                mPaint = new Paint();
                mPaint.setColor(0xFF00FF00);
                mPaint.setStrokeWidth(3);
                break;
            case R.id.blue:
                mPaint = new Paint();
                mPaint.setColor(0xFF0000FF);
                mPaint.setStrokeWidth(3);
                break;
            case R.id.normal:
                mBrush = new NormalBrush();
                break;
            case R.id.circle:
                mBrush = new CircleBrush();
                break;
            case R.id.redo:
                mCanvas.redo();
                if (!mCanvas.canRedo()) {
                    btnRedo.setEnabled(false);
                }
                btnUndo.setEnabled(true);
                break;
            case R.id.undo:
                mCanvas.undo();
                if (!mCanvas.canUndo()) {
                    btnUndo.setEnabled(false);
                }
                btnRedo.setEnabled(true);
                break;
            default:
                break;
        }
    }

    private class DrawTouchListener implements View.OnTouchListener {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    mPath = new DrawPath();
                    mPath.paint = mPaint;
                    mPath.path = new Path();
                    mBrush.down(mPath.path, event.getX(), event.getY());
                    break;
                case MotionEvent.ACTION_UP:
                    mBrush.up(mPath.path, event.getX(), event.getY());
                    mCanvas.add(mPath);
                    mCanvas.isDrawing = true;
                    btnUndo.setEnabled(true);
                    btnRedo.setEnabled(false);
                    break;
                case MotionEvent.ACTION_MOVE:
                    mBrush.move(mPath.path, event.getX(), event.getY());
                    break;
                default:
                    break;
            }
            return true;
        }
    }
}

测试Activity,Client类

在ACTION_DOWN、ACTION_UP事件触发时,mBrush会执行对应的事件方法,而不用关心具体的画笔是什么样子的。如果要增加一个新的样式也只需要添加一个继承IBrush接口的具体接收者对象,修改画笔样式只需要将mBrush指向对应的具体画笔类

诸如撤销,重做,或者日志保存等等之类的操作,交由DrawCanvas去实现,Client类只需要调用对应的方法,而不需关系具体实现逻辑,在DrawCanvas中则关联了DrawInvoker对象,实现撤销和重做等方法正式有DrawInvoker正式实现的。

Client在这里只做了这件事情,1发出执行请求,2给定接收对象,3处理一些自己的逻辑。

小结

  • 命令模式将请求的发送与执行解耦,更弱的耦合性,更灵活的控制性以及更好的拓展性
  • 可以在不同时刻指定、排列和执行请求
  • 一个命令对象可以有与初始请求无关的生存期
  • 可以支持取消、回退、前进等操作
  • 支持修改日志功能,这样当面临系统崩溃时,这些修改可以被重做一遍
  • 支持事物操作

命令模式基本上可以运用在任何地方,但是在实际开发中,需不需要或者说值不值得使用命令模式需要斟酌,因为这务必会涉及到大量类的创建

相关文章

  • 12.vim的使用技巧

    命令模式 、命令行模式、编辑模式: INSERT 首次进入文件: 命令模式输入: 表示 命令行模式出现...

  • 五、Linux vim编辑器

    vim编辑器三种模式:命令模式、编辑模式、末行模式 1.命令模式: vim进入文件后就是命令模式 1.1 命令模式...

  • 终端操作文件

    VIM 模式切换从命令模式->编辑模式:i,a,o,I,A,O从编辑模式->命令模式:ESC从命令模式->末行模式...

  • vim 操作 && Mac 配置本地Apache

    一、vim 的基本命令操作 vim 的模式: 编辑模式 ,命令模式 编辑模式: 命令模式:可以通过命令 最重要的命...

  • Vim高效编辑器

    Vim程序员高效编辑器 命令模式,输入模式,底线命令模式 默认进入vim就是命令模式,输入i变为输入模式 命令模式...

  • Command模式

    命令模式(Command) 命令模式属于对象的行为模式。命令模式又称为行动(Action)模式或交易(Tran...

  • 设计模式详解——命令模式

    本篇文章介绍一种设计模式——命令模式。本篇文章内容参考《JAVA与模式》之命令模式。 一、命令模式的概念 命令模式...

  • Vi编辑器

    命令模式 文本输入模式 末行模式。 插入模式 移动光标: 删除命令: 撤销命令: 重复命令 文本行移动: 复制粘贴...

  • JavaScript设计模式七(命令模式)

    JavaScript设计模式七(命令模式) 定义: 命令模式是最简单和优雅的模式之一,命令模式中的命令指的是一个执...

  • Cisco 路由器配置命令大全(二)

    (1)模式转换命令 用户模式----特权模式,使用命令"enable"特权模式----全局配置模式,使用命令"co...

网友评论

      本文标题:命令模式

      本文链接:https://www.haomeiwen.com/subject/xllohftx.html