設計模式之命令模式(Command Pattern)

命令模式主要通過中介Command實現了發出命令者和命令的執行者,也即Invoke類和Receiver的鬆耦合。
先看類圖:
這裏寫圖片描述
將請求封裝成一個對象,從而使用戶使用不同的請求把客戶端參數化,請求隊列或者記錄日誌,以及支持可撤銷的操作。
看一個Android的例子
先定義兩種畫筆,這裏是額外的,也就是說更這個設計模式沒有太多關係,只是程序需要:

package com.example.yale.mydrawbrush.Brush;

import android.graphics.Path;

/**
 * Created by Yale on 16/7/8.
 */
//抽象筆觸
public interface IBrush {
    /**
     * 觸點接觸時
     * @param path
     * @param x
     * @param y
     */
    void down(Path path, float x, float y);

    /**
     * 觸點移動式
     * @param path
     * @param x
     * @param y
     */
    void move(Path path, float x, float y);

    /**
     * 觸點離開時
     * @param path
     * @param x
     * @param y
     */
    void up(Path path, float x, float y);
}

普通觸筆

package com.example.yale.mydrawbrush.Brush;

import android.graphics.Path;

/**
 * Created by Yale on 16/7/8.
 */
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) {

    }
}

圓形觸筆

package com.example.yale.mydrawbrush.Brush;

import android.graphics.Path;

/**
 * Created by Yale on 16/7/8.
 */
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) {

    }
}

下面關鍵來了,我們先定義抽象命令接口,也就是繪製命令接口:

package com.example.yale.mydrawbrush.Brush;

/**
 * Created by Yale on 16/7/8.
 */

import android.graphics.Canvas;

/**
 * 對於每一次路徑繪製,有兩個命令:繪製命令和撤銷命令
 */
public interface IDraw {

    /**
     * 繪製命令
     * @param canvas 畫布對象
     */
    void draw(Canvas canvas);

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

具體的繪製命令,也就是實現上面的接口

package com.example.yale.mydrawbrush.Brush;

import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;

/**
 * Created by Yale on 16/7/8.
 */
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() {

    }
}

然後是命令的發出者Invoker,定義爲DrawInvoker

package com.example.yale.mydrawbrush.Brush;

import android.graphics.Canvas;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Created by Yale on 16/7/8.
 */
public class DrawInvoker {
    //繪製列表
    private List<DrawPath> drawList = Collections.synchronizedList(new ArrayList<DrawPath>());
    //重做列表
    private List<DrawPath> redoList = Collections.synchronizedList(new ArrayList<DrawPath>());

    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);
        }
    }

    public void execute(Canvas canvas) {
        if (drawList != null) {
            for (DrawPath temp : drawList) {
                temp.draw(canvas);
            }
        }
    }

    public boolean canRedo() {
        return redoList.size() > 0;
    }

    public boolean canUndo() {
        return drawList.size() > 0;
    }

}

再定義命令的執行者,也就是Receiver,這裏定義爲DrawCanvas

package com.example.yale.mydrawbrush.Brush;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.PorterDuff;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

/**
 * Created by Yale on 16/7/8.
 */
public class DrawCanvas extends SurfaceView  implements SurfaceHolder.Callback{

    public boolean isDrawing, isRunning;

    private Bitmap mBitmap;
    private DrawInvoker mInvoker;
    private DrawThread mThread;

    public DrawCanvas(Context context, AttributeSet attrs) {
        super(context, attrs);
        mInvoker = new DrawInvoker();
        mThread = new DrawThread();
        getHolder().addCallback(this);
    }

    public void add(DrawPath path){
        mInvoker.add(path);
    }

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

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

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

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

//
    @Override
    public void surfaceCreated(SurfaceHolder surfaceHolder) {
        isRunning = true;
        mThread.start();
    }
//
    @Override
    public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
        mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
    }
//
    @Override
    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
        boolean retry = true;
        isRunning = false;
        while(retry){
            try{
                mThread.join();
                retry = false;
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }

    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;
                }
            }
        }
    }
}

最後是把上述整合

package com.example.yale.mydrawbrush;

import android.graphics.Paint;
import android.graphics.Path;
import android.os.SystemClock;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;

import com.example.yale.mydrawbrush.Brush.CircleBrush;
import com.example.yale.mydrawbrush.Brush.DrawCanvas;
import com.example.yale.mydrawbrush.Brush.DrawPath;
import com.example.yale.mydrawbrush.Brush.IBrush;
import com.example.yale.mydrawbrush.Brush.NormalBrush;

import butterknife.Bind;
import butterknife.ButterKnife;

public class MainActivity extends AppCompatActivity {

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

    @Bind(R.id.ac_draw_canvas)
    DrawCanvas mCanvas;
    @Bind(R.id.ac_draw_draw_operate_undo_btn)
    Button btnUndo;
    @Bind(R.id.ac_draw_draw_operate_redo_btn)
    Button btnRedo;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        mPaint = new Paint();
        mPaint.setColor(0XFFFFFFFF);
        mPaint.setStrokeWidth(3);

        mCanvas.setOnTouchListener(new DrawTouchListener());

        mBrush = new NormalBrush();

        btnRedo.setEnabled(false);
        btnUndo.setEnabled(false);
    }

    public void onClick(View view){
        switch (view.getId()){
            case R.id.ac_draw_color_red_btn:
                mPaint = new Paint();
                mPaint.setStrokeWidth(3);
                mPaint.setColor(0XFFFF0000);
                System.out.print("sfsfasfafd!$@#$!@#$!");
                break;
            case R.id.ac_draw_color_blue_btn:
                mPaint = new Paint();
                mPaint.setStrokeWidth(3);
                mPaint.setColor(0XFF00FF00);
                break;
            case R.id.ac_draw_color_green_btn:
                mPaint = new Paint();
                mPaint.setStrokeWidth(3);
                mPaint.setColor(0XFF0000FF);
                break;
            case R.id.ac_draw_draw_operate_undo_btn:
                mCanvas.undo();
                if(!mCanvas.canUndo()){
                    btnUndo.setEnabled(false);
                }
                btnRedo.setEnabled(true);
                break;
            case R.id.ac_draw_draw_operate_redo_btn:
                mCanvas.redo();
                if(!mCanvas.canRedo()){
                    btnUndo.setEnabled(false);
                }
                btnRedo.setEnabled(true);
                break;
            case R.id.ac_draw_operate_brush_circle_btn:
                mBrush = new CircleBrush();
                break;
            case R.id.ac_draw_operate_brush_normal_btn:
                mBrush = new NormalBrush();
                break;
            default:
                break;
        }
    }

    private class DrawTouchListener implements View.OnTouchListener{

        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {
            if(motionEvent.getAction() == MotionEvent.ACTION_DOWN){
                mPath = new DrawPath();
                mPath.paint = mPaint;
                mPath.path = new Path();
                mBrush.down(mPath.path, motionEvent.getX(), motionEvent.getY());
            }else if(motionEvent.getAction() == MotionEvent.ACTION_MOVE){
                mBrush.move(mPath.path, motionEvent.getX(), motionEvent.getY());
            }else if(motionEvent.getAction() == MotionEvent.ACTION_UP){
                mBrush.up(mPath.path, motionEvent.getX(), motionEvent.getY());
                mCanvas.add(mPath);
                mCanvas.isDrawing = true;
                btnUndo.setEnabled(true);
                btnRedo.setEnabled(false);
            }
            return true;
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章