Java设计模式研究

库与框架无法帮助我们将应用组织成容易了解、容易维护、具有弹性的架构,所以需要设计模式。

模式不是发明,而是发现。模式不是代码,而是方案。

以下模式被认为是历经验证的OO设计经验。


1. 观察者模式

1) 类图



2) 定义

定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它所有依赖者都会收到通知并自动更新


3)设计原则 

为了交互对象之间的松耦合设计而努力。


4)实现原理

Subject主题类是事件的主导者,通过registerObserver()注册上有需求的观察者,

通过NotifyObserver()实时更新自己状态并告知已经注册的各位观察者。

observer.update(); 由每位观察者自行处理更新的数据。

观察者构造函数里指定主题,并调用该主题的registerObserver()方法实现绑定。

public class WeatherData implements Subject {
	private ArrayList observers;
		
	public WeatherData() {
		observers = new ArrayList();
	}

	public void registerObserver(Observer o) {
		observers.add(0);
	}

	public void removeObserver(Observer o) {
		int i = observers.indexOf(o);
		if (i >= 0) {
			observers.remove(i);
		}
	}

	public void notifyObserver() {
		for (int i = 0; i < observers.size(); i++) {
			Observer observer = (Observer)observers.get(i);
			observer.update(temperature, humidity, pressure);
		}
			
	}

}
public class CurrentCondition {
	private Subject weatherData;

	public CurrentCondition (Subject weatherData) {
		this.weatherData = weatherData;
		weatherData.registerObserver(this);
	}

	public void update(float t, float h, float p) {
		display(t, h, p);
	}

}

5)点评

如果让观察者去“拉”主题的数据,这样主题会门户大开,被大肆挖掘数据,不够安全。

所以更常用的设计是主题主动“推”送数据给观察者,主题不关心观察者的具体情况,实现松耦合。


6)应用场景

一对多的关系,多个对象实时监听一个对象的状态。

系统级更常见,比如建立一个对象,监听按键,传感器数据等。



2. 策略模式

1) 类图


2) 定义

定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。


3)设计原则 

针对接口编程,而不是针对实现编程。多用组合,少用继承。

把系统中会变化的部分抽离出来封装


4)实现原理

在这个类中加一个接口,把行为委托给这个接口处理,

在这个接口下有各种不同的策略实现它的行为,

客户只要通过set和get动态地设置变量就可以在运行时引用正确的策略。

public abstract class Duck {
	FlyBehavior flyBehavior;

	public Duck () {

	}

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

	public FlyBehavior getFlyBehavior() {
		return flyBehavior;
	}

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

}


public class FlyWithWings implements FlyBehavior {
	public void fly() {
		... ...
	}

}


5)点评

策略模式使用委托模型,增加了对象数量,使代码较复杂,但使用对象组合,所以更具有弹性。


6)应用场景

适用于需要经常更换策略的场景。
典型的一个例子:游戏人物换装备,一类装备只能选一种。


3. 状态模式

1) 类图

基本同策略模式,唯一区别在于委托的状态是一个抽象类而不是接口。


2) 定义

允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。


3)点评

将一群行为封装在状态对象中,context的行为随时可委托到那些状态对象中的一个,

状态是用在Context中来代表它的内部状态以及行为的,客户不会直接改变context的状态,改变状态是方案中事先定义好的。


4)应用场景

适用于同一行为在不同条件下产生不同表现的场景,把条件看成状态,或者策略。


4. 装饰者模式

1) 类图


2) 定义

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


3)设计原则 

类应该对扩展开放,对修改关闭。


4)实现原理

新功能以装饰者的身份呈现,让装饰者继承被装饰者,
装饰者构造函数里把被装饰者当做参数传入,再由构造器将其记录在实例变量中。
public class Mocha extends CondimentDecorator {
	Beverage beverage;

	public Mocha(Beverage beverage) {
		this.beverage = beverage;
	}

	public double cost(){
		return .20 + beverage.cost();
	}

}

5)点评

装饰者与被装饰者必须是同一类型,用继承达到类型匹配。

装饰者模式的设计中加入了大量的小类,会让程序变得复杂难懂。


6)应用场景

对象须要添加一个新功能,新功能具有弹性,易于修改更新替代或叠加,同时不会改变原有对象。

典型例子比如JAVA I/O里的读写功能。



5. 适配器模式

1) 类图



2) 定义

将一个类的接口,转换成客户期望的另一个接口,适配器让原本接口不兼容的类可以合作无间。

3)实现原理

客户通过目标接口调用适配器的方法对适配器发出请求。

适配器把请求转换成被适配者的接口。

客户接收到调用的结果,但并未察觉适配器在中间的作用。

public class EnumerationIterator implements Iterator
{
	Enumeration enum;

	public EnumerationIterator(Enumeration enum) {
		this.enum = enum;
	}


	public boolean hasNext() {
		return enum.hasMoreElements();
	}

	public Object next() {
		return enum.nextElement();
	}

	public void remove() {
		throw new UnsupportedOperationException();
	}

}


4)点评

如果不用适配器,客户就必须改写代码来调用新的接口。

适配器允许客户使用新的库和子集合而无须改变代码,由适配器负责转换。


5)应用场景

有新的客户需求,不想改变客户代码,又想保留原有系统接口,那么考虑用适配器模式作中间层转换。



6. 外观模式

1) 类图



2)定义

提供了一个统一的接口,用来访问子系统中的一群接口,外观定义了一个高层接口,让子系统更容易使用。


3)设计原则

最少知识”原则:只和你的密友谈话。

就任何对象而言,在该对象的方法内,我们只应该调用属于以下范围的方法:

  • 该对象本身
  • 被当做方法的参数而传递进来的对象
  • 此方法所创建或实例化的任何对象
  • 对象的任何组件

4) 点评
外观模式没有封装于系统的类,外观只提供简单的接口,依然将系统完整的功能暴露出来。
外观不只简化了接口,也将客户从组件的子系统中解耦。

5)应用场景

某对象具备复杂的功能,客户希望使用简单的高层接口。

典型的例子如家庭影院系统。



7. 工厂方法模式

1) 类图



2) 定义

定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个,工厂方法让类把实例化推迟到子类。


3)设计原则

依赖倒置原则:要依赖抽象,不要依赖具体类。

不能让高层组件依赖低层组件,而且两者都应该依赖于抽象。

遵循倒置原则应注意:

  • 变量不可以持有具体类的引用
  • 不要让类派生自具体类
  • 不要覆盖基类中已实现的方法

4)实现原理

首先声明一个工厂方法factroyMethod(),工厂方法将客户和实际创建具体产品的代码分隔开来。

子类工厂继承抽象工厂,子类工厂的工厂方法具体实现低层组件-产品。

public abstract class PizzaStore {
	public Pizza orderPizza(String type) {
		Pizza pizza;

		pizza = createPizza(type);

		pizza.prepare();
		pizza.bake();
		pizza.cut();
		pizza.box;

		return pizza;
	}

	abstract Pizza createPizza(String type);
}

public class NYPizzaStore extends PizzaStore {
	Pizza createPizza(String item) {
		if (item.equals("cheese")) {
			return new NYStyleCheesePizza();
		} else if (item.equals("veggie")) {
			return new NYStyleVeggiePizza();
		} else if (item.equals("clam")) {
			return new NYStyleClamPizza();
		} else if (item.equals("pepperoni")) {
			return new NYStylePepperoniPizza();
		} else 
			return null;
	}
}
客户只要实例化具体的子类工厂,让子类工厂生产产品。

public static void main () {
	PizzaStore nyStore = new NYPizzaStore();
	Pizza pizza = nyStore.orderPizza("cheese");
}


5)点评

工厂方法让子类决定要实例化的类是哪一个,选择使用哪个子类就决定了实际创建哪个产品。

将创建对象的代码集中在一个对象或方法中,可以避免代码的重复,且方便维护。

客户在实例化对象时,只会依赖于接口,而不是具体类。

除了工厂方法模式,还有抽象工厂模式,有利于创建对象的家族。



8. 单件模式

1) 定义

确保一个类只有一个实例,并提供一个全局访问点。


2)实现原理

public class Singleton {
	private static Singleton uniqueInstance;

	private Singleton() {}

	public static Singleton getInstance() {
		if (uniqueInstance == null)
			uniqueInstance = new Singleton();

		return uniqueInstance;
	}
}


3)应用场景

独一无二的资源,比如线程池,缓存,对话框,偏好,日志,设备驱动等。

这些对象可能非常耗资源,而且初始化后程序一直没有使用它。



9. 命令模式

1) 类图



2) 定义

将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象,命令模式也支持可撤销的操作。

3)实现原理

关键要理清客户,发起者Invoker,接收者Receiver三者的关系以及命令对象Command与具体动作receiver.Action()之间的关系。

  • 客户提出请求 - 客户创建一个具体命令对象,这个命令对象暴露execute()接口
  • 发起者封装请求 - 发起者由setCommand()持有用户提出的具体命令,并在某个时刻调用命令对象的execute(),实施动作
  • 接收者实现请求 - 具体命令对象定义了动作receiver.Action()和接收者之间的绑定关系

调用者只要调用execute()就可以发出请求,由具体命令调用接收者的一个或多个动作。

public interface Command {
	public void execute();
}

public class LightOnCommand implements Command { //具体命令
	Light light;

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

	public void execute() {
		light.on();  // 具体动作
	}
}

public class SimpleRemoteControl {  //调用者
	Command slot;

	public SimpleRemoteControl () {}

	public void setCommand(Command command) {  //调用者持有用户的具体命令
		slot = command;
	}

	public void buttonWasPressed() {  //实施动作的时刻
		slot.execute();
	}
}

public class RemoteControlTest {
	public static void main() {
		SimpleRemoteControl remote = new SimpleRemoteControl();  
		Light light = new Light();
		LightOnCommand lightOn = new LightOnCommand(light);  //用户创建具体命令

		remote.setCommand(lightOn);
		remote.buttonWasPressed();
	}
}

4)点评

一旦有新的动作加入,发起者并不需要改变。


5)应用场景

典型例子:多功能on/off按钮,记录状态可撤销动作,使用宏命令的party模式

更多应用:队列请求,日志请求


10. 模板方法模式

1) 类图



2) 定义

在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。


3)设计原则

好莱坞原则:别调用我们,我们会调用你。


4)点评

当子类必须提供算法中的某个方法或步骤的实现时,就使用抽象方法。

如果算法的这个部分是可选的,就用钩子,要不要挂钩由子类决定。

模板方法模式是最常用的模式。


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值