Java 抽象类中变与不变的行为设计

问题提出:

在实际业务开发过程中,对于同样一种事件可能有不同的处理行为,而且在开发完成之后,后期可能会添加新的处理行为,如何在不影响旧有的业务代码增加新的行为是本文需要探讨的问题。

举个栗子:

有个模拟鸭子游戏,在游戏中有各种鸭子,鸭子可以一边游泳戏水,一边呱呱叫,然后我们还要展示鸭子的颜色等等。

已知条件:

1、所有的鸭子都会游泳;

2、有的鸭子会呱呱叫,有的鸭子不会呱呱叫;

3、后期可能会添加其他行为。

思路分析:

通常对于既有变化的行为和不变化的行为,我们通过一个抽象类来实现,在抽象类中将不变的游泳实现出来,这样所有继承该抽象类的类将不需要再去重复该代码,对于不确定的行为呱呱叫写一个抽象方法进行,具体的实现由它所继承的类来实现。

可知:上述将所有的行为都通过方法进行实现。

缺点:如果要添加一个行为(该行为具有不确定性),则在抽象类中添加一个抽象方法,所有实现该抽象类的类都要去实现该接口(注意:抽象类也是接口的一种,可以作为继承类的父类),而有些实现该抽象类的类可能并不需要该行为。

解决办法:区分出可变行为,并将可变行为封装成接口类,在抽象类,通过字段声明(实现has a 而不是 is a),以及相应的方法来表现该行为,行为的具体实现类,在具体实现抽象类中的类中进行调用。这样的有点,后期在增加新的行为时,不会影响到旧有代码的逻辑,达到低耦合的目的。

举个栗子:

Duck.java

public abstract class Duck {

    QuackBehavior quackBehavior;
    FlyBehavior flyBehavior;

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

    public void setQuack(QuackBehavior quackBehavior){
        this.quackBehavior = quackBehavior;
    }

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

    public void setFly(FlyBehavior flyBehavior){
        this.flyBehavior = flyBehavior;
    }

    protected void swim(){
        System.out.println("I can swim!");
    }

    protected abstract void display();
}

分析:

在Duck.java 类这中,游泳行为swim()是鸭子实现类共有的特征且一定会游泳,因此,将其实现出来。而display()是所有鸭子的共有特征,但是不同鸭子的行为不一样,因此,将其抽象出来,所有继承该抽象方法的鸭子都要实现该方法。鸭子的飞行行为和叫的行为,并不是所有鸭子的共有特征,因此,将其声明为接口,这样,所有实现该抽象类的类可以按需要不要去关心鸭子的飞行和叫的行为,达到低耦合的目的。比如,此时,如果有些鸭子需要跳舞的行为,则可以同样添加一个跳舞的接口。

补充:

关于接口中的行为有两种:

一种是获取接口的行为,如:performFly();

另一种是改变接口的行为,如:setFly() (改变行为实现对鸭子的行为进行动态设置)。

FlyBehavior.java类:

public interface FlyBehavior {
    void fly();
}

两个实现FlyBehavior.java类如下:

Fly.java类:

public class Fly implements FlyBehavior {
    @Override
    public void fly() {
        System.out.println("I can fly!");
    }
}

NoFly.java 类:

public class NoFly implements FlyBehavior {
    @Override
    public void fly() {
        System.out.println("I can't fly!");
    }
}

QuackBehavior.java类:

public interface QuackBehavior {
    void quack();
}

两个实现QuackBehavior.java类如下:

Quack.java 类:

public class Quack implements QuackBehavior {
    @Override
    public void quack() {
        System.out.println("I can quack!");
    }
}

NoQuack.java 类:

public class NoQuack implements QuackBehavior {
    @Override
    public void quack() {
        System.out.println("I can't quack!");
    }
}

一个Duck.java的实现类如下:

MallardDuck.java 类:

public class MallardDuck extends Duck {

    public MallardDuck(){
        quackBehavior = new Quack();
        flyBehavior = new Fly();
    }

    @Override
    protected void display() {
        System.out.println("I am green!");
    }
}

分析:

上述实现类对于Duck.java的实现类中,在构造函数中实例化鸭子的飞行行为和叫的行为。另外:对于鸭子的飞行行为和叫的行为可以在后期通过set函数进行更改。

实际调用的main方法如下:

public static void main(String[] args) {
    Duck mallard = new MallardDuck();
    mallard.performFly();
    mallard.setFly(new NoFly());
    mallard.performFly();
}

输出:

I can fly!

I can't fly!

实现了运行时动态更改鸭子的飞行行为。

 

总结分析:

抽象类中的三种行为:

1、所有继承该抽象类都会有该行为,且行为都一样。(具体方法实现)

2、所有继承该抽象类都会有该行为,但行为都不一样。(抽象方法)

3、只有部分继承该抽象类会有该行为。(面向接口编程)

 

使用模式最好的方式是:“把模式装进脑子里,然后在你的设计和已有的应用中,寻找何处可以使用它们。”以往是代码复用,现在是经验复用。

 

OO原则:
1)封装变化(代码复用,低耦合)

2)多用组合,少用继承(推荐has a ,少用is a)

3)针对接口编程,不针对实现编程


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值