《Android源码设计模式解析与实战》读书笔记(十一)
《Android源码设计模式解析与实战》PDF资料下载
一、命令模式简介
命令模式,是行为型设计模式。
1.1、定义
将一个请求封装成一个对象,从而让用户使用不同的请求把客户端参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。
1.2、使用场景
- 需要抽象出待执行的动作,然后以参数的形式提供出来——类似于过程设计中的回调机制,而命令模式正是回调机制的一个面向对象的替代品。
- 在不同的时刻指定、排列和执行请求。一个命令对象可以有与初始请求无关的生存期。
- 需要支持取消操作。
- 支持修改日志功能,这样当系统崩溃时,这些修改可以被重做一遍。
- 需要支持事务操作。
二、命令模式的简单实现
以俄罗斯方块游戏为例:
//接收者角色
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、缺点
类的膨胀,大量衍生类的创建。
网友评论