专栏首页用户5325874的专栏常用设计模式——复合模式

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

复合模式

概念

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

MVC和Model2属于复合模式。

MVC

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

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

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

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

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

使用的模式

观察者模式

策略模式

组合模式

示例

/**
 * 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的数据进行渲染。

###流程补充

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

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

示例参考: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开发模式.

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 常用设计模式——装饰模式

    装饰者模式动态地将责任附加到对象上。若要扩展功能,装饰者模式提供了比继承更有弹性的方案

    用户5325874
  • 常用设计模式——迭代器模式

    用户5325874
  • 常用设计模式——命令模式

    将"请求"封装成对象,以便使用不同的请求、队列、或者日志来参数化其他对象(就是命令对象拥有相同的接口,不同的命令对象可以被设置到调用者中)。命令模式也可以支持撤...

    用户5325874
  • java内部类的作用(二)----隐藏作用

    局部内部类有一个优势:即对外部世界完全可以隐藏起来,在这个方法类中的其它方法或者代码都不能调用这个内部类。更不用说其它的类了

    wust小吴
  • 设计原则

    一、面向对象应用程序开发原则(SOLID) 1单一职责原则(SRP) 定义: 一个类应该只有一个发生变化的原因。这条原则曾被称为内聚性,即一个模块的组成元素之间...

    甜橙很酸
  • JavaSE学习总结(四)——Java面向对象十分钟入门

    面向对象编程(Object Oriented Programming,OOP)是一种计算机模拟人类的自然思维方式的编程架构技术,解决了传统结构化开发方法中客观...

    张果
  • 在C#中使用依赖注入-生命周期控制

    在使用依赖注入的过程当中,除了应用设计模式注意代码的变化隔离之外,另外一个重要的内容就是生命周期控制。

    newbe36524
  • Java接口实例

    在Java之中每一个抽象类都可以实现多个接口,但是反过来讲,一个接口却不能继承抽象类,可是Java之中,一个接口却可以同时继承多个接口,以实现接口的多继承操作。...

    葆宁
  • Spring Boot 2.X(十八):集成 Spring Security-登录认证和权限控制

    在企业项目开发中,对系统的安全和权限控制往往是必需的,常见的安全框架有 Spring Security、Apache Shiro 等。本文主要简单介绍一下 Sp...

    朝雾轻寒
  • OOAD-设计模式(四)结构型模式之适配器、装饰器、代理模式

    前言   前面我们学习了创建型设计模式,其中有5中,个人感觉比较重要的是工厂方法模式、单例模式、原型模式。接下来我将分享的是结构型模式! 一、适配器模式 1.1...

    用户1195962

扫码关注云+社区

领取腾讯云代金券