图解设计模式 -- 用类来表现

代码Github连接 :https://github.com/tangbi123/Tab-Design-Pattern

Command模式

命令也是类
每一项想做的工作不再是“方法的调用”这种动态处理,而是一个表示命令的类的实例,既可以用“物”来表示。要想管理工作的历史记录,只需管理这些事例的集合即可,而且还可以随时再次执行过去的命令,或是将多个过去的命令整合为一个新命令并执行。
Command也被称为事件。

1、示例

在这里插入图片描述
在这里插入图片描述

代码清单

1)Command

public interface Command {
    public abstract void execute();
}

2)MacroCommand

public class MacroCommand implements Command{

    private Stack commands = new Stack();
    @Override
    public void execute() {
        Iterator it =commands.iterator();
        while(it.hasNext()){
            ((Command)it.next()).execute();
        }
    }

    public void append(Command cmd){
        if(cmd != this){
            commands.push(cmd);
        }
    }
    public void undo(){
        if(!commands.empty()){
            commands.pop();
        }
    }
    public void clear(){
        commands.clear();
    }
}

3)DrawCommand

public class DrawCommand implements Command{
    protected Drawable drawable;
    private Point position;

    public DrawCommand(Drawable drawable, Point position) {
        this.drawable = drawable;
        this.position = position;
    }

    @Override
    public void execute() {
        drawable.draw(position.x, position.y);
    }
}

4)Drawable

public interface Drawable {
    public abstract void draw(int x, int y);
}

5)DrawCanvas

public class DrawCanvas extends Canvas implements Drawable{
    private Color color = Color.red;

    private int radius = 6;

    private MacroCommand history;

    public DrawCanvas(int width, int height, MacroCommand history){
        setSize(width, height);
        setBackground(Color.white);
        this.history = history;
    }
    public void paint(Graphics g){
        history.execute();
    }
    @Override
    public void draw(int x, int y) {
        Graphics g=  getGraphics();
        g.setColor(color);
        g.fillOval(x - radius, y - radius, radius * 2, radius * 2);
    }
}

6)CommandMain

public class CommandMain extends JFrame implements ActionListener,
        MouseMotionListener, WindowListener {
    private MacroCommand history = new MacroCommand();

    private DrawCanvas canvas = new DrawCanvas(400,400,history);

    private JButton clearButton = new JButton("clear");

    public CommandMain(String title){
        super(title);

        this.addWindowListener(this);
        canvas.addMouseMotionListener(this);
        clearButton.addActionListener(this);

        Box buttonBox = new Box(BoxLayout.X_AXIS);
        buttonBox.add(clearButton);
        Box mainBox = new Box(BoxLayout.Y_AXIS);
        mainBox.add(buttonBox);
        mainBox.add(canvas);
        getContentPane().add(mainBox);
        pack();
        show();

    }
    public static void main(String[] args) {
        new CommandMain("Command Pattern Sample");
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if(e.getSource() == clearButton){
            history.clear();
            canvas.repaint();
        }
    }

    @Override
    public void mouseDragged(MouseEvent e) {
        Command cmd = new DrawCommand(canvas, e.getPoint());
        history.append(cmd);
        cmd.execute();
    }

    @Override
    public void mouseMoved(MouseEvent e) {

    }

    @Override
    public void windowOpened(WindowEvent e) {

    }

    @Override
    public void windowClosing(WindowEvent e) {
        System.exit(0);
    }

    @Override
    public void windowClosed(WindowEvent e) {

    }

    @Override
    public void windowIconified(WindowEvent e) {

    }

    @Override
    public void windowDeiconified(WindowEvent e) {

    }

    @Override
    public void windowActivated(WindowEvent e) {

    }

    @Override
    public void windowDeactivated(WindowEvent e) {

    }
}

时序图

在这里插入图片描述

2、角色

在这里插入图片描述
在这里插入图片描述

1)Command
负责定义接口(API)
2)ConcreteCommand
负责实现在Command角色中定义的接口(API)
3)Receiver
Command角色执行命令时的对象,命令接收者
4)Client
负责生成ConcreteCommand角色并分配Receiver角色。
相应鼠标拖动事件时,他声称了DrawCommand类的实例,并将攀岩Receiver角色的DrawCanvas类的实例传递给了DrawCommand类的构造函数。
5)Invoker
开始执行命令的角色,它会调用在Command角色中定义的接口(API)

3、思路要点

命令中应该包含那些信息

没有绝对答案,命令的目的不同,包含的信息也不同。
加入保存了事件发生的时间戳,那么重新绘制时,也可以重现用户鼠标操作的缓急。

保存历史记录

MacroCommand保存绘制的历史记录

适配器

Interpreter模式

语法规则也是类
解释器:翻译程序会理解迷你语言,并解释和运行迷你程序。

迷你语言语法:

BFN时Backus-Naur Form 或 Backus Normal Form的略称,他经常被用于描述语法。
在这里插入图片描述
1)program + command

<program> ::= program <command list>
ex: program go

2)多个命令 + end

<command list> ::= <command>* end 
program go right go right go right end

3)command 的定义

<command> ::= <repeat command> | <primitive command> 

4)循环命令 < repeat command>

<repeat command> ::= repeat <number> <command list>
program repeat 4 repeat 3 go right go left end right end end

5)基本命令 < rimitive command>

<primitive command> ::= go | right | left

6)终结符表达式与非终结符表达式
终结符表达式:像< primitive command>不会被进一步展开的表达式。(Nonterminal Expression)
非终结符表达式:像< program> 和< command> 需要被进一步展开的表达式。

1、示例

实现了一个迷你程序的语法解析器,将其推到成语法树进行处理,就是语法解析。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码清单

1)Context

public class Context {
    private StringTokenizer tokenizer;
    private String currentToken;
    public Context(String text){
        tokenizer = new StringTokenizer(text);
        nextToken();
    }

    public String nextToken(){
        if(tokenizer.hasMoreTokens()){
            currentToken = tokenizer.nextToken();
        }else{
            currentToken = null;
        }
        return currentToken;
    }
    public String currentToken(){
        return currentToken;
    }
    public void skipToken(String token)throws ParseException{
        if(!token.equals(currentToken)){
            throw new ParseException("Warning:" + token + "is expected,but" +
                    currentToken + "is not found.,");
        }
        nextToken();
    }
    public int currentNumber()throws ParseException{
        int number = 0;
        try{
            number = Integer.parseInt(currentToken);
        }catch (NumberFormatException e){
            throw new ParseException("Warning:" + e);
        }
        return number;

    }
}

2)ParseException

public class ParseException extends Exception{
    public ParseException(String msg){
        super(msg);
    }
}

3)Node

public abstract class Node {
    public abstract void parse(Context context)throws ParseException;
}

4)ProgramNode

public class ProgrameNode extends Node{
    private Node commandListNode;
    @Override
    public void parse(Context context) throws ParseException {
        context.skipToken("program");
        commandListNode = new CommandListNode();
        commandListNode.parse(context);
    }
    public String toString(){
        return "[program" + commandListNode + "]";
    }
}

5)CommandListNode

public class CommandListNode extends Node{
    private ArrayList list = new ArrayList();
    public void parse(Context context)throws ParseException{
        while(true){
            if(context.currentToken() == null){
                throw new ParseException("Missing ‘end'");
            }
            else if(context.currentToken().equals("end")){
                context.skipToken("end");
                break;
            }
            else{
                Node commandNode = new CommandNode();
                commandNode.parse(context);
                list.add(commandNode);
            }
        }
    }
    public String toString(){
        return list.toString();
    }
}

6)CommandNode

public class CommandNode extends Node{
    private Node node;
    @Override
    public void parse(Context context) throws ParseException {
        if(context.currentToken().equals("repeat")){
            node = new RepeatCommandNode();
            node.parse(context);
        }else{
            node = new PrimitiveCommandNode();
            node.parse(context);
        }
    }

    public String toString(){
        return node.toString();
    }
}

7)RepeatCommandNode

public class RepeatCommandNode extends Node{
    private int number;
    private Node commandListNode;

    @Override
    public void parse(Context context) throws ParseException {
        context.skipToken("repeat");
        number = context.currentNumber();
        context.nextToken();
        commandListNode = new CommandListNode();
        commandListNode.parse(context);
    }

    public String toString(){
        return "[repeat " + number + " " + commandListNode + "]";
    }
}

8)PrimitiveCommandNode

public class PrimitiveCommandNode extends Node{
    private String name;
    @Override
    public void parse(Context context) throws ParseException {
        name = context.currentToken();
        context.skipToken(name);
        if(!name.equals("go") && !name.equals("right") && !name.equals("left")){
            throw new ParseException(name + "is undefined");
        }
    }
    public String toString(){
        return name;
    }
}

9)program.txt

program end
program go end
program go right go right go right go right end
program repeat 4 go right end end
program repeat 4 repeat 3 go right go left end right end end

10)Main

public class InterpreterMain {
    public static void main(String[] args) {
        try{
            BufferedReader reader = new BufferedReader(
                    //注意文件的位置
                    new FileReader("src/Interpreter/program.txt")
            );
            String text;
            while((text = reader.readLine()) != null){
                System.out.println("text = \"" + text + "\"");
                Node node = new ProgrameNode();
                node.parse(new Context(text));
                System.out.println("node = " + node);
            }
            reader.close();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

在这里插入图片描述

2、角色

在这里插入图片描述
1)AbstractException(抽象表达式)
定义了语法树节点的共同接口(API). Node => parse
2)TerminalExpression(终结符表达式)
对应BNF中的终结符表达式。 =》 PrimitiveCommandNode
3)NonterminalExperssion(非终结符表达式)
对应BNF中的非终结符表达式。 ProgramNode、CommandNode…
4)Context
为解析器进行语法解析提供了必要信息。
5)Client
为了推到语法树,Client角色会调用TerminalExpression角色和NonterminalExperssion角色。

3、思路要点

还有其他那些迷你语言

正则表达式
检索表达式
批处理语言

跳过标记还是读取标记

在制作解析器时,经常会出现多读了一个标记或是漏读了一个标记的Bug。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值