模式通常被一起使用,并被组合在同一个解决方案中。
复合模式在一个解决方案中结合两个或多个模式,以解决一般或重复发生的问题。
首先重新构建鸭子模拟器:
package headfirst.designpatterns.combining.ducks;
public interface Quackable {
public void quack();
}
public class MallardDuck implements Quackable {
public void quack() {
System.out.println("Quack");
}
}
public class RubberDuck implements Quackable {
public void quack() {
System.out.println("Squeak");
}
}
package headfirst.designpatterns.combining.ducks;
public class DuckSimulator {
public static void main(String[] args) {
DuckSimulator simulator = new DuckSimulator();
simulator.simulate();
}
void simulate() {
Quackable mallardDuck = new MallardDuck();
Quackable redheadDuck = new RedheadDuck();
Quackable duckCall = new DuckCall();
Quackable rubberDuck = new RubberDuck();
System.out.println("\nDuck Simulator");
simulate(mallardDuck);
simulate(redheadDuck);
simulate(duckCall);
simulate(rubberDuck);
}
void simulate(Quackable duck) {
duck.quack();
}
}
需求1,有鸭子的地方大概率就会有鹅,怎样在模拟器使用鹅呢?适配器
package headfirst.designpatterns.combining.adapter;
public class Goose {
public void honk() {
System.out.println("Honk");
}
}
package headfirst.designpatterns.combining.adapter;
public class GooseAdapter implements Quackable {
Goose goose;
public GooseAdapter(Goose goose) {
this.goose = goose;
}
public void quack() {
goose.honk();
}
public String toString() {
return "Goose pretending to be a Duck";
}
}
需求2,如果在不变化鸭子类的情况下,计算呱呱叫的次数呢?装饰者
将鸭子包装进装饰者对象,赋予鸭子一些新行为。
package headfirst.designpatterns.combining.decorator;
public class QuackCounter implements Quackable {
Quackable duck;
static int numberOfQuacks;
public QuackCounter (Quackable duck) {
this.duck = duck;
}
public void quack() {
duck.quack();
numberOfQuacks++;
}
public static int getQuacks() {
return numberOfQuacks;
}
public String toString() {
return duck.toString();
}
}
// 更新模拟器
Quackable mallardDuck = new QuackCounter(new MallardDuck());
需求3,有时候我们在代码中可能搞忘装饰对象,所以我们希望在最开始创建的时候就确保鸭子是被装饰过的:工厂模式
package headfirst.designpatterns.combining.factory;
public abstract class AbstractDuckFactory {
public abstract Quackable createMallardDuck();
public abstract Quackable createRedheadDuck();
public abstract Quackable createDuckCall();
public abstract Quackable createRubberDuck();
}
package headfirst.designpatterns.combining.factory;
public class CountingDuckFactory extends AbstractDuckFactory {
public Quackable createMallardDuck() {
return new QuackCounter(new MallardDuck());
}
public Quackable createRedheadDuck() {
return new QuackCounter(new RedheadDuck());
}
public Quackable createDuckCall() {
return new QuackCounter(new DuckCall());
}
public Quackable createRubberDuck() {
return new QuackCounter(new RubberDuck());
}
}
抽象工厂通过传入不同的工厂到创建方法中,得到不同的产品家族。
package headfirst.designpatterns.combining.factory;
public class DuckSimulator {
public static void main(String[] args) {
DuckSimulator simulator = new DuckSimulator();
AbstractDuckFactory duckFactory = new CountingDuckFactory();
simulator.simulate(duckFactory);
}
void simulate(AbstractDuckFactory duckFactory) {
Quackable mallardDuck = duckFactory.createMallardDuck();
Quackable redheadDuck = duckFactory.createRedheadDuck();
Quackable duckCall = duckFactory.createDuckCall();
Quackable rubberDuck = duckFactory.createRubberDuck();
Quackable gooseDuck = new GooseAdapter(new Goose());
System.out.println("\nDuck Simulator: With Abstract Factory");
simulate(mallardDuck);
simulate(redheadDuck);
simulate(duckCall);
simulate(rubberDuck);
simulate(gooseDuck);
System.out.println("The ducks quacked " +
QuackCounter.getQuacks() +
" times");
}
void simulate(Quackable duck) {
duck.quack();
}
}
需求4,同时管理一群鸭子:组合模式
package headfirst.designpatterns.combining.composite;
import java.util.Iterator;
import java.util.ArrayList;
public class Flock implements Quackable {
ArrayList<Quackable> quackers = new ArrayList<Quackable>();
public void add(Quackable quacker) {
quackers.add(quacker);
}
public void quack() {
Iterator<Quackable> iterator = quackers.iterator();
while (iterator.hasNext()) {
Quackable quacker = iterator.next();
quacker.quack();
}
}
public String toString() {
return "Flock of Quackers";
}
}
package headfirst.designpatterns.combining.composite;
public class DuckSimulator {
public static void main(String[] args) {
DuckSimulator simulator = new DuckSimulator();
AbstractDuckFactory duckFactory = new CountingDuckFactory();
simulator.simulate(duckFactory);
}
void simulate(AbstractDuckFactory duckFactory) {
Quackable redheadDuck = duckFactory.createRedheadDuck();
Quackable duckCall = duckFactory.createDuckCall();
Quackable rubberDuck = duckFactory.createRubberDuck();
Quackable gooseDuck = new GooseAdapter(new Goose());
System.out.println("\nDuck Simulator: With Composite - Flocks");
Flock flockOfDucks = new Flock();
flockOfDucks.add(redheadDuck);
flockOfDucks.add(duckCall);
flockOfDucks.add(rubberDuck);
flockOfDucks.add(gooseDuck);
Flock flockOfMallards = new Flock();
Quackable mallardOne = duckFactory.createMallardDuck();
Quackable mallardTwo = duckFactory.createMallardDuck();
Quackable mallardThree = duckFactory.createMallardDuck();
Quackable mallardFour = duckFactory.createMallardDuck();
flockOfMallards.add(mallardOne);
flockOfMallards.add(mallardTwo);
flockOfMallards.add(mallardThree);
flockOfMallards.add(mallardFour);
flockOfDucks.add(flockOfMallards);
System.out.println("\nDuck Simulator: Whole Flock Simulation");
simulate(flockOfDucks);
System.out.println("\nDuck Simulator: Mallard Flock Simulation");
simulate(flockOfMallards);
System.out.println("\nThe ducks quacked " +
QuackCounter.getQuacks() +
" times");
}
void simulate(Quackable duck) {
duck.quack();
}
}
需求5,持续追踪个别鸭子的实时呱呱叫:观察者模式
package headfirst.designpatterns.combining.observer;
public interface QuackObservable {
public void registerObserver(Observer observer);
public void notifyObservers();
}
public interface Quackable extends QuackObservable {
public void quack();
}
现在需要保证Quackable的具体类都能扮演QuackObservable角色(在具体类中添加一个arraylist变量,然后实现接口方法)。但这里用的不同的做法(不知道为什么),在Observable类中封装注册和通知的代码,然后将其和具体类组合在一起。
package headfirst.designpatterns.combining.observer;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
public class Observable implements QuackObservable {
List<Observer> observers = new ArrayList<Observer>();
QuackObservable duck;
public Observable(QuackObservable duck) {
this.duck = duck;
}
public void registerObserver(Observer observer) {
observers.add(observer);
}
public void notifyObservers() {
Iterator<Observer> iterator = observers.iterator();
while (iterator.hasNext()) {
Observer observer = iterator.next();
observer.update(duck);
}
}
public Iterator<Observer> getObservers() {
return observers.iterator();
}
}
package headfirst.designpatterns.combining.observer;
public class MallardDuck implements Quackable {
Observable observable;
public MallardDuck() {
observable = new Observable(this);
}
public void quack() {
System.out.println("Quack");
notifyObservers();
}
public void registerObserver(Observer observer) {
observable.registerObserver(observer);
}
public void notifyObservers() {
observable.notifyObservers();
}
public String toString() {
return "Mallard Duck";
}
}
设计模式是MVC的钥匙。
为什么不将控制器代码放入视图中?
如果放入,1.视图就有两个责任,不但要管理用户界面,还要处理如何控制模型的逻辑。2.不能对视图进行复用。
- 模型利用“观察者”让控制器和视图可以随着最新状态的改变而更新。
- 视图和控制器实现“策略模式”。控制器是视图的行为。如果想要不同的行为,则可以直接更换控制器。
- 视图内部使用组合模式来管理窗口、按钮及其他显示组件,
Java DJ View
public class BeatModel implements BeatModelInterface, Runnable {
List<BeatObserver> beatObservers = new ArrayList<BeatObserver>();
List<BPMObserver> bpmObservers = new ArrayList<BPMObserver>();
Sequencer sequencer;
public void initialize() {
setUpMidi();
buildTrackAndStart();
}
public void on() {
sequencer.start();
setBPM(90);
}
public void off() {
setBPM(0);
sequencer.stop();
}
public void setBPM(int bpm) {
this.bpm = bpm;
sequencer.setTempoInBPM(getBPM);
notifyBPMObservers();
}
public int getBPM() {
return bpm;
}
void beatEvent() {
notifyBPMObservers();
}
// 注册观察者,通知观察者的代码
// 处理节拍的MIDI代码
}
package headfirst.designpatterns.combined.djview;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class DJView implements ActionListener, BeatObserver, BPMObserver {
BeatModelInterface model;
ControllerInterface controller;
JFrame viewFrame;
JPanel viewPanel;
BeatBar beatBar;
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);
beatBar = new BeatBar();
beatBar.setValue(0);
JPanel bpmPanel = new JPanel(new GridLayout(2, 1));
bpmPanel.add(beatBar);
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((event) -> controller.start());
// was....
/*
startMenuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
controller.start();
}
});
*/
stopMenuItem = new JMenuItem("Stop");
menu.add(stopMenuItem);
stopMenuItem.addActionListener((event) -> controller.stop());
// was...
/*
stopMenuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
controller.stop();
}
});
*/
JMenuItem exit = new JMenuItem("Quit");
exit.addActionListener((event) -> System.exit(0));
// was...
/*
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) {
int bpm = 90;
String bpmText = bpmTextField.getText();
if (bpmText == null || bpmText.contentEquals("")) {
bpm = 90;
} else {
bpm = Integer.parseInt(bpmTextField.getText());
}
controller.setBPM(bpm);
} else if (event.getSource() == increaseBPMButton) {
controller.increaseBPM();
} else if (event.getSource() == decreaseBPMButton) {
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() {
if (beatBar != null) {
beatBar.setValue(100);
}
}
}
package headfirst.designpatterns.combined.djview;
public class BeatController implements ControllerInterface {
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();
}
public void start() {
model.on();
view.disableStartMenuItem();
view.enableStopMenuItem();
}
public void stop() {
model.off();
view.disableStopMenuItem();
view.enableStartMenuItem();
}
public void increaseBPM() {
int bpm = model.getBPM();
model.setBPM(bpm + 1);
}
public void decreaseBPM() {
int bpm = model.getBPM();
model.setBPM(bpm - 1);
}
public void setBPM(int bpm) {
model.setBPM(bpm);
}
}
使用适配器可以将模型适配成符合现有视图和控制器的需要的模型。(如果我们还有一个心脏模型,如下图)
MVC和Web(model2)
复合模式:复合模式结合两个或以上的模式,组成一个解决方案,解决一再发生的一般性问题。