多态和策略模式

今天我们继续来讲解一下有关于多态和策略模式

多态

说到多态,我们都知道这是java的特性(继承,封装,多态,抽象)之一,但是我们对他了解多少呢?

我们首先了解一下它的概念:

相同类型的变量,调用同一个方法时呈现出多种不同的行为特征,主要是通过继承来实现,将子类对象直接赋给父类引用变量,这样在编译时引用变量类型是父类类型,而运行时类型是子类类型,导致运行时调用该引用变量的方法总会表现子类方法,而调用其引用变量的属性则会表现出父类属性。

好吧,我承认我一开始也看懵了.

我们首先逐字分析,进行最简单的分析,一个父类,一个子类,两者是父子关系(继承).
我们都知道java中有编译期和运行期.
我们在代码编写的时候代码会自动编译(eclipse除外),如果某个地方出错了,工具会报红.
下面给出代码测试

//父类
public class Parent {
    private String name;
    public void say(){
        System.out.println("我似隔壁老王~!!!");
    }
}

public class Child extends Parent {
    @Override
    public void say() {
        super.say();
        //在子类中新添加的语句
        System.out.println("呼叫老王!!我是小王,我以后也是要成为隔壁老王的男银~~~");
    }

    //我们在子类child中创建一个方法
    public void  run(){
        System.out.println("老王老王!!!我是小王独有的媳妇,你追不到@!!!!!!");
    }
}

public static void main(String[] args) {
        //我们创建对象,此时创建的是子类Child对象但是指向的父类Parent的引用
        Parent ChildorParent = new Child();
        ChildorParent.say();
        //此时明明子类Child中有一个独有的run方法,但是此时在编译期却无法调用
        ChildorParent.run();//此时这个方法是无法调出来的,可以看见run爆红了,此时是无法运行的.
    }

下面为上面测试方法打印输出的信息:

我似隔壁老王~!!!  
呼叫老王!!我是小王,我以后也是要成为隔壁老王的男银~~~

那我们可以这么理解:
我们new子类对象,但是引用却指向父类的时候,我们运行时只能调用父类的方法,但是运行时确实调用的子类的经过重写父类后的方法.

此时有的人会说,直接new父类用父类接收,或者直接new 子类 用子类接收结果又是如何呢?
 public static void main(String[] args) {
        //我们创建对象,此时创建的是子类Child对象但是指向的父类Parent的引用
        Parent ChildorParent = new Child();
        ChildorParent.say();
        //此时明明子类Child中有一个独有的run方法,但是此时在编译期却无法调用
        //ChildorParent.run();//此时这个方法是无法调出来的,可以看见run爆红了,此时是无法运行的.
        System.out.println("---------------我是和谐的分界线-----------------");
        //new 子类并用子类接收
        Child child = new Child();
        child.say();//重写父类的方法1
        System.out.println("------------子类方法1和2的分界线------------");
        child.run();//自身的方法2
        System.out.println("---------------我是和谐的分界线-----------------");
        //new 父类并用父类接收
        Parent parent = new Parent();
        parent.say();//父类的方法
    }

下面为输出的内容:

我似隔壁老王~!!!
呼叫老王!!我是小王,我以后也是要成为隔壁老王的男银~~~
---------------我是和谐的分界线-----------------
我似隔壁老王~!!!
呼叫老王!!我是小王,我以后也是要成为隔壁老王的男银~~~
------------子类方法1和2的分界线------------
老王老王!!!我是小王独有的媳妇,你追不到@!!!!!!
---------------我是和谐的分界线-----------------
我似隔壁老王~!!!
通过上面的输出我们可以得到很多东西:
  1. new 子类引用指向子类:
    这种情况下我们此时可以调用子类的任何方法(包含子类重写父类的方法,不重写肯定是调不了的),编译期和运行期都没有问题.此时如果调用子类重写父类的方法,最终会输出子类重写后的方法.
  2. new 父类引用指向父类:
    这种情况下我们只能调用父类的方法,子类的是无法调用的,编译期和运行期皆可调用.
  3. new 子类引用指向父类:
    这种情况下编译期和运行期是不同的,编译期时:由于引用指向的是父类,此时只能调用父类拥有的方法,不包含子类(比如子类中的run()),如果强行写出run方法是无法运行的.含义是此时编译期调用的是父类的方法.
    对于运行期:我们看输出结果,输出的确实经过子类重写父类方法后的结果,我们可以这么总结,运行期时实际上调用的是子类的方法
进而我们进行总结:

对于多态而言:
1.继承 2.方法重写 3.父类引用指向子类对象
我们延伸一下,如果A类继承B类,B类继承C类,C类继承D类等等…我们可以选择B,C,D进行子类引用指向父类.这样我们可以定向的选择指定父类的方法或者多个方法.

策略模式

我们首先了解一下策略模式
策略: 将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化.
看完之后我是一脸懵逼的状态,不过还是从其中看到了一个共同接口字眼.

那我们怎么理解这个策略模式呢?

这个时代的人基本都玩过对战游戏,当然现在最火的莫过于lol(英雄联盟),那我们可以针对lol中的英雄进行分析.

分析:
lol中有上百个英雄,那我们怎么进行设计呢?  
第一步:
我们可以创建一个英雄class,让他作为父类,不同的英雄直接继承他并且根据实际情况进行技能的重写即可.

在这里插入图片描述
做到这一步我们需要考虑了,每个英雄的QWER技能确实是不一样的,这样写的确没什么矛盾.

但是针对于召唤师技能而言,无论哪个英雄所使用的的召唤师技能无非就是那几个中的两个,那这种情况我们如何解决呢? 重写的话考虑是不是有点代码重复~
第二步:
设计原则:找出应用可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。

每一个不同的英雄类基本使用的召唤师技能都是从固定的几个中获取的,那么我们可以考虑做一个召唤师技能接口,所有的召唤师技能都需要实现这个类.
在这里插入图片描述

设计原则:针对接口编程,而不是针对实现编程。

这样做其实可以把所有固定的召唤师技能先写好,等玩家选择召唤师技能只需要设置具体的召唤师技能即可。就算以后有新的召唤师技能只需要实现这个接口就好了,具体实现类可以互相的替换。

第三步:
把第一步和第二步组合之后会是什么状态呢?  
设计原则:多用组合,少用继承。

在这里插入图片描述

策略总结:
首先定义了算法(召唤师技能接口),分别封装起来(具体的实现类:传送、治疗术,清晰术等),让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户,这就是所谓的策略模式。

其他的实现方式

一. 接口的实现方式
public interface Move {
    
    void move();
}

并让抽象父类实现它

public abstract class Car implements Move{
    //汽车品牌
    private String brand;

    public Car(String brand) {
        this.brand = brand;
    }
}
这样所有继承Car的具体汽车类都必须实现自己的move方法,也就是让具体的汽车子类来决定汽车的具体行为:到底是使用汽油运行还是使用电池运行。但是这么做至少有以下几个缺点:
  1. 具体的汽车运行行为不方便后期维护。因而move行为无法被复用,具体的实现都分散在了子类中。如果要对某种驱动方式的实现进行修改,不得不修改所有子类,这简直是灾难。
  2. 导致类数量的膨胀。同样品牌的汽车,由于有汽油和电动两种运行方式,不得不为其维护两个类,如果在增加一种驱动方式,比如氢能源驱动,那不得为每个品牌的汽车再增加一个类。
  3. 不方便move行为的扩展,也不方便动态的更换其实现方式。
二.if-else的实现方式
move方法接受客户端传递的参数,通过if-else或者swich-case进行判断,选择正确的驱动方式。
public void move(String moveStrategy) {
    if("electricity".equals(moveStrategy)){
        System.out.println(" Use Electricity Move!");
    }else if("gasoline".equals(moveStrategy)){
        System.out.println(" Use Gasoline Move!");
    }
}

但这样做相当于硬编码,不符合开闭原则。比如我要增加一种氢能源的驱动方式,这种实现就需要修改move中的代码。而如果使用上面说的策略模式,则只需要增加一个实现实现策略接口的具体策略实现类,而不需要修改move中的任何代码,即可被客户端所使用。

public class HydrogenMovetrategy implements MoveStrategy {
    @Override
    public void move() {
        System.out.println(" Use Hydrogen Move!");
    }
}
由上可以总结得知策略模式的优点:

1.可以优化类结构,当类的某种功能有多种实现时,可以在类中定义策略接口,将真正的功能实现委托给具体的策略实现类。这样避免了类膨胀,也能更好的进行扩展和维护。

2.避免使用多重条件判断导致的硬编码和扩展性差的问题

3.可以使具体的算法实现自由切换,增强程序设计的弹性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值