JAVA基础(六)行为类设计模式总结

本章开始行为类模式的介绍,行为类模式的核心在于类与对象的交互和职责的分配,本章主要介绍HEAD FIRST中重点讲述的策略模式、观察者模式、命令模式、模板方法和状态模式,至于迭代器模式会在下一章与组合模式一同讲述,一方面这两个模式结合紧密,在HEAD FIRST中也是同一章节,另一方面,其代码复杂度多少有些让人头疼。。。
废话少说,下面开始对上述模式进行介绍
一、策略模式
策略模式Strategy:定义了算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户
策略模式作为Head First的第一个设计模式,介绍了一个设计程序中非常重要的思想:多用封装、少用继承
这是为什么呢?就以Head First中的鸭子为例,想要实现一个鸭子超类,并提供给子类个性化的quack()和fly()功能,书中给出了下面三种方法:
1.1 抽象函数
将quack和fly作为鸭子超类的抽象函数,并由子类继承实现
public abstract class Duck {	//定义抽象函数
	public abstract void fly();
	public abstract void quack();
}
public class RedDuck extends Duck{	//普通鸭子实现
	public void fly() {
		System.out.println("fly away");
	}
	public void quack() {
		System.out.println("gagaga");
	}
}
public class MachineDuck extends Duck{	//机器鸭子实现
	public void fly() {
		System.out.println("I can't fly");
	}
	public void quack() {
		System.out.println("555");
	}
}
这个方法勉强可行,但是显得十分笨拙,随着子类的扩大,函数重复实现代码将越来越多,如果需要实现的函数数目较多,每一个子类的实现简直就是噩梦,可扩展性极差

1.2 继承接口
将quack和fly分别作为接口Quackable和Flyable的抽象函数,并通过子类进行接口继承
public interface flyable {		//fly方法封装为接口
	public abstract void fly();
}
public interface quackable {		//quack方法封装为接口
	public abstract void quack();
}
public class RedDuck extends Duck implements flyable,quackable{		//普通鸭实现类继承接口
	public void fly() {
		System.out.println("fly away");
	}
	public void quack() {
		System.out.println("gagaga");
	}
}
public class MachineDuck extends Duck implements flyable,quackable{	//机器鸭实现类继承接口
	public void fly() {
		System.out.println("I can't fly");
	}
	public void quack() {
		System.out.println("555");
	}
}
这个方法比抽象函数的方法更加笨拙。。。这使得鸭子类不再具备统一的对外接口,而且重复代码的问题完全没有解决
除了上述问题之外,这两个方法还有一个共同的问题,就是不能在运行时改变对象的方法,基于这些问题,策略模式应运而生~~~

1.3策略模式
将quack和fly分别作为接口直接封装到鸭子类内部,并通过子类定义接口的实现方式
首先将接口独立出来并单独实现(quackable接口与此对应,此处略):
public interface flyable {
	public abstract void fly();
}
class duckfly implements flyable{	//普通fly实现
	public void fly() {
		System.out.println("fly away");
	}
}
class mfly implements flyable{		//机器鸭fly实现
	public void fly() {
		System.out.println("I can't fly");
	}
}
之后重构鸭子类的代码和实现过程:
public abstract class Duck {
	flyable f;	//fly接口将在子类定义
	quackable q;	//quack接口将在子类定义
	public void fly(){
		f.fly();	//调用实例化后的接口函数
	}
	public void quack(){
		q.quack();	//调用实例化后的接口函数
	}
}
public class RedDuck extends Duck{
	public RedDuck(){
		f = new duckfly();	//普通鸭实例化fly方法接口
		q = new duckquack();	//普通鸭实例化quack方法接口
	}
}
public class MDuck extends Duck{
	public MDuck(){
		f = new mfly();		//机器鸭实例化fly方法接口
		q = new mquack();	//机器鸭实例化quack方法接口
	}
}
这种封装提供了统一的接口,简洁的代码实现和巨大的灵活性,在超类中加入方法setFlyBehavior和setQuackBehavior后,可以轻易的改变子类的行为方式,让子类“重获新生”
策略模式是“多用组合,少用继承”的完美体现,这个原则在Effective Java中的16条~21条均有很详尽的介绍,由于内容较多,以后有时间会开帖介绍

二、观察者模式
观察者模式Observer:定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会受到通知并自动更新
在Head First书中,观察者模式共有两个案例,即自己编写的版本和java.util.Observer库的版本,在自己编写的版本中,Observable和Observer、Displable分别通过接口实现(此处为了简便删除了Display功能):
2.1 先编写发布者接口:
public interface Observable {
	public abstract void RegistOb(Observer ob);	//注册观察者
	public abstract void deleteOb(Observer ob);	//删除观察者
	public abstract void notifyObs();		//通知所有观察者
}
2.2 编写观察者接口:
public interface Observer {
	public abstract void update(double d);	//简单起见,就改一个参数
}
2.3 编写发布者对象:
public class Subject implements Observable {

	private double usefuldata = 5.5;	//随便设个发布数
	private ArrayList<Observer> obs;	//做个观察者列表,格式任意,此处用ArryList
	public void RegistOb(Observer ob) {	
		obs.add(ob);			//列表直接添加观察者
	}
	public void deleteOb(Observer ob) {
		obs.remove(ob);			//移除观察者
	}
	public void changeData(double d){
		usefuldata = d;			//设置自身的变量,并调用函数通知所有观察者
		notifyObs();
	}
	
	public void notifyObs() {
		for(Observer ob : obs){
			ob.update(usefuldata);	//遍历,调观察者的update函数更新数据
		}
	}
}
2.4 编写观察者对象
public class SimpleObs implements Observer {

	private double d;			
	Observable subject;			//这里记录一下要观察的对象
	public SimpleObs(Observable subject){
		this.subject = subject;		//存储要观察的对象
		subject.RegistOb(this);		//构造初始将记录加入观察列表
	}
	public void update(double d) {
		this.d = d;			//更新自己的数据
	}
}

在java.util.Observer类中,Observable和Observer、Displable分别被定义成了抽象类,并添加了一些方法,如setChanged(),该函数在每次修改参数后可以选择进行调用,作为notifyObservers()的前置条件,可以只在满足特定阈值或某些周期条件下使用,增加了通知观察者时的灵活性,但Observer由于使用了抽象类的方式,导致灵活性和可扩展性大为减弱,所以在有条件的情况下,建议自己进行开发



三、命令模式
命令模式Command:将“请求”封装成对象,以便使用不同的请求,队列或者日志来参数化其他对象。命令模式也支持可撤销的操作
Head First中,使用命令模式实现了遥控器的功能,通过命令模式,可以用统一的接口执行命令,方便的完成队列控制和回撤操作。命令模式的关键是,针对自己的操作,实现command统一接口:
3.1 命令接口
public interface Command {
	public abstract void execute();
}
对于要实现控制器的功能,继承Commad接口并重写方法excute即可

3.2  控制器对象
public class Controller {
	Command[] cmd = new Command[6];
	public void setCommand(int i,Command scmd){	//把想添加的设备直接放入列表
		cmd[i] = scmd;
	}
	public void pushButton(int i){
		cmd[i].execute();			//按按钮直接调用excute函数
	}
}
命令模式本身并不复杂,相比之下更像是模板方法的简化版,通过继承命令接口,控制器对象能够任意的调用使用该接口的设备,实现了接口的对外统一

四、模板方法
模板方法Template Method:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤
初一看来,这个方法本身没有什么复杂的地方,无非就是在抽象类中定义一个总控方法,并对其中的子方法进行抽象,并在子类中将抽象方法进行实现:
public abstract class BeverageStore {
	public final void prepare(){
		boil();
		if(wantCondiments())   addCondiments();
		play();
	}
	abstract void boil();
	void addCondiments(){
		System.out.println("add something");
	}
	void play(){
		System.out.println("give beverage");
	}
	boolean wantCondiments(){	//实现挂钩,动态决定是否执行某些步骤
		return true;
	}
}
稍进一步,我们可以用继承的方法决定某些步骤是否会执行,比如在子类中加入如下的函数:
boolean wantCondiments(){	
		return false;	//子类决定了是否执行原有流程中的某些步骤
}
模板模式看似结构接单,但这种朴素的策略应用却十分广泛,最常见的例子之一就是Array.sort()函数,只要实现了comparable接口,我们就可以直接用该函数完成一组对象的排序,compareTo接口实现比较简单,在这里就不再赘述了


五、状态模式
状态模式State:允许对象在内部状态改变是改变它的行为,对象看起来好像修改了它的类。
对象状态的不同能够导致不同的行为,这需要为每一个对象的状态设置一个类,作为行为的封装,状态类负责不同的行为实现,并在行为内部完成对象的状态转换,这需要状态类和对象本身的相互依赖。需要注意的是,所有的状态应该在对象内部全部生成,而不应该在状态中进行创建,这样才能避免状态类的反复创建
5.1 先看看状态类的定义:
public abstract class State {
	public abstract void func1();	//随便写俩函数,不同的状态实现不同的行为
	public abstract void func2();
}
5.2  机器类定义:
public class Machine {
	State state1 = new State1(this);	//提前定义好所有状态类型,并传入本机
	State state2 = new State1(this);	//同上
	State nstate = state1;			//nstate反映本机实际的状态
	public void func1(){			//func1,func2会调用实际状态的函数
		nstate.func1();
	}
	public void func2(){
		nstate.func2();
	}
}
5.3 状态类的实例化:
public class State1 extends State {
	Machine m;
	public State1(Machine m){	//传入本机,以方便对本机状态进行修改
		this.m = m;
	}
	public void func1() {
		System.out.println("haha,func1");
	}
	public void func2() {
		m.nstate = m.state2;	//重要!!此状态可通过func2转移至另一状态
	}
}
public class State2 extends State {
	Machine m;
	public State2(Machine m){
		this.m = m;
	}
	public void func1() {
		m.nstate = m.state1;	//重要!!此状态可通过func1转移至另一状态
	}
	public void func2() {
		System.out.println("haha,func2");
	}

}
到此,状态机的实现完成了,只要状态的不同,同样的操作就会产生完全不同的行为





  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值