命令模式

2018-04-10  本文已影响0人  joychic

定义

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

理解

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

模式结构

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

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

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

实现

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处理一些自己的逻辑。

小结

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

上一篇 下一篇

猜你喜欢

热点阅读