命令模式

命令模式

定義

將一個請求封裝成一個對象,從而讓用戶使用不一樣的請求把客戶端參數化;對請求排隊或者記錄請求日誌,以及支持可撤銷的操做。canvas

理解

命令模式的本質是對命令進行封裝,將**發出命令的責任執行命令的責任**分割開。 請求的一方發出請求,要求執行一個操做; 接收的一方收到請求,並執行操做; 請求的一方沒必要知道接收請求一方的接口,更沒必要知道請求是怎麼被接收、操做是否被執行、什麼時候執行以及如何執行的; 請求自己也是一個對象,這個對象和其餘對象同樣能夠被存儲和傳遞。bash

模式結構

  • Command 抽象命令類

定義因此具體命令類的抽象接口ide

  • ConcreteCommand 具體命令類測試

    實現抽象命令類的接口,實現執行方法excute()來調用接收者對象的相應操做ui

  • Invoker 調用者this

請求的發送者,它經過命令對象來執行請求,它與抽象命令類之間存在關聯關係。在程序運行時,將調用具體命令對象的excute()方法,間接調用接收者的相關操做spa

  • Receiver 接收者

負責具體實現或實施一個請求,是執行具體邏輯的角色日誌

  • Client 客戶類code

    在客戶類中須要建立發送者對象和具體命令類對象,在建立具體命令對象時指定其對應的接收者,發送者和接收者之間經過具體命令對象實現間接調用orm

實現

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處理一些本身的邏輯。

小結

  • 命令模式將請求的發送與執行解耦,更弱的耦合性,更靈活的控制性以及更好的拓展性
  • 能夠在不一樣時刻指定、排列和執行請求
  • 一個命令對象能夠有與初始請求無關的生存期
  • 能夠支持取消、回退、前進等操做
  • 支持修改日誌功能,這樣當面臨系統崩潰時,這些修改能夠被重作一遍
  • 支持事物操做

命令模式基本上能夠運用在任何地方,可是在實際開發中,需不須要或者說值不值得使用命令模式須要斟酌,由於這務必會涉及到大量類的建立

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息