策略模式的定义:
策略模式作为一种软件设计模式,指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。比如每个人都要“交个人所得税”,但是“在美国交个人所得税”和“在中国交个人所得税”就有不同的算税方法。
策略模式:
定义了一组算法(业务规则);
封装了每个算法;
这族的算法运行时可互换代替(interchangeable)。
应用场景:
1、 多个类只区别在表现行为不同,可以使用Strategy模式,在运行时动态选择具体要执行的行为。
2、 需要在不同情况下使用不同的策略(算法),或者策略还可能在未来用其它方式来实现。
3、 对客户隐藏具体策略(算法)的实现细节,彼此完全独立。
一、策略模式的问题引入:
- 有一家游戏公司,制作一款鸭子游戏.在这个鸭子游戏中,角色都是鸭子,
- 不同的鸭子之间,有共性,所以为了提高代码的重用性。开发人员,就制作了
- 一个鸭子的父类Duck,把这些鸭子的共性上提到一个父类中。
abstract class Duck{
public void quack() {
System.out.println("嘎嘎");
}
public void swim() {
System.out.println("游泳...");
}
public abstract void display();
}
class MallardDuck extends Duck{
public void display() {
System.out.println("外观是野鸭!~");
};
}
class HeadRedDuck extends Duck{
public void display() {
System.out.println("外观是红头鸭!~");
};
}
public class AppTesta {
public static void main(String[] args) {
MallardDuck mallardDuck = new MallardDuck();
mallardDuck.quack();
mallardDuck.display();
mallardDuck.swim();
}
}
此时看不出什么问题。
2.问题发生:橡皮鸭能飞了
- 游戏公司 的老总们开会,得出一个提高本公司竞争游戏竞争力的方案,
- 要求让游戏中的鸭子能飞起来!把其他竞争者远远甩在身后!
*程序员:是时候展现我们面向对象程序员的威力了!我只需要在父类Duck
*中,添加一个fly方法。那么所有Duck的子类,也都共用了fly方法.
*此时:问题看似解决了,但实际上出现了更麻烦的问题。所有的Duck的子类鸭子都会飞了。
*要知道,父类中的方法, 并不是所有子类都能通用的!!比如:橡皮鸭!
*橡皮鸭是没有生命的,不能飞。结果搞得所有的鸭子都能飞了。
abstract class Duck{
public void quack() {
System.out.println("嘎嘎");
}
public void swim() {
System.out.println("游泳...");
}
public void fly() {
System.out.println("我飞~~~");
}
public abstract void display();
}
class MallardDuck extends Duck{
public void display() {
System.out.println("外观是野鸭!~");
};
}
class HeadRedDuck extends Duck{
public void display() {
System.out.println("外观是红头鸭!~");
};
}
class RubberDuck extends Duck{
//因为橡皮鸭不会像真实的鸭子一样叫,所以模拟了橡皮鸭特有的
//叫声。所以重写了父类的quack方法.
public void quack() {
System.out.println("吱吱叫...");
}
@Override
public void display() {
// TODO Auto-generated method stub
System.out.println("外观是橡皮鸭");
}
public void fly() {
System.out.println("橡皮鸭不能飞");
}
}
public class AppTestb {
public static void main(String[] args) {
Duck m = new RubberDuck();
m.quack();
m.display();
m.swim();
m.fly();
}
}
- 看起来,问题好像解决了,但是问题并没有解决,变化总是不断地出现。程序员就要在每次添加新的鸭子角色时,都要判断,新的鸭子角色,会不会叫,会不会飞。针对于不同的鸭子,更有不同的处理方法。这样也很麻烦,只不过是从一个噩梦跌入了另一个噩梦。
3.提出改进措施:
- 针对于b包中的问题,程序员需要判断每个鸭子子类,
- 谁会不会叫,谁会不会飞,
- 不会叫的,就重写quack方法,
- 不会飞的,就重写fly方法,
- 这个工作量是很大的。
abstract class Duck{
public void swim() {
System.out.println("游泳...");
}
public abstract void display();
}
interface quackable{
void quack();
}
interface flyable{
void fly();
}
class MallardDuck extends Duck implements quackable,flyable{
public void display() {
System.out.println("外观是野鸭!~");
}
@Override
public void quack() {
System.out.println("嘎嘎叫");
}
@Override
public void fly() {
System.out.println("我飞");
};
}
class HeadRedDuck extends Duck implements flyable,quackable{
public void display() {
System.out.println("外观是红头鸭!~");
}
@Override
public void quack() {
System.out.println("嘎嘎叫");
}
@Override
public void fly() {
// TODO Auto-generated method stub
System.out.println("我飞!~~~~~");
};
}
class RubberDuck extends Duck implements quackable{
//因为橡皮鸭不会像真实的鸭子一样叫,所以模拟了橡皮鸭特有的
//叫声。所以重写了父类的quack方法.
public void quack() {
System.out.println("吱吱叫...");
}
@Override
public void display() {
// TODO Auto-generated method stub
System.out.println("外观是橡皮鸭");
}
}
class DecoyDuck extends Duck{
@Override
public void display() {
System.out.println("外观是诱饵鸭~~");
}
}
public class AppTestc {
public static void main(String[] args) {
//向上转型发生时,子类独有的方法掉不了.只能把其变成
//子类型,然后再调用fly方法.
RubberDuck m = new RubberDuck();
m.display();
m.swim();
m.quack();
}
}
- 思考:这样问题解决了吗?没有!
- 以前是每加入一个新的鸭子角色,程序员就要判断这个新鸭子角色是否会飞,
- 是否会叫,不会飞的就重写飞方法,不会叫的就重写叫方法。
- 现在是每加入一个新的鸭子角色,程序员就要判断这个新鸭子角色是否会飞,
- 是否会叫,不会飞的就不实现flyable接口,不会叫的就不实现quackable接口。
- 如此,程序员仍然没有减少工作量啊?仍然要不断地判断新鸭子角色。
- 另外一个缺点就是,fly和quack方法没有重用性可言。
- 比如有48种鸭子,有8种不会飞,那么飞方法就要在40个鸭子子类中一共重复40次!
- 此时,应有杠点:
- 老师,从jdk1.8开始,接口中的方法就有默认实现即可,只写1次。
- 解释:对于48种鸭子,有12种飞行方法,又该如何?飞行方法,仍然在子类中重复。
4.引出策略模式:
- 针对于c包中的问题,修改代码如下:
- 是时候,把飞行和叫方法,从鸭子类中分离出来了!注意:这里的分离,与c包中分离不一样。
interface FlyBehavior {
void fly();
}
class FlyWithWings implements FlyBehavior{
@Override
public void fly() {
System.out.println("用翅膀飞~~");
}
}
class FlyWithRocket implements FlyBehavior{
public void fly() {
System.out.println("用火箭飞~~");
}
}
class FlyWithKick implements FlyBehavior{
public void fly() {
System.out.println("被人踢飞~~");
}
}
class FlyNoWay implements FlyBehavior {
@Override
public void fly() {
System.out.println("目前不会飞~~");
}
}
interface QuackBehavior{
void quack();
}
class Quack implements QuackBehavior{
public void quack() {
System.out.println("呱呱叫");
}
}
class Squeak implements QuackBehavior{
public void quack() {
System.out.println("橡皮鸭吱吱叫");
}
}
class MuteQuack implements QuackBehavior{
public void quack() {
System.out.println("不会叫");
}
}
abstract class Duck {
protected FlyBehavior fb;
protected QuackBehavior qb;
public void swim() {
System.out.println("游泳...");
}
public abstract void display();
public void performFly() {
fb.fly();
}
public void performQueak() {
qb.quack();
}
public FlyBehavior getFb() {
return fb;
}
public void setFb(FlyBehavior fb) {
this.fb = fb;
}
public QuackBehavior getQb() {
return qb;
}
public void setQb(QuackBehavior qb) {
this.qb = qb;
}
}
class MallardDuck extends Duck{
public MallardDuck() {
fb = new FlyWithWings();
qb = new Quack();
}
public void display() {
System.out.println("外观是野鸭!~");
}
}
class HeadRedDuck extends Duck {
public HeadRedDuck(){
fb = new FlyWithWings();
qb = new Quack();
}
public void display() {
System.out.println("外观是红头鸭!~");
}
}
class RubberDuck extends Duck {
public RubberDuck() {
fb = new FlyNoWay();
qb = new Squeak();
}
@Override
public void display() {
// TODO Auto-generated method stub
System.out.println("外观是橡皮鸭");
}
}
class DecoyDuck extends Duck{
public DecoyDuck() {
fb = new FlyNoWay();
qb = new MuteQuack();
}
@Override
public void display() {
System.out.println("外观是诱饵鸭~~");
}
}
//=========================方法可以扩展
class FlyWithRobotCat implements FlyBehavior{
public void fly() {
System.out.println("头上绑个竹蜻蜓飞~~");
}
}
public class AppTestd {
public static void main(String[] args) {
//向上转型发生时,子类独有的方法掉不了.只能把其变成
//子类型,然后再调用fly方法.
Duck r = new RubberDuck();
r.performFly();
//**运行时改变了方法行为**
r.setFb(new FlyWithKick());
r.performFly();
r.performQueak();
r.display();
r.swim();
}
}
- 此时,针对于48种鸭子,有12种飞行方法而言,每种飞行方法,写一次!
- 这就是传说中的策略模式。
- 策略模式关键点:不让鸭子直接实现接口,单独让一个类实现接口,让这个类组合到鸭子的具体类中。
- 策略模式体现了组合优于继承原则、开闭原则和单一职责原则。
二、策略模式举例:在策略模式中,运行时替换是其独有的亮点。
设计一款游戏,使得小人在运行时,吃到什么道具,就发出什么攻击。
package q_strategy.a;
//定义一个weapon接口,让道具实现不同的攻击方式.
interface Weapon{
public void attack();
}
class Sword implements Weapon{
//java中如果你没有声明任何构造器,编译器会自动帮你创建个默认无参构造器的
//就是这样,你没有显示的写上一个构造器,那么在你编译的时候编译器就给你创建了
@Override
public void attack() {
// TODO Auto-generated method stub
System.out.println("用剑砍~~");
}
}
class Axe implements Weapon{
@Override
public void attack() {
// TODO Auto-generated method stub
System.out.println("三板斧");
}
}
class Arrow implements Weapon{
@Override
public void attack() {
// TODO Auto-generated method stub
System.out.println("用矛刺");
}
}
class Role{
private String name;
private Weapon weapon;
public Role(String name) {
this.name = name;
}
public Weapon getWeapon() {
return weapon;
}
public void setWeapon(Weapon weapon) {
this.weapon = weapon;
}
//注意这是最关键的一步:fight中的内容不能写死,而是要根据weapon中赋值的不同,
//选择不同的攻击方式。
public void fight() {
weapon.attack();
}
}
//===============================此外,还可以扩充
class Gun implements Weapon{
@Override
public void attack() {
// TODO Auto-generated method stub
System.out.println("用机关枪哒哒哒~~~~");
}
}
public class testa {
public static void main(String[] args) {
Role role = new Role("骑士");
//运行时调用剑
role.setWeapon(new Sword());
role.fight();
role.fight();
role.fight();
//捡到斧头刀具
role.setWeapon(new Axe());
role.fight();
role.fight();
role.fight();
//捡到长矛道具
role.setWeapon(new Arrow());
role.fight();
role.fight();
role.fight();
//捡到枪道具
role.setWeapon(new Gun());
role.fight();
role.fight();
role.fight();
}
}