【Java】清除Java中的重复代码-骨架实现

清除Java中的重复代码-骨架实现

Reference:
在 Java 中应用骨架实现
Effective Java - ITEM 18 重组合,轻继承
Effective Java 3 相关博客

Java Skeletal Implementation/Abstract Interfaces(骨架实现/抽象接口)指通过接口和抽象类,集接口多继承的优势与抽象类可以减少重复代码的优势于一体。

Java Collection API 已经采用了这种设计:AbstractSetAbstractMap 等都是骨架实现案例。

Joshua BlochEffective Java书中也提到了骨架接口。

接口和抽象类

  • 接口: 可以实现多继承(extends),但是不能有具体实现

  • 抽象类: 可以有具体实现,但是不能多继承(extends),可以实现(implement)多个接口

场景

对于一个咖啡续命的人来说,每天泡咖啡是常态,假如将我们泡咖啡的行为抽象则主要为以下步骤:

  • 选择咖啡
  • 泡咖啡
  • 喝咖啡

方式一(接口实现)

CoffeeDaily

public interface CoffeeDaily {
    void chooseCoffee();

    void makeCoffee();

    void drinkCoffee();

    void process();
}

InstantCoffee

public class InstantCoffee implements CoffeeDaily{
    @Override
    public void chooseCoffee() {
        System.out.println("today is instant coffee.");
    }

    @Override
    public void makeCoffee() {
        System.out.println("make instant coffee");
    }

    @Override
    public void drinkCoffee() {
        System.out.println("drink instant coffee");
    }

    @Override
    public void process() {
        chooseCoffee();
        makeCoffee();
        drinkCoffee();
    }
}

LatteCoffee

public class LatteCoffee implements CoffeeDaily{
    @Override
    public void chooseCoffee() {
        System.out.println("today is latte coffee.");
    }

    @Override
    public void makeCoffee() {
        System.out.println("make latte coffee");
    }

    @Override
    public void drinkCoffee() {
        System.out.println("drink latte coffee");
    }

    @Override
    public void process() {
        chooseCoffee();
        makeCoffee();
        drinkCoffee();
    }
}

Daily

public class Daily {
    public static void main(String[] args) {
        InstantCoffee instantCoffee = new InstantCoffee();
        LatteCoffee latteCoffee = new LatteCoffee();

        instantCoffee.process();
        System.out.println("-----------------");
        latteCoffee.process();
    }
}

output

today is instant coffee.
make instant coffee
drink instant coffee
-----------------
today is latte coffee.
make latte coffee
drink latte coffee

由于实现接口的类都必须实现接口中定义的方法,所以可能会有重复的方法。

上述例子将所有步骤集中到process()中处理,但是其中其实有很多重复代码,单我们继续添加具体实现时,重复代码也会继续增加。

如果我们将公共的重复代码提取放入一个工具类中,看上去解决了大段重复代码的问题

但是可能会造成Shotgun surgery 问题代码,而且也违反了单一责任原则

Shotgun surgery 是软件开发中的一种反模式,它发生在开发人员向应用程序代码库添加特性的地方,这些代码库会在一次更改中跨越多个实现。

方式二(抽象类)

AbstractCoffeeDaily

public abstract class AbstractCoffeeDaily {

    private String coffeeName;

    public AbstractCoffeeDaily(String coffeeName) {
        this.coffeeName = coffeeName;
    }

    public void chooseCoffee(){
        System.out.println((String.format("today is %s coffee.",coffeeName)));
    }

    public void makeCoffee(){
        System.out.println((String.format("make %s coffee",coffeeName)));
    }

    public void drinkCoffee(){
        System.out.println((String.format("drink %s coffee",coffeeName)));
    }

    protected final void process(){
        this.chooseCoffee();
        this.makeCoffee();
        this.drinkCoffee();
    }

}

InstantCoffee

public class InstantCoffee extends AbstractCoffeeDaily{

    private static final String COFFEE_NAME = "instant";

    public InstantCoffee() {
        super(COFFEE_NAME);
    }
}

LatteCoffee

public class LatteCoffee extends AbstractCoffeeDaily{

    private static final String COFFEE_NAME = "latte";

    public LatteCoffee() {
        super(COFFEE_NAME);
    }
}

Daily

public class Daily {
    public static void main(String[] args) {
        AbstractCoffeeDaily instantCoffee = new InstantCoffee();
        AbstractCoffeeDaily latteCoffee = new LatteCoffee();

        instantCoffee.process();
        System.out.println("-----------------");
        latteCoffee.process();
    }
}

output

today is instant coffee.
make instant coffee
drink instant coffee
-----------------
today is latte coffee.
make latte coffee
drink latte coffee

由于菱形继承问题,Java 不支持多重继承。

菱形继承问题: 两个子类继承同一个父类,而又有子类又分别继承这两个子类,产生二义性问题

InstantCoffeeLatteCoffee都继承AbstractCoffeeDaily,抽取了公用代码,消除了重复的代码。

但是由于Java不支持多重继承,如果我此时想在添加一个外部类引入的方法,如:自动清洗杯子机器类CupCleanMachine

此时的InstantCoffeeLatteCoffee不能再继续继承,如果采用组合(composition)的方式,将CupCleanMachine引入,则会造成强耦合。

方式三(骨架实现或抽象接口)

骨架实现的步骤:

  • 创建接口
  • 创建抽象类实现该接口,并实现公共方法
  • 在具体实现子类中创建一个私有内部类,继承抽象类。将外部调用委托给抽象接口,该类可以在使用通用方法的同时继承和实现其他接口。

接口:CoffeeDaily

public interface CoffeeDaily {
    void chooseCoffee();

    void makeCoffee();

    void drinkCoffee();

    void process();
}

杯子清洗机器:CupCleanMachine

public class CupCleanMachine {
    public void clean(){
        System.out.println("clean the cup.");
    }
}

AbstractCoffeeDaily

public class AbstractCoffeeDaily implements CoffeeDaily{

    private String coffeeName;

    public AbstractCoffeeDaily(String coffeeName) {
        this.coffeeName = coffeeName;
    }

    @Override
    public void chooseCoffee() {
        System.out.println((String.format("today is %s coffee.",coffeeName)));
    }

    @Override
    public void makeCoffee() {
        System.out.println((String.format("make %s coffee",coffeeName)));
    }

    @Override
    public void drinkCoffee() {
        System.out.println((String.format("drink %s coffee",coffeeName)));
    }

    @Override
    public final void process() {
        chooseCoffee();
        makeCoffee();
        drinkCoffee();
    }
}

InstantCoffee

public class InstantCoffee implements CoffeeDaily{
    private static final String COFFEE_NAME = "instant";

    InstantCoffeeDelegator instantCoffeeDelegator = new InstantCoffeeDelegator(COFFEE_NAME);

    @Override
    public void chooseCoffee() {
        instantCoffeeDelegator.chooseCoffee();
    }

    @Override
    public void makeCoffee() {
        instantCoffeeDelegator.makeCoffee();
    }

    @Override
    public void drinkCoffee() {
        instantCoffeeDelegator.drinkCoffee();
    }

    @Override
    public void process() {
        instantCoffeeDelegator.process();
    }

    /** delegator AbstractCoffeeDaily general function */
    private class InstantCoffeeDelegator extends AbstractCoffeeDaily {
        public InstantCoffeeDelegator(String coffeeName) {
            super(coffeeName);
        }

    }
}

LatteCoffee

public class LatteCoffee extends CupCleanMachine implements CoffeeDaily{

    private static final String COFFEE_NAME = "latte";

    LatteCoffeeDelegator latteCoffeeDelegator = new LatteCoffeeDelegator(COFFEE_NAME);

    @Override
    public void chooseCoffee() {
        latteCoffeeDelegator.chooseCoffee();
    }

    @Override
    public void makeCoffee() {
        latteCoffeeDelegator.makeCoffee();
    }

    @Override
    public void drinkCoffee() {
        latteCoffeeDelegator.drinkCoffee();
    }

    @Override
    public void process() {
        latteCoffeeDelegator.process();
        clean();
    }

    /** delegator AbstractCoffeeDaily general function */
    private class LatteCoffeeDelegator extends AbstractCoffeeDaily {
        public LatteCoffeeDelegator(String coffeeName) {
            super(coffeeName);
        }
    }
}

Daily

public class Daily {
    public static void main(String[] args) {
        InstantCoffee instantCoffee = new InstantCoffee();
        LatteCoffee latteCoffee = new LatteCoffee();

        instantCoffee.process();
        System.out.println("-----------------");
        latteCoffee.process();
    }
}

output

today is instant coffee.
make instant coffee
drink instant coffee
-----------------
today is latte coffee.
make latte coffee
drink latte coffee
clean the cup.

上述方法中先创建了一个接口,定义主要的行为,然后创建抽象类实现接口定义所有通用的方法实现。

在具体子类中,实现一个委托类(delegator),通过delegator调用通用的方法实现

这样子类即可以再继承其他类,又可以实现其他的接口,同时也消除了重复的通用方法。

转载于:https://my.oschina.net/u/2254055/blog/3026961

你可能感兴趣的:(【Java】清除Java中的重复代码-骨架实现)