标签归档:读书笔记

设计模式之命令模式

命令模式就是将请求封装为一个对象,从而使你可用不同的请求对客户进行参数化; 对请求排队或记录请求日志,以及支持可撤销的操作。

在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。

这是一个遥控器的例子
一个灯类,有开灯、关灯、昏暗灯光之类的方法。
一个接口命令类:定义了执行和撤销两个方法。
下面有四个类实现了命令接口,分别是:开灯命令、关灯命令、昏暗开灯命令、昏暗关灯命令。
一个遥控器类,在模式中叫做调度者,依赖命令接口,调用命令的方法。
客户类,上图没有画,初始化命令类,传入遥控器类,调用遥控器类相应方法。

代码:
Light.java:

package headfirst.command.undo;

public class Light {
	String location;
	int level;

	public Light(String location) {
		this.location = location;
	}

	public void on() {
		level = 100;
		System.out.println("Light is on");
	}

	public void off() {
		level = 0;
		System.out.println("Light is off");
	}

	public void dim(int level) {
		this.level = level;
		if (level == 0) {
			off();
		}
		else {
			System.out.println("Light is dimmed to " + level + "%");
		}
	}

	public int getLevel() {
		return level;
	}
}

命令接口 Command.java:

package headfirst.command.undo;

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

开灯命令 LightOnCommand.java:

package headfirst.command.undo;

public class LightOnCommand implements Command {
	Light light;
 
	public LightOnCommand(Light light) {
		this.light = light;
	}
 
	public void execute() {
		light.on();
	}
 
	public void undo() {
		light.off();
	}
}

关灯命令 LightOffCommand.java:

package headfirst.command.undo;

public class LightOffCommand implements Command {
	Light light;
 
	public LightOffCommand(Light light) {
		this.light = light;
	}
 
	public void execute() {
		light.off();
	}
 
	public void undo() {
		light.on();
	}
}

昏暗开灯命令 DimmerLightOnCommand.java:

package headfirst.command.undo;

public class DimmerLightOnCommand implements Command {
	Light light;
	int prevLevel;

	public DimmerLightOnCommand(Light light) {
		this.light = light;
	}

	public void execute() {
		prevLevel = light.getLevel();
		light.dim(75);
	}

	public void undo() {
		light.dim(prevLevel);
	}
}

昏暗关灯 DimmerLightOffCommand.java:

package headfirst.command.undo;

public class DimmerLightOffCommand implements Command {
	Light light;
	int prevLevel;

	public DimmerLightOffCommand(Light light) {
		this.light = light;
		prevLevel = 100;
	}

	public void execute() {
		prevLevel = light.getLevel();
		light.off();
	}

	public void undo() {
		light.dim(prevLevel);
	}
}

空命令 NoCommand.java:

package headfirst.command.undo;

public class NoCommand implements Command {
	public void execute() { }
	public void undo() { }
}

遥控器 RemoteControlWithUndo.java:

package headfirst.command.undo;

import java.util.*;

//
// This is the invoker
//
public class RemoteControlWithUndo {
	Command[] onCommands;
	Command[] offCommands;
	Command undoCommand;
 
	public RemoteControlWithUndo() {
		onCommands = new Command[7];
		offCommands = new Command[7];
 
		Command noCommand = new NoCommand();
		for(int i=0;i<7;i++) {
			onCommands[i] = noCommand;
			offCommands[i] = noCommand;
		}
		undoCommand = noCommand;
	}
  
	public void setCommand(int slot, Command onCommand, Command offCommand) {
		onCommands[slot] = onCommand;
		offCommands[slot] = offCommand;
	}
 
	public void onButtonWasPushed(int slot) {
		onCommands[slot].execute();
		undoCommand = onCommands[slot];
	}
 
	public void offButtonWasPushed(int slot) {
		offCommands[slot].execute();
		undoCommand = offCommands[slot];
	}
 
	public void undoButtonWasPushed() {
		undoCommand.undo();
	}
  
	public String toString() {
		StringBuffer stringBuff = new StringBuffer();
		stringBuff.append("\n------ Remote Control -------\n");
		for (int i = 0; i < onCommands.length; i++) {
			stringBuff.append("[slot " + i + "] " + onCommands[i].getClass().getName()
				+ "    " + offCommands[i].getClass().getName() + "\n");
		}
		stringBuff.append("[undo] " + undoCommand.getClass().getName() + "\n");
		return stringBuff.toString();
	}
}

入口类:

package headfirst.command.undo;

public class RemoteLoader {
 
	public static void main(String[] args) {
		RemoteControlWithUndo remoteControl = new RemoteControlWithUndo();
 
		Light livingRoomLight = new Light("Living Room");
 
		LightOnCommand livingRoomLightOn = 
				new LightOnCommand(livingRoomLight);
		LightOffCommand livingRoomLightOff = 
				new LightOffCommand(livingRoomLight);
 
		remoteControl.setCommand(0, livingRoomLightOn, livingRoomLightOff);
 
		remoteControl.onButtonWasPushed(0);
		remoteControl.offButtonWasPushed(0);
		System.out.println(remoteControl);
		remoteControl.undoButtonWasPushed();
		remoteControl.offButtonWasPushed(0);
		remoteControl.onButtonWasPushed(0);
		System.out.println(remoteControl);
		remoteControl.undoButtonWasPushed();

		CeilingFan ceilingFan = new CeilingFan("Living Room");
   
		CeilingFanMediumCommand ceilingFanMedium = 
				new CeilingFanMediumCommand(ceilingFan);
		CeilingFanHighCommand ceilingFanHigh = 
				new CeilingFanHighCommand(ceilingFan);
		CeilingFanOffCommand ceilingFanOff = 
				new CeilingFanOffCommand(ceilingFan);
  
		remoteControl.setCommand(0, ceilingFanMedium, ceilingFanOff);
		remoteControl.setCommand(1, ceilingFanHigh, ceilingFanOff);
   
		remoteControl.onButtonWasPushed(0);
		remoteControl.offButtonWasPushed(0);
		System.out.println(remoteControl);
		remoteControl.undoButtonWasPushed();
  
		remoteControl.onButtonWasPushed(1);
		System.out.println(remoteControl);
		remoteControl.undoButtonWasPushed();
	}
}

Head First设计模式 读书笔记之 观察者模式

目标:一个主题类,当主题类发生某种变化的时候,会调用观察者类的一个方法。来达到类似监听的效果,比如图形界面设计中,一个按钮按下去的响应其实就是使用观察者模式实现的。

首先上UML图:

UML图不太会用,暂时用语言补充一下,首先这是一个有关气象的示例代码,一个气象数据类(在观察者模式中是subject),几个公告板(在观察者模式中作为观察者)。要实现的时候当气象数据类发生变化的时候,会调用在气象数据类中注册了的观察者的 update方法,然后观察者(也就是布告板)会经过处理然后显示出来。

气象数据类实现接口subject 接口,公告板类实现 Observer和 DisplayElement接口。其中 DisplayElement接口定义了 display方法,用来显示内容。Observer接口定义了update方法,供气象数据类(subject的实现)调用。

接口 subject定义了三个方法:

	public void registerObserver(Observer o);
	public void removeObserver(Observer o);
	public void notifyObservers();

分别是用来注册、移除和通知观察者。其中 notifyObservers()调用的是观察者的 update()。注册和移除的调用方法是:在new 观察者的时候把subject作为参数传递给观察者的构造函数,然后在构造函数中就可以调用subject的registerObserver了,而且这样并不需要担心性能,因为php对象的传递其实传递的是引用。

下面粘贴具体的代码:
Subject 接口(定义了注册、移除、通知观察者的方法):

package headfirst.observer.weather;

public interface Subject {
	public void registerObserver(Observer o);
	public void removeObserver(Observer o);
	public void notifyObservers();
}

Observer 接口(定义了观察者的update方法):

package headfirst.observer.weather;

public interface Observer {
	public void update(float temp, float humidity, float pressure);
}

DisplayElement 接口(定义了显示方法):

package headfirst.observer.weather;

public interface DisplayElement {
	public void display();
}

WeatherData类,subject接口的实现:

package headfirst.observer.weather;

import java.util.*;

public class WeatherData implements Subject {
	private ArrayList observers;
	private float temperature;
	private float humidity;
	private float pressure;
	
	public WeatherData() {
		observers = new ArrayList();
	}
	
	public void registerObserver(Observer o) {
		observers.add(o);
	}
	
	public void removeObserver(Observer o) {
		int i = observers.indexOf(o);
		if (i >= 0) {
			observers.remove(i);
		}
	}
	
	public void notifyObservers() {
		for (int i = 0; i < observers.size(); i++) {
			Observer observer = (Observer)observers.get(i);
			observer.update(temperature, humidity, pressure);
		}
	}
	
	public void measurementsChanged() {
		notifyObservers();
	}
	
	public void setMeasurements(float temperature, float humidity, float pressure) {
		this.temperature = temperature;
		this.humidity = humidity;
		this.pressure = pressure;
		measurementsChanged();
	}
	
	// other WeatherData methods here
	
	public float getTemperature() {
		return temperature;
	}
	
	public float getHumidity() {
		return humidity;
	}
	
	public float getPressure() {
		return pressure;
	}
}

下面是几个公告板(也就是观察者):
CurrentConditionsDisplay 类:

package headfirst.observer.weather;
	
public class CurrentConditionsDisplay implements Observer, DisplayElement {
	private float temperature;
	private float humidity;
	private Subject weatherData;
	
	public CurrentConditionsDisplay(Subject weatherData) {
		this.weatherData = weatherData;
		weatherData.registerObserver(this);
	}
	
	public void update(float temperature, float humidity, float pressure) {
		this.temperature = temperature;
		this.humidity = humidity;
		display();
	}
	
	public void display() {
		System.out.println("Current conditions: " + temperature 
			+ "F degrees and " + humidity + "% humidity");
	}
}

StatisticsDisplay 类:

package headfirst.observer.weather;

import java.util.*;

public class StatisticsDisplay implements Observer, DisplayElement {
	private float maxTemp = 0.0f;
	private float minTemp = 200;
	private float tempSum= 0.0f;
	private int numReadings;
	private WeatherData weatherData;

	public StatisticsDisplay(WeatherData weatherData) {
		this.weatherData = weatherData;
		weatherData.registerObserver(this);
	}

	public void update(float temp, float humidity, float pressure) {
		tempSum += temp;
		numReadings++;

		if (temp > maxTemp) {
			maxTemp = temp;
		}
 
		if (temp < minTemp) {
			minTemp = temp;
		}

		display();
	}

	public void display() {
		System.out.println("Avg/Max/Min temperature = " + (tempSum / numReadings)
			+ "/" + maxTemp + "/" + minTemp);
	}
}

还有几个观察者,篇幅原因不在粘贴了 .....

下面是两个入口类:
WeatherStation类:

package headfirst.observer.weather;

import java.util.*;

public class WeatherStation {

	public static void main(String[] args) {
		WeatherData weatherData = new WeatherData();
	
		CurrentConditionsDisplay currentDisplay = 
			new CurrentConditionsDisplay(weatherData);
		StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
		ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);

		weatherData.setMeasurements(80, 65, 30.4f);
		weatherData.setMeasurements(82, 70, 29.2f);
		weatherData.setMeasurements(78, 90, 29.2f);
	}
}

WeatherStationHeatIndex类:

package headfirst.observer.weather;

import java.util.*;

public class WeatherStationHeatIndex {

	public static void main(String[] args) {
		WeatherData weatherData = new WeatherData();
		CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);
		StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
		ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);
		HeatIndexDisplay heatIndexDisplay = new HeatIndexDisplay(weatherData);

		weatherData.setMeasurements(80, 65, 30.4f);
		weatherData.setMeasurements(82, 70, 29.2f);
		weatherData.setMeasurements(78, 90, 29.2f);
	}
}

可以看到观察者的创建过程,大家可以编译执行一下,看看显示的结果是否如预期。

Head First设计模式 读书笔记之 策略模式

话说池塘有一群鸭子,其中有各种颜色的,有玩具鸭子,有木头鸭子,有真正的鸭子,鸭子有各种颜色的,红头鸭、绿头鸭等等,有的行为是飞、叫。如果用策略模式如何定义呢?
%e7%ad%96%e7%95%a5%e6%a8%a1%e5%bc%8f

把飞行的行为和呱呱叫的行为分别定义接口,其实就是封装了起来。对于Duck类的子类,可以动态的加载行为类,然后调用相应的方法。不管是加载呱呱叫还是吱吱叫,调用的接口相同,这有点向上转型的意思。

代码:
Duck.java 鸭子的超类

public abstract class Duck {

	FlyBehavior flyBehavior;
	QuackBehavior quackBehavior;

	public Duck() {

	}

	public void setFlyBehavior(FlyBehavior fb) {
		flyBehavior = fb;
	}

	public void setQuackBehavior(QuackBehavior qb) {
		quackBehavior = qb;
	}

	public abstract void display();

	public void performFly() {
		flyBehavior.fly();
	}

	public void performQuack() {
		quackBehavior.quack();
	}

	public void swim() {
		System.out.println("All ducks float, even decoys!");
	}
}

MallardDuck.java 野鸭

public class MallardDuck extends Duck {
	
	public MallardDuck() {
		quackBehavior = new Quack();
		flyBehavior = new FlyWithWings();
	}

	public void display() {
		System.out.println("I'm a real Mallard duck");
	}
}

ModelDuck.java 模型鸭

public class ModelDuck extends Duck {
	public ModelDuck() {
		flyBehavior = new FlyNoWay();
		quackBehavior = new Quack();
	}

	public void display() {
		System.out.println("I'm a model duck");
	}
}

FlyBehavior.java 飞行行为类的接口

public interface FlyBehavior {
	public void fly();
}

FlyNoWay.java 飞行接口的实现 不能飞

public class FlyNoWay implements FlyBehavior {
	public void fly() {
		System.out.println("I can't fly");
	}
}

FlyRocketPowered.java 飞行接口的实现 火箭飞行

public class FlyRocketPowered implements FlyBehavior {
	public void fly() {
		System.out.println("I'm flying with a rocket!");
	}
}

FlyWithWings.java 飞行接口的实现 飞行

public class FlyWithWings implements FlyBehavior {
	public void fly() {
		System.out.println("I'm flying!");
	}
}

QuackBehavior.java 呱呱叫行为接口

public interface QuackBehavior {
	public void quack();
}

Quack.java 呱呱叫接口的实现

public class Quack implements QuackBehavior {
	public void quack() {
		System.out.println("Quack");
	}
}

Squeak.java 呱呱叫接口的实现 吱吱叫

public class Squeak implements QuackBehavior {
	public void quack() {
		System.out.println("Squeak");
	}
}

MuteQuack.java 呱呱叫接口的实现 沉默

public class MuteQuack implements QuackBehavior {
	public void quack() {
		System.out.println("<< Silence >>");
	}
}

MiniDuckSimulator.java 入口类

public class MiniDuckSimulator {
	public static void main(String[] args) {
		Duck mallard = new MallardDuck();
		mallard.performQuack();
		mallard.performFly();

		Duck model = new ModelDuck();
		model.performFly();
		model.setFlyBehavior(new FlyRocketPowered());
		model.performFly();
	}
}

输出:
2017-01-14-11-24-44%e5%b1%8f%e5%b9%95%e6%88%aa%e5%9b%be

不过多解释了,有时候看代码比看图、看注释还一目了然,正所谓好的代码是自解释的。