美文网首页Android开发项目踩坑Android进阶
让程序畅通执行——命令模式

让程序畅通执行——命令模式

作者: 程序员丶星霖 | 来源:发表于2019-03-05 09:18 被阅读4次

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

    一、命令模式简介

    命令模式,是行为型设计模式。

    1.1、定义

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

    1.2、使用场景

    1. 需要抽象出待执行的动作,然后以参数的形式提供出来——类似于过程设计中的回调机制,而命令模式正是回调机制的一个面向对象的替代品。
    2. 在不同的时刻指定、排列和执行请求。一个命令对象可以有与初始请求无关的生存期。
    3. 需要支持取消操作。
    4. 支持修改日志功能,这样当系统崩溃时,这些修改可以被重做一遍。
    5. 需要支持事务操作。

    二、命令模式的简单实现

    以俄罗斯方块游戏为例:

    //接收者角色
    public class TetrisMachine {
        /**
         * 真正处理"向左"操作的逻辑代码
         */
        public void toLeft(){
            System.out.println("向左");
        }
    
        /**
         * 真正处理"向右"操作的逻辑代码
         */
        public void toRight(){
            System.out.println("向右");
        }
    
        /**
         * 真正处理"快速落下"操作的逻辑代码
         */
        public void fastToBottom() {
            System.out.println("快速落下");
        }
    
        /**
         * 真正处理"改变形状"操作的逻辑代码
         */
        public void transform() {
            System.out.println("改变形状");
        }
    }
    
    //命令者抽象
    public interface Command {
        /**
         * 命令执行方法
         */
        void execute();
    }
    
    //具体命令者,向左移的命令类
    public class LeftCommand implements Command {
        //持有一个接收者对象的引用
        private TetrisMachine machine;
    
        public LeftCommand(TetrisMachine machine) {
            this.machine = machine;
        }
    
        @Override
        public void execute() {
            //调用游离的具体方法执行操作
            machine.toLeft();
        }
    }
    
    //具体命令类,向右移的命令类
    public class RightCommand implements Command {
        //持有一个接收者对象的引用
        private TetrisMachine machine;
    
        public RightCommand(TetrisMachine machine) {
            this.machine = machine;
        }
    
        @Override
        public void execute() {
            //调用游戏里的具体方法执行操作
            machine.toRight();
        }
    }
    
    //具体命令者,快速落下的命令类
    public class FallCommand implements Command {
        //持有一个接收者对象的引用
        private TetrisMachine machine;
    
        public FallCommand(TetrisMachine machine) {
            this.machine = machine;
        }
    
        @Override
        public void execute() {
            machine.fastToBottom();
        }
    }
    
    //具体命令者,改变形状的命令类
    public class TransformCommand implements Command {
        private TetrisMachine machine;
    
        public TransformCommand(TetrisMachine machine) {
            this.machine = machine;
        }
    
        @Override
        public void execute() {
            machine.transform();
        }
    }
    
    //请求者类,命令由按钮发起
    public class Buttons {
        private LeftCommand leftCommand;//向左移动的命令对象引用
        private RightCommand rightCommand;//向右移动的命令对象引用
        private FallCommand fallCommand;//快速落下的命令对象引用
        private TransformCommand transformCommand;//变换形状的命令对象引用
    
        /**
         * 设置向左移动的命令对象
         *
         * @param leftCommand
         */
        public void setLeftCommand(LeftCommand leftCommand) {
            this.leftCommand = leftCommand;
        }
    
        /**
         * 设置向右移动的命令对象
         *
         * @param rightCommand
         */
        public void setRightCommand(RightCommand rightCommand) {
            this.rightCommand = rightCommand;
        }
    
        /**
         * 设置快速落下的命令对象
         *
         * @param fallCommand
         */
        public void setFallCommand(FallCommand fallCommand) {
            this.fallCommand = fallCommand;
        }
    
        /**
         * 设置变换形状的命令对象
         *
         * @param transformCommand
         */
        public void setTransformCommand(TransformCommand transformCommand) {
            this.transformCommand = transformCommand;
        }
    
        /**
         * 按下按钮向左移动
         */
        public void toLeft() {
            leftCommand.execute();
        }
    
        /**
         * 按下按钮向右移动
         */
        public void toRight() {
            rightCommand.execute();
        }
    
        /**
         * 按下按钮快速落下
         */
        public void fall() {
            fallCommand.execute();
        }
    
        /**
         * 按下按钮改变形状
         */
        public void transform() {
            transformCommand.execute();
        }
    }
    
    public class Player {
        public static void main(String[] args) {
            //首先要有俄罗斯方块游戏
            TetrisMachine machine = new TetrisMachine();
    
            //根据游戏构造4中命令
            LeftCommand leftCommand = new LeftCommand(machine);
            RightCommand rightCommand = new RightCommand(machine);
            FallCommand fallCommand = new FallCommand(machine);
            TransformCommand transformCommand = new TransformCommand(machine);
    
            //按钮可以执行不同的命令
            Buttons buttons = new Buttons();
            buttons.setLeftCommand(leftCommand);
            buttons.setRightCommand(rightCommand);
            buttons.setFallCommand(fallCommand);
            buttons.setTransformCommand(transformCommand);
    
            //具体按下哪个按钮玩家说了算
            buttons.toLeft();
            buttons.toRight();
            buttons.fall();
            buttons.transform();
        }
    }
    

    输出结果:

    命令模式.png

    三、命令模式实战

    //抽象笔触
    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);
    }
    
    //普通笔触
    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 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 interface IDraw {
        /**
         * 绘制命令
         * @param canvas
         */
        void draw(Canvas canvas);
    
        /**
         * 撤销命令
         */
        void undo();
    }
    
    //具体绘制路径命令
    public class DrawPath implements IDraw {
        public Path path;//需要绘制的路径
        public Paint mPaint;//绘制画笔
    
        @Override
        public void draw(Canvas canvas) {
            canvas.drawPath(path, mPaint);
        }
    
        @Override
        public void undo() {
    
        }
    }
    
    //绘制请求封装类
    public class DrawInvoker {
        //绘制列表
        private List<DrawPath> mDrawPathList = Collections.synchronizedList(new ArrayList<DrawPath>());
    
        //重做列表
        private List<DrawPath> redoList = Collections.synchronizedList(new ArrayList<DrawPath>());
    
        /**
         * 增加一个命令
         * @param command
         */
        public void add(DrawPath command) {
            redoList.clear();
            mDrawPathList.add(command);
        }
    
        /**
         * 撤销上一步的命令
         */
        public void undo() {
            if (mDrawPathList.size() > 0) {
                DrawPath undo = mDrawPathList.get(mDrawPathList.size() - 1);
                mDrawPathList.remove(mDrawPathList.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);
                mDrawPathList.add(redoCommand);
            }
        }
    
        /**
         * 执行命令
         * @param canvas
         */
        public void execute(Canvas canvas) {
            if (mDrawPathList != null) {
                for (DrawPath tmp : mDrawPathList) {
                    tmp.draw(canvas);
                }
            }
        }
    
        /**
         * 是否可以重做
         * @return
         */
        public boolean canRedo() {
            return redoList.size() > 0;
        }
    
        /**
         * 是否可以撤销
         * @return
         */
        public boolean canUndo() {
            return mDrawPathList.size() > 0;
        }
    }
    
    //绘制的真正执行者画布
    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);
        }
    
        /**
         * 增减一条绘制路径
         *
         * @param path
         */
        public void add(DrawPath path) {
            mInvoker.add(path);
        }
    
        /**
         * 重做上一步撤销的绘制
         */
        public void redo() {
            isDrawing = true;
            mInvoker.redo();
        }
    
        /**
         * 撤销上一步的绘制
         */
        public void undo() {
            isDrawing = true;
            mInvoker.undo();
        }
    
        /**
         * 是否可以撤销
         *
         * @return
         */
        public boolean canUndo() {
            return mInvoker.canUndo();
        }
    
    
        /**
         * 是否可以重做
         *
         * @return
         */
        public boolean canRedo() {
            return mInvoker.canRedo();
        }
    
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            isRunning = true;
            mThread.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 {
                    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;
                    }
                }
            }
        }
    }
    
    public class DrawActivity extends AppCompatActivity implements View.OnClickListener {
        private DrawCanvas mCanvas;//绘制画布
        private DrawPath mPath;//路径绘制命令
        private Paint mPaint;//画笔对象
        private IBrush mBrush;//笔触对象
    
        private Button btnRedo, btnUndo;//重做、撤销按钮
    
        @Override
        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);
                    break;
                case R.id.ac_draw_color_green_btn://切换为绿色
                    mPaint = new Paint();
                    mPaint.setStrokeWidth(3);
                    mPaint.setColor(0xFF00FF00);
                    break;
                case R.id.ac_draw_color_blue_btn://切换为蓝色
                    mPaint = new Paint();
                    mPaint.setStrokeWidth(3);
                    mPaint.setColor(0xFF0000FF);
                    break;
                case R.id.ac_draw_operate_undo_btn://撤销操作
                    mCanvas.undo();
                    if (!mCanvas.canUndo()) {
                        btnUndo.setEnabled(false);
                    }
                    btnRedo.setEnabled(true);
                    break;
                case R.id.ac_draw_operate_redo_btn://重做操作
                    mCanvas.redo();
                    if (!mCanvas.canRedo()) {
                        btnRedo.setEnabled(false);
                    }
                    btnUndo.setEnabled(true);
                    break;
                case R.id.ac_draw_brush_circle_btn://切换为圆形笔触
                    mBrush = new CircleBrush();
                    break;
                case R.id.ac_draw_brush_normal_btn://切换为正常笔触
                    mBrush = new NormalBrush();
                    break;
            }
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_draw);
    
            mPaint = new Paint();
            mPaint.setColor(0xFFFFFFFF);
            mPaint.setStrokeWidth(3);
    
            mBrush = new NormalBrush();
    
            mCanvas = (DrawCanvas) findViewById(R.id.ac_draw_canvas);
            mCanvas.setOnTouchListener(new DrawTouchListener());
    
            btnRedo = (Button) findViewById(R.id.ac_draw_operate_redo_btn);
            btnRedo.setEnabled(false);
            btnUndo = (Button) findViewById(R.id.ac_draw_operate_undo_btn);
            btnUndo.setEnabled(false);
            btnUndo.setOnClickListener(this);
            btnRedo.setOnClickListener(this);
            findViewById(R.id.ac_draw_color_red_btn).setOnClickListener(this);
            findViewById(R.id.ac_draw_color_green_btn).setOnClickListener(this);
            findViewById(R.id.ac_draw_color_blue_btn).setOnClickListener(this);
            findViewById(R.id.ac_draw_brush_normal_btn).setOnClickListener(this);
            findViewById(R.id.ac_draw_brush_circle_btn).setOnClickListener(this);
        }
    
        private class DrawTouchListener implements View.OnTouchListener {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    mPath = new DrawPath();
                    mPath.mPaint = mPaint;
                    mPath.path = new Path();
                    mBrush.down(mPath.path, event.getX(), event.getY());
                } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
                    mBrush.move(mPath.path, event.getX(), event.getY());
                } else if (event.getAction() == MotionEvent.ACTION_UP) {
                    mBrush.up(mPath.path, event.getX(), event.getY());
                    mCanvas.add(mPath);
                    mCanvas.isDrawing = true;
                }
                btnUndo.setEnabled(true);
                btnRedo.setEnabled(false);
                return true;
            }
        }
    
    }
    

    布局如下:

    命令模式布局.png

    四、总结

    4.1、优点

    更弱的耦合性、更灵活的控制性以及更好的扩展性。

    4.2、缺点

    类的膨胀,大量衍生类的创建。

    学海无涯苦作舟

    我的微信公众号

    相关文章

      网友评论

        本文标题:让程序畅通执行——命令模式

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