Head First设计模式 第一章:策略模式

版权声明:站在巨人的肩膀上学习。 https://blog.csdn.net/zgcr654321/article/details/83303964

模拟鸭子应用:

继承:

设计一个游戏。里面有各种鸭子。一开始我们创建一个鸭子超类(Suprclass),并让各种鸭子来继承这个类。

如:

对所有鸭子通用的方法由超类来实现,对每个鸭子子类不同的方法由各个子类来实现该方法。

这时,如果我们想让鸭子能飞,我们又在超类里创建了一个fly()方法。

问题来了,新的子类橡皮鸭子也继承了这个方法,橡皮鸭子也能飞,这产生了冲突。

如:

如果一个方法只有某些子类具备而有些子类不具备,此时将这个方法放到超类中就会产生一系列问题。

当涉及“维护”时,为了复用目的而使用继承时,结局并不完美。

接口:

由于fly()方法不是对所有子类都适用,我们考虑将fly()从超类中拿出来,建立一个Flyable接口,只有会飞的鸭子子类才需要实现这个接口。同样也可以设计一个Quackable接口,因为不是所有的鸭子都会叫。 

如:

由于Java接口不具有实现代码,所以继承接口无法达到代码的复用。我们就的给每个会飞的鸭子实现fly方法,代码量会很大。

第一个设计原则:

找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。

或者另一种说法:把会变化的部分取出并封装起来,以便以后可以轻易地改动或扩充此部分,而不影响不需要变化的其他部分。

针对上面鸭子的例子,我们建立两组类(完全远离Duck类), 一个是"fly"相关的, 一个是"quack"相关的,每一组类将实现各自的动作。比方说,我们可能有一个类实现“呱呱叫“,另一个类实现“吱吱叫“,还有一个类实现“安静”。

如:

第二个设计原则:

针对接口编程,而不是针对实现编程。

这次鸭子类不会负责实现Flying与Quacking接口,反而是由我们制造一组其他类专门实现FlyBehavior与QuackBehavior, 这
就称为"行为"类。由行为类而不是Duck类来实现行为接口。

如:

鸭子的FlyBehavior接口下面有一组飞行行为类,每种飞行行为的具体动作不相同。

“ 针对接口编程“ 真正的意思是“ 针对超类型( supertype ) 编程”。“针对超类型编程”这句话,可以更明确地说成“变量的声明类应该是超类型,通常是一个抽象类或者是一个接口,这样只要是具体实现此超类型的子类所产生的对象,都可以指定给这个变量。

多态:

上面这段话的意思其实就是多态。即父类的引用变量指向子类对象。

多态的格式:

父类类型 变量名 = new 子类类型();
变量名.方法名();

所使用的方法往往被在子类中被实现(即覆写了父类的同名方法)。

如:

回到上面的鸭子问题。我们给FlyBehavior和QuackBehavior接口写一组实现类。

如:

这样一来,我们可以让动作被其他对象复用,还可以新增一些行为,既不会影响到已有的行为类,也不会影响到“使用”飞行行为的鸭子类。

关键在于, 鸭子现在会将飞行和呱呱叫的动作“ 委托" ( delegate ) 别人处理, 而不是使用定义在Duck类( 或子类) 内的呱呱叫和飞行方法。

具体做法:

首先,在Duck类中“加入两个实例变量”,分别为"flyBehavior" 与"quackBehavior" , 声明为接口类型(而不是具体类实现类型),每个鸭子对象都会动态地设置这些变量以在运行时引用正确的行为类型(例如: FlyWithWings 、Squeak等)。

我们也必须将Duck类与其所有子类中的fty()与quack()删除,因为这些行为已经被搬到FlyBehavior与QuackBehavior类中了。

我们用两个相似的方法performFly()和perform Quack()取代Duck类中的fly()与quack()。

如:

现在,我们来实现performQuack():

如何设定flyBehavior与quackBehavior的实例变量:

我们现在来看看其中一个子类:绿头鸭

当MallardDuck实例化时,它的构造器会把继承来的quackBehavior实例变量初始化成Quack类型的新实例(Quack是QuackBehavior的具体实现类)。

MallardDuck的构造器也会将flyBehavior实例变昼初始化成FlyWithWings类型的实例(FlyWith Wings是Fly Behavior的具休实现类)。

下面我们来看一下完整的实现:

1、输入并编译下面的Duck类( Duck.java ) 以及两页前的MallardDuck类( MallardDuck.java) 。

2、输入并编译FlyBehavior接口( FlyBehavior.Java) 与两个行为实现类( FlyWithWings .java与FlyNoWay.java) 。

3、输入并编译QuackBehavior接口( QuackBehavior.java ) 及其三个实现类( Quack .java 、MuteQuack .java 、Squeak.java ) 。

4、输入并编译测试类( MiniDuckSimulator.java )。

代码如下:

//Duck.java
public abstract class Duck {
    //父类中声明两个接口FlyBehavior和QuackBehavior的实例变量
	FlyBehavior flyBehavior;
	QuackBehavior quackBehavior;

	public Duck() {
	}

	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() {
        //子类中将两个接口的变量实例化成对应的实现行为子类,方法我们事先已经写好了一组
        //根据你写的子类的不同,选择相应的方法
        //如绿头鸭这个子类叫声应该是呱呱叫即Quack行为类,飞行应该是FlyWithWings行为类
        //Quack行为类实现QuackBehavior接口,FlyWithWings行为类实现FlyBehavior接口
        //Quack行为类覆写了QuackBehavior的quack()方法
        //FlyWithWings行为类覆写了FlyBehavior接口的fly()方法
        //这样在创建一个多态的变量时如果new一个MallardDuck
        //quackBehavior变量和flyBehavior变量会实例化成对应的实现行为子类
        //再调用quack()和fly()方法时调用的是实现行为子类中覆写后的方法
        //这样就实现了不同的鸭子的同一类行为的灵活配置
		quackBehavior = new Quack();
		flyBehavior = new FlyWithWings();
	}

	public void display() {
		System.out.println("I'm a real Mallard duck");
	}
}
//FlyBehavior.Java
public interface FlyBehavior {
	public void fly();
}
//FlyWithWings.java
public class FlyWithWings implements FlyBehavior {
	public void fly() {
		System.out.println("I'm flying!!");
	}
}
//FlyNoWay.java
public class FlyNoWay implements FlyBehavior {
	public void fly() {
		System.out.println("I can't fly");
	}
}
//QuackBehavior.java
public interface QuackBehavior {
	public void quack();
}
//Quack.java
public class Quack implements QuackBehavior {
	public void quack() {
		System.out.println("Quack");
	}
}
//MuteQuack.java
public class MuteQuack implements QuackBehavior {
	public void quack() {
		System.out.println("<< Silence>>");
	}
}
//Squeak.java
public class Squeak implements QuackBehavior {
	public void quack() {
		System.out.println("Squeak");
	}
}
//MiniDuckSimulator.java,最后运行这个.java文件
public class MiniDuckSimulator {
	public static void main(String[] args) {
        //多态的定义格式:就是父类的引用变量指向子类对象
        //即:父类类型 变量名 = new 子类类型();
        //变量名.方法名();
		Duck mallard = new MallardDuck();
        //performQuack()方法和performFly()方法都继承自Duck类
        //这两个方法分别调用quackBehavior变量的quack()方法、flyBehavior变量的fly()方法
        //由于在前面我们已经在MallardDuck子类中将quackBehavior变量和flyBehavior变量实例化成对应的实现行为子类Quack和FlyWithWings,所以实际调用的quack()方法和fly()方法是实现行为子类中覆写的quack()方法和fly()方法
		mallard.performQuack();
		mallard.performFly();
	}
}

运行截图如下:

动态设定行为:

假设我们想在鸭子子类中通过“设定方法(setter method) "来设定鸭子的行为,而不是在鸭子的构造器内实例化。

1、在Duck类中,加入两个新方法:

2、制造一个新的鸭子类型:模型鸭(ModelDuck.java)

3、建立一个新的FlyBehavior行为类型(FlyRocketPowered .java)

4、改变测试类( MiniDuckSimulator.java ) , 加上模型鸭,井使模型鸭具有火箭动力。

在运行时如果想改变鸭子的行为,只需要调用鸭子的setter()方法即可。

代码如下:

//Duck.java
public abstract class Duck {
    //父类中声明两个接口FlyBehavior和QuackBehavior的实例变量
	FlyBehavior flyBehavior;
	QuackBehavior quackBehavior;

	public Duck() {
	}

	public abstract void display();

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

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

    //两个用于改变鸭子行为的方法
	public void setFlyBehavior(FlyBehavior fb) {
		flyBehavior = fb;
	}

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

	public void swim() {
		System.out.println("All ducks float, even decoys!");
	}
}
//MallardDuck.java
public class MallardDuck extends Duck {
	public MallardDuck() {
        //子类中将两个接口的变量实例化成对应的实现行为子类,方法我们事先已经写好了一组
        //根据你写的子类的不同,选择相应的方法
        //如绿头鸭这个子类叫声应该是呱呱叫即Quack行为类,飞行应该是FlyWithWings行为类
        //Quack行为类实现QuackBehavior接口,FlyWithWings行为类实现FlyBehavior接口
        //Quack行为类覆写了QuackBehavior的quack()方法
        //FlyWithWings行为类覆写了FlyBehavior接口的fly()方法
        //这样在创建一个多态的变量时如果new一个MallardDuck
        //quackBehavior变量和flyBehavior变量会实例化成对应的实现行为子类
        //再调用quack()和fly()方法时调用的是实现行为子类中覆写后的方法
        //这样就实现了不同的鸭子的同一类行为的灵活配置
		quackBehavior = new Quack();
		flyBehavior = new FlyWithWings();
	}

	public void display() {
		System.out.println("I'm a real Mallard duck");
	}
}
//FlyBehavior.Java
public interface FlyBehavior {
	public void fly();
}
//FlyWithWings.java
public class FlyWithWings implements FlyBehavior {
	public void fly() {
		System.out.println("I'm flying!!");
	}
}
//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!");
	}
}
//QuackBehavior.java
public interface QuackBehavior {
	public void quack();
}
//Quack.java
public class Quack implements QuackBehavior {
	public void quack() {
		System.out.println("Quack");
	}
}
//MuteQuack.java
public class MuteQuack implements QuackBehavior {
	public void quack() {
		System.out.println("<< Silence>>");
	}
}
//Squeak.java
public class Squeak implements QuackBehavior {
	public void quack() {
		System.out.println("Squeak");
	}
}
//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");
	}
}
//MiniDuckSimulator.java,最后运行这个.java文件
public class MiniDuckSimulator {
	public static void main(String[] args) {
        //多态的定义格式:就是父类的引用变量指向子类对象
        //即:父类类型 变量名 = new 子类类型();
        //变量名.方法名();
		Duck mallard = new MallardDuck();
        //performQuack()方法和performFly()方法都继承自Duck类
        //这两个方法分别调用quackBehavior变量的quack()方法、flyBehavior变量的fly()方法
        //由于在前面我们已经在MallardDuck子类中将quackBehavior变量和flyBehavior变量实例化成对应的实现行为子类Quack和FlyWithWings,所以实际调用的quack()方法和fly()方法是实现行为子类中覆写的quack()方法和fly()方法
		mallard.performQuack();
		mallard.performFly();
        //创建一个新的子类表示模型鸭
        Duck model = new ModelDuck();
        //先看看模型鸭原本的fly方法
		model.performFly();
        //修改模型鸭的fly方法为FlyRocketPowered()方法,new一个FlyRocketPowered行为类重新实例化flyBehavior变量
		model.setFlyBehavior(new FlyRocketPowered());
        //看看模型鸭的fly方法是否更新成功
		model.performFly();
	}
}

运行截图如下:

重新设计后的类结构:鸭子子类继承Duck ,飞行行为实现FlyBehavior接口,呱呱叫行为实现QuackBehavior接口。每一鸭子都有一个FlyBehavior和一个QuackBehavior, 好将飞行和呱呱叫委托给它们代为处理。当你将两个类结合起来使用,如同本例一般,这就是组合(composition) 。

请特别注意类之间的“关系”。关系可以是IS-A (是一个)、HAS-A(有一个)或IMPLEMENTS (实现)。

第三个设计原则:

多用组合,少用继承。使用组合建立系统具有很大的弹性,不仅可将算法族封装成类,更可以“在运行时动态地改变行为" , 只要
组合的行为对象符合正确的接口标准即可。

策略模式:

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

猜你喜欢

转载自blog.csdn.net/zgcr654321/article/details/83303964