常用设计模式——复合模式

复合模式

概念

复合模式结合两个或以上的模式,组成一个解决方案,解决问题。

MVC和Model2属于复合模式。

MVC

MVC是复合模式的一种,结合了观察者模式、策略模式、组合模式。

视图:用来呈现模型。视图通常直接从模型中取得它需要显示的状态和数据。视图使用组合模式实现用户界面,用户界面通常组合了嵌套的组件,像面板、框架和按钮。

控制器:取得用户的输入,并解读其对模型的意思。控制器是视图的策略,视图可以使用不同的控制器(不同的策略),得到不同的行为。

模型:模型持有所有数据、状态和程序逻辑。使用了观察者模式,以便观察者更新,同时保持两者之间解耦。

流程 : 用户在视图上面进行操作,然后控制器取得用户的输入,并解读其对模型的意思。 控制器调用模型,模型负责处理具体逻辑。然后通知视图更新。

使用的模式

观察者模式

image-20190609104051467

策略模式

image-20190609104116426

组合模式

image-20190609104138045

示例

/**
 * MVC示例
 * (1)视图、控制器、模型3层结构。
 *      流程:用户在视图上面进行操作,然后控制器取得用户的输入,并解读其对模型的意思。
 *  *      控制器调用模型,模型负责处理具体逻辑。然后通知视图更新。
 * (2)采用策略模式(控制器作为策略),观察者模式(模型和 视图、控制器解耦),组合模式(视图)
 * @author huangy on 2019-06-09
 */
public class DJTestDrive {

    public static void main (String[] args) {

        // 模型
        BeatModelInterface model = new BeatModel();

        // 控制器,相当于把模型(策略赋值给控制器)
        ControllerInterface controller = new BeatController(model);
    }
}
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class DJView implements ActionListener, BeatObserver, BPMObserver {//同时关心时时节拍和BPM的改变

    // 视图持有模型和控制器的引用
    BeatModelInterface model;
    ControllerInterface controller;

    JFrame viewFrame;
    JPanel viewPanel;
    JLabel bpmOutputLabel;
    JFrame controlFrame;
    JPanel controlPanel;
    JLabel bpmLabel;
    JTextField bpmTextField;
    JButton setBPMButton;
    JButton increaseBPMButton;
    JButton decreaseBPMButton;
    JMenuBar menuBar;
    JMenu menu;
    JMenuItem startMenuItem;
    JMenuItem stopMenuItem;

    public DJView(ControllerInterface controller, BeatModelInterface model) {
        this.controller = controller;
        this.model = model;
        // 注册观察者
        model.registerObserver((BeatObserver)this);
        model.registerObserver((BPMObserver)this);
    }
    public void createView() {
        // Create all Swing components here
        viewPanel = new JPanel(new GridLayout(1, 2));
        viewFrame = new JFrame("View");
        viewFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        viewFrame.setSize(new Dimension(100, 80));
        bpmOutputLabel = new JLabel("offline", SwingConstants.CENTER);
        JPanel bpmPanel = new JPanel(new GridLayout(2, 1));
        bpmPanel.add(bpmOutputLabel);
        viewPanel.add(bpmPanel);
        viewFrame.getContentPane().add(viewPanel, BorderLayout.CENTER);
        viewFrame.pack();
        viewFrame.setVisible(true);
    }

    public void createControls() {
        // Create all Swing components here
        JFrame.setDefaultLookAndFeelDecorated(true);
        controlFrame = new JFrame("Control");
        controlFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        controlFrame.setSize(new Dimension(100, 80));

        controlPanel = new JPanel(new GridLayout(1, 2));

        menuBar = new JMenuBar();
        menu = new JMenu("DJ Control");
        startMenuItem = new JMenuItem("Start");
        menu.add(startMenuItem);
        startMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                // 视图的点击触发控制器的事件
                controller.start();
            }
        });
        stopMenuItem = new JMenuItem("Stop");
        menu.add(stopMenuItem);
        stopMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                controller.stop();
            }
        });
        JMenuItem exit = new JMenuItem("Quit");
        exit.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                System.exit(0);
            }
        });

        menu.add(exit);
        menuBar.add(menu);
        controlFrame.setJMenuBar(menuBar);

        bpmTextField = new JTextField(2);
        bpmLabel = new JLabel("Enter BPM:", SwingConstants.RIGHT);
        setBPMButton = new JButton("Set");
        setBPMButton.setSize(new Dimension(10,40));
        increaseBPMButton = new JButton(">>");
        decreaseBPMButton = new JButton("<<");
        setBPMButton.addActionListener(this);
        increaseBPMButton.addActionListener(this);
        decreaseBPMButton.addActionListener(this);

        JPanel buttonPanel = new JPanel(new GridLayout(1, 2));

        buttonPanel.add(decreaseBPMButton);
        buttonPanel.add(increaseBPMButton);

        JPanel enterPanel = new JPanel(new GridLayout(1, 2));
        enterPanel.add(bpmLabel);
        enterPanel.add(bpmTextField);
        JPanel insideControlPanel = new JPanel(new GridLayout(3, 1));
        insideControlPanel.add(enterPanel);
        insideControlPanel.add(setBPMButton);
        insideControlPanel.add(buttonPanel);
        controlPanel.add(insideControlPanel);
        bpmLabel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
        bpmOutputLabel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));

        controlFrame.getRootPane().setDefaultButton(setBPMButton);
        controlFrame.getContentPane().add(controlPanel, BorderLayout.CENTER);

        controlFrame.pack();
        controlFrame.setVisible(true);
    }

    public void enableStopMenuItem() {
        stopMenuItem.setEnabled(true);
    }

    public void disableStopMenuItem() {
        stopMenuItem.setEnabled(false);
    }

    public void enableStartMenuItem() {
        startMenuItem.setEnabled(true);
    }

    public void disableStartMenuItem() {
        startMenuItem.setEnabled(false);
    }

    // 视图接收动作
    public void actionPerformed(ActionEvent event) {
        if (event.getSource() == setBPMButton) {
            System.out.println("view receive set bpm action");
            int bpm = Integer.parseInt(bpmTextField.getText());
            // 视图的改变会直接传递给控制器
            controller.setBPM(bpm);
        } else if (event.getSource() == increaseBPMButton) {
            System.out.println("view receive increase bpm action");
            controller.increaseBPM();
        } else if (event.getSource() == decreaseBPMButton) {
            System.out.println("view receive decrease bpm action");
            controller.decreaseBPM();
        }
    }

    // 模型发生改变时,这个方法会被调用(观察者模式)
    public void updateBPM() {
        if (model != null) {
            int bpm = model.getBPM();
            if (bpm == 0) {
                if (bpmOutputLabel != null) {
                    bpmOutputLabel.setText("offline");
                }
            } else {
                if (bpmOutputLabel != null) {
                    bpmOutputLabel.setText("Current BPM: " + model.getBPM());
                }
            }
        }
    }
    public void updateBeat() {//相应的,当模型开始一个新的节拍时,这个方法会被调用

    }
}
/**
 * 控制器接口(策略)
 * @author huangy on 2019-06-09
 */
public interface ControllerInterface {
    void start();
    void stop();
    void increaseBPM();
    void decreaseBPM();
    void setBPM(int bpm);
}
/**
 * Bpm观察者
 * @author huangy on 2019-06-09
 */
public interface BPMObserver {
    void updateBPM();

}
/**
 * 节拍观察者
 * @author huangy on 2019-06-09
 */
public interface BeatObserver {

    void updateBeat();

}
/**
 * Model模型接口
 * @author huangy on 2019-06-09
 */
public interface BeatModelInterface {

    void initialize();

    void on();

    void off();

    void setBPM(int bpm);

    int getBPM();

    void registerObserver(BeatObserver observer);

    void removeObserver(BeatObserver observer);

    void registerObserver(BPMObserver observer);

    void removeObserver(BPMObserver observer);
}
import javax.sound.midi.*;
import java.util.ArrayList;

/**
 * 模型
 * @author huangy on 2019-06-09
 */
public class BeatModel implements BeatModelInterface, MetaEventListener {

    Sequencer sequencer;

    Sequence sequence;

    Track track;

    // 观察者列表(观察敲击)
    ArrayList<BeatObserver> beatObservers = new ArrayList<>();

    // 观察者列表(观察BPM)
    ArrayList<BPMObserver> BPMObservers = new ArrayList<>();

    int bpm = 90;

    @Override
    public void initialize() {
        setUpMidi();
        buildTrackAndStart();
    }

    @Override
    public void on() {
        sequencer.start();
        setBPM(90);
    }

    @Override
    public void setBPM(int bpm) {
        System.out.println("model receive set bpm action");
        this.bpm = bpm;
        notifyBPMObservers();
    }

    @Override
    public void off() {
        setBPM(0);
        sequencer.stop();
    }

    @Override
    public int getBPM() {
        return bpm;
    }

    void beatEvent() {
        notifyBeatObservers();
    }

    // 注册观察者
    @Override
    public void registerObserver(BeatObserver observer) {
        beatObservers.add(observer);
    }

    // 有敲击事件发生的时候,通知观察者
    public void notifyBeatObservers() {
        for (int i = 0; i < beatObservers.size(); i++) {
            BeatObserver beatObserver = beatObservers.get(i);
            beatObserver.updateBeat();
        }
    }

    @Override
    public void registerObserver(BPMObserver observer) {
        BPMObservers.add(observer);
    }

    public void notifyBPMObservers() {
        for (BPMObserver observer : BPMObservers) {
            observer.updateBPM();
        }
    }

    @Override
    public void removeObserver(BeatObserver observer) {
        beatObservers.remove(observer);
    }

    @Override
    public void removeObserver(BPMObserver observer) {
        BPMObservers.remove(observer);
    }

    public void meta(MetaMessage metaMessage) {

        if (metaMessage.getType() == 47) {

            beatEvent();

            sequencer.start();

            setBPM(getBPM());
        }

    }

    public void setUpMidi() {
        try {
            sequencer = MidiSystem.getSequencer();
            sequencer.open();
            sequencer.addMetaEventListener(this);
            sequence = new Sequence(Sequence.PPQ, 4);
            track = sequence.createTrack();
            sequencer.setTempoInBPM(getBPM());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void buildTrackAndStart() {
        int[] trackList = {35, 0, 46, 0};

        sequence.deleteTrack(null);
        track = sequence.createTrack();

        makeTracks(trackList);

        track.add(makeEvevt(192, 9, 1, 0, 4));

        try {
            sequencer.setSequence(sequence);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void makeTracks(int[] list) {
        for (int i = 0; i < list.length; i++) {
            int key = list[i];
            track.add(makeEvevt(144, 9, key, 100, i));
            track.add(makeEvevt(128, 9, key, 100, i + i));
        }
    }

    public MidiEvent makeEvevt(int comd, int chan, int one, int two, int tick) {
        MidiEvent event = null;
        try {
             ShortMessage a = new ShortMessage();
             a.setMessage(comd, chan, one, two);
             event = new MidiEvent(a, tick);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return event;
    }
}

/**
 * 控制器
 * @author huangy on 2019-06-09
 */
public class BeatController implements ControllerInterface {

    // MVC中,控制器在中间,所以要同时持有模型以及视图的引用。
    BeatModelInterface model;
    DJView view;

    public BeatController(BeatModelInterface model) {
        this.model = model;

        // 控制器创建视图
        view = new DJView(this, model);
        view.createView();
        view.createControls();
        view.disableStopMenuItem();
        view.enableStartMenuItem();

        model.initialize();
    }

    // 控制器在得到start指令时去操纵模型和视图,下边的几个动作同理。
    public void start() {

        // 模型负责具体操作(下面几个命令同理)
        model.on();

        // 注意,控制器这时在帮视图做决定,视图只知道如何将菜单项变成开或者关而不知道在何时该这么做
        view.disableStartMenuItem();
        view.enableStopMenuItem();
    }

    public void stop() {

        model.off();

        view.disableStopMenuItem();
        view.enableStartMenuItem();
    }

    // 控制器扩展了模型的动作
    public void increaseBPM() {
        System.out.println("controller receive increase bpm action");
        int bpm = model.getBPM();
        model.setBPM(bpm + 1);
    }

    public void decreaseBPM() {
        System.out.println("controller receive decrease bpm action");
        int bpm = model.getBPM();
        model.setBPM(bpm - 1);
    }

    public void setBPM(int bpm) {
        System.out.println("controller receive set bpm action");
        model.setBPM(bpm);
    }
}

Model2

Model2是MVC在Web上的应用。在Model2中,控制器实现成Servlet,而JSP/HTML实现成视图。

在Web开发中,MVC被经常叫做Model 2。有了这个模型,该编程的人就去做编程,该做网页的人就去做网页。JSP只知道会从控制器收到一个Bean,利用Bean的数据进行渲染。

Snip20190609_14

image-20190609105141128

###流程补充

Model2模式的工作原理如下,其工作流程如下5个步骤进行。

  1. Servlet接收浏览器发出的请求
  2. Servlet根据不同的请求调用相应的JavaBean
  3. JavaBean按自己的业务逻辑,通过JDBC操作数据库
  4. Servlet将结果传递给JSP
  5. JSP将后台处理的结果呈现给浏览器

image-20190609135612391

示例参考:https://www.jianshu.com/p/1c6d5d6bb8d6

Model1

早期的Java EE项目全部采用JSP编写,JSP文件既要负责创建HTML页面,又要控制网页流程.同时还要负责处理业务逻辑. 这给Java EE的开发带来一系列问题 如 代码耦合性强,系统控制流程复杂,难以维护等,为了解决这些问题,原Sun公司制定了Model1模式作为Java EE程序员开发的考性规范.

在Java EE程序开发中,通常用JSP负责动态生成Web网页,而业务逻辑则由其他可重用的组件(如JavaBean)来实现 .JSP可通过Java程序片段来访问这些组件,于是就有了JSP+JavaBean这样同行的程序结构 ,也就是Model1开发模式.

image-20190609135848375

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值