当我拥有你,无论是在百货公司买领带,还是在厨房收拾一尾鱼,我都觉得幸福。

– 川端康成

策略模式在《Head First 设计模式》中的定义:定义算法簇,分别封装起来,让他们之间可以互相替换;策略模式让算法的变化独立于算法的使用。

练习题

设计几个英雄,每个英雄使用不同的武器进行战斗。

  1. 首先分析:

    所有英雄都是一个(is-a)角色,每个英雄都有一个(has-a)使用武器战斗的技能。那么可以抽象出来一个角色,同时把使用武器进行战斗抽象出来一个行为,并且这个行为在各个具体的英雄上可以自由设定。

  2. 接着UML图就可以设计出来了

    classDiagram
    Character <|-- King
    Character <|-- Queue
    Character <|-- Troll
    Character *-- WeaponBehavior
    
    Character : WeaponBehavior weapon
    Character : setWeapon()
    Character : fight()
    
    King : fight()
    Queue : fight()
    Troll : fight()
    
    WeaponBehavior <|.. SwordBehavior
    WeaponBehavior <|.. KnifeBehavior
    WeaponBehavior <|.. ArrowBehavior
    
    WeaponBehavior : useWeapon()
    SwordBehavior: useWeapon()
    KnifeBehavior: useWeapon()
    ArrowBehavior: useWeapon()
    
  3. 实现代码

    public class Character {//角色类
    
        WeaponBehavior weaponBehavior;//使用武器fight的行为
        void setWeapon(WeaponBehavior weaponBehavior){
            this.weaponBehavior = weaponBehavior;
        }
        void fight(){
            weaponBehavior.useWeapon();//由具体的英雄决定使用什么样的武器去fight
        }
    }
    // ---------------------------------------
    
    public class King extends Character{//英雄1
        public King() {//构造时直接指定武器类
            weaponBehavior =new SwordBehavior();
        }
        @Override
        void fight() {
            System.out.println("我是King");
            super.fight();//直接调用父类的实现,最终由自己的武器来实现fight
            System.out.println("-----");
        }
    }
    // ---------------------------------------
    
    public class Queue extends Character{//英雄2
        @Override
        void fight() {
            System.out.println("我是Queue");
            super.fight();
            System.out.println("-----");
        }
    }
    
    // ---------------------------------------
    
    public interface WeaponBehavior {// 武器接口
        void useWeapon();//定义使用武器的方法,由具体的武器类去实现
    }
    
    // ---------------------------------------
    
    public class SwordBehavior implements WeaponBehavior {// 武器1
        @Override
        public void useWeapon() {
            System.out.println("使用宝剑Fight");
        }
    }
    
    public class KnifeBehavior implements WeaponBehavior{//武器2
        @Override
        public void useWeapon() {
            System.out.println("使用匕首Fight");
        }
    }
    

    测试类

    public class Test {
    
        public static void main(String[] args) {
            King king = new King();
            king.fight();//使用默认(构造时指定)方式fight
            Queue queue = new Queue();
            queue.setWeapon(new KnifeBehavior());//运行时指定武器
            queue.fight();
        }
    }
    

    运行结果

    我是King
    使用宝剑Fight
    -----
    我是Queue
    使用匕首Fight
    -----
    

总结

设计原则:

  1. 封装变化

    找出程序中可能变化的部分,把他们抽取并且封装起来,使其不影响不变的部分

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

    变量的声明应该是超类型(supertype),通常是一个抽象类或者接口,如此,只要是具体实现次此超类型的类锁产生的对象都可以指定给这个变量,这也意味着,声明类时不用理会以后执行时的真正对象类型

  3. 多使用组合,少使用继承