在开发大规模的Java项目时,通常会涉及到各种设计模式,本文简要说明当使用模板方法时,如何利用接口default方法或抽象类对具体的子类进行精简,便于开发与维护。

背景说明

假设部门要进行一次团建,主要包含如下3个活动:

  • 轰趴(play)
  • 聚餐(eat)
  • 唱歌(sing)

这3个按照时间顺序先后进行,大家可以选择参加启动的一个或多个,则可以创建一个类似如下的类来表示上述流程

public abstract class TeamBuilding {

    public void participate() {
        play();
        eat();
        sing();
    }

    public abstract void play();

    public abstract void eat();

    public abstract void sing();

}

上述代码中participate()方法表示参加此次活动,里面包含了3个抽象方法,表示 邀请了所有人,被邀请人员可以选择性参加其中一个或多个活动。

按照正常思维实际使用时需要继承该类并分别实现该类中的方法,即使不参加其中某一项活动,对应的实现类参考如下

public class PersonA extends TeamBuilding {

    @Override
    public void play() {
        System.out.println("play card");
    }

    @Override
    public void eat() {
        System.out.println("eat cake");
    }

    @Override
    public void sing() {
        // 不参加该项活动,实现为空方法
    }
}

public class PersonB extends TeamBuilding {

    @Override
    public void play() {
        // 不参加该项活动,实现为空方法
    }

    @Override
    public void eat() {
        System.out.println("eat cake");
    }

    @Override
    public void sing() {
        System.out.println("sing a song");
    }
}

public class PersonC extends TeamBuilding {

    @Override
    public void play() {
        System.out.println("play basketball");
    }

    @Override
    public void eat() {
        // 不参加该项活动,实现为空方法
    }

    @Override
    public void sing() {
        System.out.println("sing a song");
    }
}

可以看出相关的实现类中,有很多空的实现方法,形成了冗余,不便于维护。

同时在模板方法中每增加一个步骤,所有的子类中都需要实现方法,改动量大,也不便于维护。

public abstract class TeamBuilding {

    public void participate() {
        play();
        drink();
        eat();
        sing();
    }

    public abstract void play();

    // 新增一个方法后,对应的子类都需要实现该方法
    public abstract void drink();

    public abstract void eat();

    public abstract void sing();

}

期望实现的效果是对应的子类中是实现自己关注的流程,且添加新的步骤时,子类不用全部改动,类似如下

public class PersonA extends TeamBuilding {

    @Override
    public void play() {
        System.out.println("play card");
    }

    @Override
    public void eat() {
        System.out.println("eat cake");
    }
    
}

public class PersonB extends TeamBuilding {

    @Override
    public void eat() {
        System.out.println("eat cake");
    }

    @Override
    public void sing() {
        System.out.println("sing a song");
    }
}

public class PersonC extends TeamBuilding {

    @Override
    public void play() {
        System.out.println("play basketball");
    }

    @Override
    public void sing() {
        System.out.println("sing a song");
    }
}

接下来以抽象类和接口默认方法分别说明如何设计父类以便达成上述目的。

抽象类实现

仿照Java多线程编程中的AbstractQueuedSynchronizer类里面的获取锁与释放锁的实现,在父类中给所有的方法都取消abstract修饰符,默认用UnsupportedOperationException实现全部方法,子类按需override即可。

AbstractQueuedSynchronizer相关实现 :

protected boolean tryAcquire(int arg) {
    throw new UnsupportedOperationException();
}

protected boolean tryRelease(int arg) {
    throw new UnsupportedOperationException();
}

相关代码如下:

public abstract class TeamBuilding {

    public void participate() {
        play();
        eat();
        sing();
    }

    protected void play() {
        throw new UnsupportedOperationException();
    }

    protected void eat() {
        throw new UnsupportedOperationException();
    }

    protected void sing() {
        throw new UnsupportedOperationException();
    }

}

接口实现

JDK8和以上版本中,也可将抽象类修改为接口,利用接口默认方法实现,相关代码如下:

public interface TeamBuilding {

    default void participate() {
        play();
        eat();
        sing();
    }

    default void play() {
        throw new UnsupportedOperationException();
    }

    default void eat() {
        throw new UnsupportedOperationException();
    }

    default void sing() {
        throw new UnsupportedOperationException();
    }

}

对应的子类实现如下:

public class PersonA implements TeamBuilding {

    @Override
    public void play() {
        System.out.println("play card");
    }

    @Override
    public void eat() {
        System.out.println("eat cake");
    }

}

public class PersonB implements TeamBuilding {

    @Override
    public void eat() {
        System.out.println("eat cake");
    }

    @Override
    public void sing() {
        System.out.println("sing a song");
    }
}

public class PersonC implements TeamBuilding {

    @Override
    public void play() {
        System.out.println("play basketball");
    }

    @Override
    public void sing() {
        System.out.println("sing a song");
    }
}