Как отказаться от внутреннего интерфейса - PullRequest
0 голосов
/ 10 мая 2018

Мне нужно отказаться от API в Java SDK, чтобы сделать их более общими. Но я не могу понять, как это сделать для следующего случая:

public class AdoptDog {
    public interface OnDogAdoption {
        public void onDogAdoption(String dogName);
    }
    public void adoptDog(final String dogName, OnDogAdoption callbackObj) {
        // Perform asynchronous tasks...
            // Then call the callback:
            callbackObj.onDogAdoption(dogName);
    }
}

Пользователи SDK совершают звонки следующим образом:

AdoptDog adoptDog = new AdoptDog();
adoptDog.adoptDog("Snowball", new OnDogAdoption {
    @Override
    public void onDogAdoption(String dogName) {
        System.out.println("Welcome " + dogName);
    }
};

Я хочу обобщить из Dog в Pet и отказаться от API, в которых упоминается Dog. Для обратной совместимости приведенный выше фрагмент кода, в котором используется Snowball, не должен изменяться при устаревании API.

Как я пытался устареть в Dog API:

// Introduce Pet API

public class AdoptPet {
    public interface OnPetAdoption {
        public void onPetAdoption(String petName);
    }
    public void adoptPet(final String petName, OnPetAdoption callbackObj) {
        // Perform asynchronous tasks...
            // Then call the callback:
            if (callbackObj instanceof OnDogAdoption) {
                ((OnDogAdoption) callbackObj).onDogAdoption(petName);
            }
            else {
                callbackObj.onPetAdoption(petName);
            }
    }
}

// Dog API now extends Pet API for backward compatibility

@Deprecated
public class AdoptDog extends AdoptPet {
    @Deprecated
    public interface OnDogAdoption extends AdoptPet.OnPetAdoption {
        @Deprecated
        public void onDogAdoption(String dogName);
    }
    @Deprecated
    public void adoptDog(final String dogName, OnDogAdoption callbackObj) {
        super.adoptPet(dogName, callbackObj);
    }
}

Проблема в том, что он не полностью обратно совместим. Пользователи SDK должны реализовать AdoptPet.OnPetAdoption.onPetAdoption (), иначе они получат ошибку компилятора:

AdoptDog adoptDog = new AdoptDog();
adoptDog.adoptDog("Snowball", new OnDogAdoption {
    @Override
    public void onDogAdoption(String dogName) {
        System.out.println("Welcome " + dogName);
    }

    // PROBLEM: How avoid customers having to implement this dummy method? 
    @Override
    public void onPetAdoption(String petName) {
        assert("This code should not be reached");
    }
};

Есть ли какой-то другой способ отказаться от AdoptDog (в частности, OnDogAdoption) и поддерживать полную обратную совместимость?

Ответы [ 2 ]

0 голосов
/ 10 мая 2018

Вы не расширяете OnPetAdoption. A OnDogAdoption не является OnPetAdoption. A OnPetAdoption также может справиться с усыновлением кошки. А OnDogAdoption нельзя.

Я бы посоветовал вам обернуть OnDogAdoption в OnDogAdoptionDispatcher и вызвать новый метод с этим диспетчером, например ::

@Deprecated
public class AdoptDog extends AdoptPet {
    @Deprecated
    public interface OnDogAdoption {
        @Deprecated
        public void onDogAdoption(String dogName);
    }

    private static class DogAdoptionDispatcher implements AdoptPet.OnPetAdoption {
            final OnDogAdoption target;
            public DogAdoptionDispatcher(OnDogAdoption target) {
                this.target = target;
            }
            @Override
            public void onPetAdoption(String petName) {
                target.onDogAdoption(petName);
            }
    }

    @Deprecated
    public void adoptDog(final String dogName, OnDogAdoption callbackObj) {
        super.adoptPet(dogName, new DogAdoptionDispatcher(callbackObj));
    }
}

Таким образом, у вас все еще есть обратная совместимость и чистый новый интерфейс.

Если вы используете Java 8 или выше, вам не нужен отдельный класс для этого, вы можете просто сделать

public void adoptDog(final String dogName, OnDogAdoption callbackObj) {
    super.adoptPet(dogName, callbackObj::onDogAdoption);
}
0 голосов
/ 10 мая 2018

Java 8 позволяет указать default реализации метода.Вы можете использовать это, чтобы помочь вам, например:

@Deprecated
public interface OnDogAdoption extends AdoptPet.OnPetAdoption {
    @Deprecated
    void onDogAdoption(String dogName);

    default void onPetAdoption(String petName) {
        onDogAdoption(petName);
    }
}

Имея реализацию по умолчанию, клиентский код не потребуется для ее реализации (но может, если они захотят), поэтому компиляции не должно бытьошибка.


Примечание: Все методы интерфейса по умолчанию public - фактически они могут быть только public - указывать это не нужно.

...