继承与实现的决择,策略模式 strategy

继承引发的问题

子类继承父类,主要目的是为了复用父类的方法

这里写图片描述

代码结构

这里写图片描述

对应相关代码

Animal

package headfirst.hd.strategy.base;

public class Animal {

    //呼吸
    public void breath() {
        System.out.println("我是动物,我用鼻子呼吸");
    }

    //看
    public void look() {
        System.out.println("我是动物,我用眼睛看");
    }

}

Dog

package headfirst.hd.strategy.base;

public class Dog extends Animal {

}

Person

package headfirst.hd.strategy.base;

public class Person extends Animal {

}

DriveTest

package headfirst.hd.strategy.run;

import headfirst.hd.strategy.base.Dog;
import headfirst.hd.strategy.base.Person;

public class DriveTest {

    public static void main(String[] args) {
        Person person = new Person();
        person.breath();
        person.look();

        Dog dog = new Dog();
        dog.breath();
        dog.look();

    }

}

符合继承
-子类具有相同的行为,这里person(人),dog(狗)都具有呼吸,看行为
-当子类具有相同行为,且具有相同的实现,这里person(人),dog(狗)都是用鼻子呼吸,用眼睛看强调内容

子类具有相同的行为,不具有相同的实现

动物都有move(移动)方法,但是person(人)使用两只脚移动,dog(狗)是用四只脚移动
这里我们引入接口,如下图所示
这里写图片描述
对应相关代码
Animal

package headfirst.hd.strategy.interfaces;

public interface Animal {
    void move();
}
Dog
package headfirst.hd.strategy.impl;

import headfirst.hd.strategy.interfaces.Animal;

public class Dog implements Animal {

    @Override
    public void move() {
        System.out.println("我是一个只狗,我用四只脚走路");
    }

}

Person

package headfirst.hd.strategy.impl;

import headfirst.hd.strategy.interfaces.Animal;

public class Person implements Animal {

    @Override
    public void move() {
        System.out.println("我是一个人,我用两只脚走路");
    }

}

DriveTest2

package headfirst.hd.strategy.run;

import headfirst.hd.strategy.impl.Dog;
import headfirst.hd.strategy.impl.Person;

public class DriveTest2 {

    public static void main(String[] args) {
        Person person = new Person();
        person.move();

        Dog dog = new Dog();
        dog.move();

    }

}

引入抽象类,使Animal具有三个行为

Animal当前有breath,look,move方法,breath与look是父类提供实现,子类共同拥有,move方法是父类提供行为,子类负责具体实现。
这里写图片描述

 对应相关代码
 Animal
package headfirst.hd.strategy.base;

public abstract class Animal {

    //呼吸
    public void breath() {
        System.out.println("我是动物,我用鼻子呼吸");
    }

    //看
    public void look() {
        System.out.println("我是动物,我用眼睛看");
    }

    public abstract void move();

}
Dog
package headfirst.hd.strategy.base;

public class Dog extends Animal {

    @Override
    public void move() {
        System.out.println("我是一个只狗,我用四只脚走路");
    }

}

Person

package headfirst.hd.strategy.base;

public class Person extends Animal {

    @Override
    public void move() {
        System.out.println("我是一个人,我用两只脚走路");
    }

}

DriveTest2

package headfirst.hd.strategy.run;

import headfirst.hd.strategy.base.Dog;
import headfirst.hd.strategy.base.Person;

public class DriveTest2 {

    public static void main(String[] args) {
        Person person = new Person();
        person.breath();
        person.look();
        person.move();

        Dog dog = new Dog();
        dog.breath();
        dog.look();
        dog.move();

    }

}

引入抽象类,使Animal具有三个行为

Animal当前有breath,look,move方法,breath与look是父类提供实现,子类共同拥有,move方法是父类提供行为,子类负责具体实现。
这里写图片描述

 对应相关代码
 Animal
package headfirst.hd.strategy.base;

public abstract class Animal {

    //呼吸
    public void breath() {
        System.out.println("我是动物,我用鼻子呼吸");
    }

    //看
    public void look() {
        System.out.println("我是动物,我用眼睛看");
    }

    public abstract void move();

}
Dog
package headfirst.hd.strategy.base;

public class Dog extends Animal {

    @Override
    public void move() {
        System.out.println("我是一个只狗,我用四只脚走路");
    }

}

Person

package headfirst.hd.strategy.base;

public class Person extends Animal {

    @Override
    public void move() {
        System.out.println("我是一个人,我用两只脚走路");
    }

}

DriveTest2

package headfirst.hd.strategy.run;

import headfirst.hd.strategy.base.Dog;
import headfirst.hd.strategy.base.Person;

public class DriveTest2 {

    public static void main(String[] args) {
        Person person = new Person();
        person.breath();
        person.look();
        person.move();

        Dog dog = new Dog();
        dog.breath();
        dog.look();
        dog.move();

    }

}

引入implements,使Animal具有三个行为

Animal当前有breath,look,breath与look是父类提供实现,子类共同拥有,move方法是子类通过实现其他接口,获取行为,子类负责具体实现。
这里写图片描述

 对应相关代码

 Moveable
package headfirst.hd.strategy.interfaces;

public interface Moveable {
    void move();
}
package headfirst.hd.strategy.base;

public class Animal {

    //呼吸
    public void breath() {
        System.out.println("我是动物,我用鼻子呼吸");
    }

    //看
    public void look() {
        System.out.println("我是动物,我用眼睛看");
    }

}
Dog
package headfirst.hd.strategy.base;

import headfirst.hd.strategy.interfaces.Moveable;

public class Dog extends Animal implements Moveable {

    @Override
    public void move() {
        System.out.println("我是一个只狗,我用四只脚走路");
    }

}

Person

package headfirst.hd.strategy.base;

import headfirst.hd.strategy.interfaces.Moveable;

public class Person extends Animal implements Moveable {

    @Override
    public void move() {
        System.out.println("我是一个人,我用两只脚走路");
    }

}

DriveTest2

package headfirst.hd.strategy.run;

import headfirst.hd.strategy.base.Dog;
import headfirst.hd.strategy.base.Person;

public class DriveTest2 {

    public static void main(String[] args) {
        Person person = new Person();
        person.breath();
        person.look();
        person.move();

        Dog dog = new Dog();
        dog.breath();
        dog.look();
        dog.move();

    }

}

不符合继承

  • 新增加子类,不再满足原子类具有相同的行为
  • 父类添加新行为,子类部分满足,部分不满足

继承问题1-新增加子类,不再满足原子类具有相同的行为

新增鱼子类,鱼不是用鼻子呼吸的,这里我认为是用鱼鳃呼吸的,继承了错误的行为,因为鱼不是用鼻子呼吸的。
新增蝙蝠子类,蝙蝠是不能用眼睛看到,继承了不该拥有的行为,,但是他可以用耳朵听,这里添加listen方法
使用abstract方式关系图说明,图如下

这里写图片描述

解决蝙蝠bat拥有不该拥有的look行为
关键方法使用implement方式关系图说明,图如下
这里写图片描述

两种方式的区别

  • 继承,继承了错了方法,要把错误方法修改或者去掉,使用重写父类方法方式,简单理解可以理解为在做减法,把不合适的行为减掉
  • implement,为子类添加方法,如果子类有该行为,则实现该接口,简单理解可以理解为做加法,为子类添加行为

但是在类规模大的情况下,两种方法后期维护都极其困难,现在假设一种情景。当前有一个父类,父类下有20个子类。为父类添加A方法,其中,子类中,有10个子类与父类A方法相同,5个子类与A方法不同,需要修改,5个子类没有A方法。
1. 用继承方法,那么子类需要重写10个子类,其中5个需要重写新行为,5个需要覆盖掉行为
2. 用implement方法,那么子类需要实现15个子类,其中10个子类实现代码一致,没有达到代码复用,如果这10个子类实现后期有变化,需要修改10个子类的实现

综合以上分析,继承与实现都无法满足我们的要求,幸运的是,有一种设计模式能解决这个问题

策略模式 strategy

为什么会有模式,因为我们需求总是在不断变化,导致我们不得不修改代码,这个模式可以应对这些问题的发生,现在,在这里将这些问题总结出来。

  1. 父类拥有A方法,新增子类,子类没有A方法,或许说,需要重写的A方法,大前提,新增子类不影响之前的代码
  2. 父类增加B方法,子类部分满足B方法,部分不满足,上面已经描述了这个情况,很复杂,如何解决代码复用大前提,新增B方法不影响之前的代码
  3. 父类A方法,实现发生变化,我们只希望修改一处代码
  4. 子类A方法,实现发生变化(可能子类有几个类实现都是相同的),我们只希望修改一处代码

个人总结,strategy模式,将变化的方法,独立封装成接口

封装变化

将子类中,变化大的方法封装,这里先将breath方法封装起来,因为子类中有不同的实现。

使用下图模型说明,这是改造前的UML图
这里写图片描述

对应代码
Animal

package headfirst.hd.strategy.base;

public abstract class Animal {

    //呼吸
    public void breath() {
        System.out.println("我是动物,我用鼻子呼吸");
    }

    public abstract void move();
}

Lookable

package headfirst.hd.strategy.interfaces;

public interface Lookable {
    void look();
}

Dog

package headfirst.hd.strategy.base;

import headfirst.hd.strategy.interfaces.Lookable;

public class Dog extends Animal implements Lookable {

    @Override
    public void move() {
        System.out.println("我是一个只狗,我用四只脚走路");
    }

    @Override
    public void look() {
        System.out.println("我是动物,我用眼睛看");
    }

}

Person

package headfirst.hd.strategy.base;

import headfirst.hd.strategy.interfaces.Lookable;

public class Person extends Animal implements Lookable {

    @Override
    public void move() {
        System.out.println("我是一个人,我用两只脚走路");
    }

    @Override
    public void look() {
        System.out.println("我是动物,我用眼睛看");
    }
}

Fish

package headfirst.hd.strategy.base;

import headfirst.hd.strategy.interfaces.Lookable;

public class Fish extends Animal implements Lookable {

    @Override
    public void move() {
        System.out.println("我是一只鱼,我用手划水移动");
    }

    @Override
    public void look() {
        System.out.println("我是动物,我用眼睛看");
    }

    @Override   //重写父类方法,鱼不是鼻子呼吸的
    public void breath() {
        System.out.println("我是一只鱼,我用鱼鳃呼吸");
    }
}

Bat

package headfirst.hd.strategy.base;

//蝙蝠不能用眼睛看,不实现看行为
public class Bat extends Animal {

    @Override
    public void move() {
        System.out.println("我是一只蝙蝠,我用翅膀飞行移动");
    }

    //蝙蝠使用耳朵听得,增加listen方法
    public void listen() {
        System.out.println("我是一只蝙蝠,我可以用耳朵判断距离");
    }
}

测试类DriveTest2

package headfirst.hd.strategy.run;

import headfirst.hd.strategy.base.Bat;
import headfirst.hd.strategy.base.Dog;
import headfirst.hd.strategy.base.Fish;
import headfirst.hd.strategy.base.Person;

public class DriveTest2 {

    public static void main(String[] args) {

        Person person = new Person();
        person.breath();
        person.look();
        person.move();

        Dog dog = new Dog();
        dog.breath();
        dog.look();
        dog.move();

        Fish fish = new Fish();
        fish.move();
        fish.look();
        fish.breath();

        Bat bat = new Bat();
        bat.move();
        bat.breath();
        bat.listen();

    }

}

运行结果
这里写图片描述

代码符合我们预期设计

改造breath方法

将breath方法封装成独立的接口

这里写图片描述

对应代码
Breathable

package headfirst.hd.strategy.interfaces;

public interface Breathable {
    void breath();
}

NormalBreath

package headfirst.hd.strategy.impl;

import headfirst.hd.strategy.interfaces.Breathable;

public class NormalBreath implements Breathable {

    @Override // 呼吸
    public void breath() {
        System.out.println("我是动物,我用鼻子呼吸");
    }
}

FishBreath

package headfirst.hd.strategy.impl;

import headfirst.hd.strategy.interfaces.Breathable;

public class FishBreath implements Breathable {

    @Override // 呼吸
    public void breath() {
        System.out.println("我是一只鱼,我用鱼鳃呼吸");
    }
}

Animal

package headfirst.hd.strategy.base;

import headfirst.hd.strategy.interfaces.Breathable;

public abstract class Animal {

    Breathable breathable;

    //呼吸
    public void breath() {
        breathable.breath();
    }

    public abstract void move();
}

Lookable

package headfirst.hd.strategy.interfaces;

public interface Lookable {
    void look();
}

Dog

package headfirst.hd.strategy.base;

import headfirst.hd.strategy.interfaces.Lookable;

public class Dog extends Animal implements Lookable {

    @Override
    public void move() {
        System.out.println("我是一个只狗,我用四只脚走路");
    }

    @Override
    public void look() {
        System.out.println("我是动物,我用眼睛看");
    }

}

Person

package headfirst.hd.strategy.base;

import headfirst.hd.strategy.interfaces.Lookable;

public class Person extends Animal implements Lookable {

    @Override
    public void move() {
        System.out.println("我是一个人,我用两只脚走路");
    }

    @Override
    public void look() {
        System.out.println("我是动物,我用眼睛看");
    }
}

Fish

package headfirst.hd.strategy.base;

import headfirst.hd.strategy.interfaces.Lookable;

public class Fish extends Animal implements Lookable {

    @Override
    public void move() {
        System.out.println("我是一只鱼,我用手划水移动");
    }

    @Override
    public void look() {
        System.out.println("我是动物,我用眼睛看");
    }

    @Override   //重写父类方法,鱼不是鼻子呼吸的
    public void breath() {
        System.out.println("我是一只鱼,我用鱼鳃呼吸");
    }
}

Bat

package headfirst.hd.strategy.base;

//蝙蝠不能用眼睛看,不实现看行为
public class Bat extends Animal {

    @Override
    public void move() {
        System.out.println("我是一只蝙蝠,我用翅膀飞行移动");
    }

    //蝙蝠使用耳朵听得,增加listen方法
    public void listen() {
        System.out.println("我是一只蝙蝠,我可以用耳朵判断距离");
    }
}

测试类DriveTest2

package headfirst.hd.strategy.run;

import headfirst.hd.strategy.base.Bat;
import headfirst.hd.strategy.base.Dog;
import headfirst.hd.strategy.base.Fish;
import headfirst.hd.strategy.base.Person;

public class DriveTest2 {

    public static void main(String[] args) {

        Person person = new Person();
        person.breath();
        person.look();
        person.move();

        Dog dog = new Dog();
        dog.breath();
        dog.look();
        dog.move();

        Fish fish = new Fish();
        fish.move();
        fish.look();
        fish.breath();

        Bat bat = new Bat();
        bat.move();
        bat.breath();
        bat.listen();

    }

}

测试结果
这里写图片描述

改造breath方法后的优点

抛出之前预留的问题,来看看改造后的方法是否能够满足,这里针对breath方法

  1. 父类拥有A方法,新增子类,子类没有A方法,或许说,需要重写的A方法,大前提,新增子类不影响之前的代码
  2. 父类增加B方法,子类部分满足B方法,部分不满足,上面已经描述了这个情况,很复杂,如何解决代码复用大前提,新增B方法不影响之前的代码
  3. 父类A方法,实现发生变化,我们只希望修改一处代码
  4. 子类A方法,实现发生变化(可能子类有几个类实现都是相同的),我们只希望修改一处代码
  • 新增Snail (蜗牛)子类,蜗牛是通过皮肤呼吸的,对于蜗牛,我们只用为其添加新的行为实现SnailBreath ,不会影响已经实现的代码,满足条件1

SnailBreath

package headfirst.hd.strategy.impl;

import headfirst.hd.strategy.interfaces.Breathable;

public class SnailBreath implements Breathable {

    @Override // 呼吸
    public void breath() {
        System.out.println("我是一只蜗牛,我用皮肤呼吸");
    }
}
  • 在breath不涉及情况2,pass
  • 父类定义需要修改,我们只需要修改定义一个地方,子类所有都会被修改到,例如,呼吸前,我们的有些激素要先分泌一下,满足情况3
package headfirst.hd.strategy.base;

import headfirst.hd.strategy.interfaces.Breathable;

public abstract class Animal {

    Breathable breathable;

    //呼吸
    public void breath() {
        System.out.println("分泌激素");

        breathable.breath();
    }

    public abstract void move();
}
  • 当通过鼻子呼吸方式实现,需要修改,我们只需要修改NormalBreath一个地方,即可,(反例,当需要修改用眼睛看look方法,我们需要修改Person,Dog,Fish等多处),满足情况4

改造look方法

将look方法封装成独立的接口,为蝙蝠(bat)也封装一个方法,如图标1所示,改造后UML图

这里写图片描述

对应代码,这里已改造步骤展示代码

  • 将有变化的方法封装成独立的接口,且子类维护一组实现
    Lookable
package headfirst.hd.strategy.interfaces;

public interface Lookable {
    void look();
}

NormalLook

package headfirst.hd.strategy.impl;

import headfirst.hd.strategy.interfaces.Lookable;

public class NormalLook implements Lookable {

    @Override
    public void look() {
        System.out.println("我是动物,我用眼睛看");
    }

}

CannotLook

package headfirst.hd.strategy.impl;

import headfirst.hd.strategy.interfaces.Lookable;

public class CannotLook implements Lookable {

    @Override
    public void look() {
        System.out.println("我是蝙蝠,我不能用眼睛看");
    }

}
  • 改造含有业务的父类,为其添加一个改造后的接口属性,并调用接口方法
    Animal
package headfirst.hd.strategy.base;

import headfirst.hd.strategy.interfaces.Breathable;
import headfirst.hd.strategy.interfaces.Lookable;

public abstract class Animal {

    Breathable breathable;
    Lookable lookable;   //添加属性

    //呼吸
    public void breath() {
        breathable.breath();
    }

    //看
    public void look() {  //调用接口
        lookable.look();
    }

    public abstract void move();
}
  • 改造业务子类

Person

package headfirst.hd.strategy.base;

import headfirst.hd.strategy.impl.NormalBreath;
import headfirst.hd.strategy.impl.NormalLook;

public class Person extends Animal {

    public Person() {
        breathable = new NormalBreath();
        lookable = new NormalLook();
    }

    @Override
    public void move() {
        System.out.println("我是一个人,我用两只脚走路");
    }
}

Fish

package headfirst.hd.strategy.base;

import headfirst.hd.strategy.impl.FishBreath;
import headfirst.hd.strategy.impl.NormalLook;

public class Fish extends Animal {

    public Fish() {
        breathable = new FishBreath();
        lookable = new NormalLook();
    }

    @Override
    public void move() {
        System.out.println("我是一只鱼,我用手划水移动");
    }
}

Dog

package headfirst.hd.strategy.base;

import headfirst.hd.strategy.impl.NormalBreath;
import headfirst.hd.strategy.impl.NormalLook;

public class Dog extends Animal {

    public Dog() {
        breathable = new NormalBreath();
        lookable = new NormalLook();
    }

    @Override
    public void move() {
        System.out.println("我是一个只狗,我用四只脚走路");
    }
}

Bat

package headfirst.hd.strategy.base;

import headfirst.hd.strategy.impl.CannotLook;
import headfirst.hd.strategy.impl.NormalBreath;

//蝙蝠不能用眼睛看,不实现看行为
public class Bat extends Animal {

    public Bat() {
        breathable = new NormalBreath();
        lookable = new CannotLook();
    }

    @Override
    public void move() {
        System.out.println("我是一只蝙蝠,我用翅膀飞行移动");
    }

    //蝙蝠使用耳朵听得,增加listen方法
    public void listen() {
        System.out.println("我是一只蝙蝠,我可以用耳朵判断距离");
    }
}

测试类
DriveTest2

package headfirst.hd.strategy.run;

import headfirst.hd.strategy.base.Bat;
import headfirst.hd.strategy.base.Dog;
import headfirst.hd.strategy.base.Fish;
import headfirst.hd.strategy.base.Person;

public class DriveTest2 {

    public static void main(String[] args) {

        Person person = new Person();
        person.breath();
        person.look();
        person.move();

        Dog dog = new Dog();
        dog.breath();
        dog.look();
        dog.move();

        Fish fish = new Fish();
        fish.move();
        fish.look();
        fish.breath();

        Bat bat = new Bat();
        bat.look();
        bat.move();
        bat.breath();
        bat.listen();

    }

}

这里写图片描述

满足业务场景

改造look方法后的优点

抛出之前预留的问题,来看看改造后的方法是否能够满足,这里针对look方法

  1. 父类拥有A方法,新增子类,子类没有A方法,或许说,需要重写的A方法,大前提,新增子类不影响之前的代码
  2. 父类增加B方法,子类部分满足B方法,部分不满足,上面已经描述了这个情况,很复杂,如何解决代码复用大前提,新增B方法不影响之前的代码
  3. 父类A方法,实现发生变化,我们只希望修改一处代码
  4. 子类A方法,实现发生变化(可能子类有几个类实现都是相同的),我们只希望修改一处代码

ps: 1,3,4点优点和breath方法一致,这里我们谈谈2

假设look方法是父类新增的方法,子类中有三个具有该行为,一个不具有,我们将其设计为如下图,解决问题

这里写图片描述

现在假设,新增一个子类,他是用其他器官来看,我们只需要改变仅仅做如下改变,不会影响之前的任务代码
这里写图片描述

完美解决问题2

是否有必要改造move方法

这里写图片描述

基于当前子类,由于当前每个子类move方法实现都不同,没有任务重合度,没有任何复用,反之,观察NormalLook,NormalBreath子类,业务子类中有多个子类复用,如果后期增加猫,大象,老虎等这些和狗一样都是四只脚走路的,代码实现重复时,可考虑这种模式

总结

以上就是策略模式 strategy
用到了面向对象的三个原则

  • 封装变化
    策略模式封装的是变化的方法,后期我们还会封装变量等(状态模式)
  • 多用组合,少用继承
    这里写图片描述
    多用图中圈出来这种调用设计关系,组合
  • 针对接口编程,不针对具体的实现编程
    这里写图片描述
    这是一个很完美的诠释,具体实现在子类,子类会选择何种实现,如蝙蝠bat
    这里写图片描述

自己体会吧

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值