Реализация интерфейса в методах (лучшая практика) - PullRequest
0 голосов
/ 29 августа 2018

У меня есть некоторый обратный вызов:

public interface FooCallback {
    boolean doSomething(Foo fooArg);
}

, который реализован как блок anonymouos в методах на уровне сервиса:

public void methodA(Bar barArg) {
    // some bussiness logic like parsing Foo object from Bar for example

    FooCallback cb = new FooCallback() {
        @Override
        public boolean doSomething(Foo fooArg) {
            // some calculation with fooArg and barArg and return boolean value
        }
    }
    saveBar(barArg, cb);
}

saveBar - это метод, который также находится в этом классе и который используется для сохранения объекта Bar в базу данных, зависит от результата doSomething метода FooCallback;

public void saveBar(barArg, cb) {
    // get Foo object from database

    if (cb.doSomething(fooFromDB)) {
        // save barArg into DB
    } else {
        // antother logic
    }
}

У меня есть много методов, таких как methodA в моем классе обслуживания. Блок кода Anonyme также плохо поддается тестированию. Можете ли вы сказать мне, что является лучшей практикой с подобной реализацией? Я выяснил, как переместить содержимое из doSomething в другой метод, но это означает, что каждый метод будет self methodCallback. Как вы думаете? Я использую Java. Спасибо за совет.

1 Ответ

0 голосов
/ 30 августа 2018

Вы правы, что стиль анонимного блока усложнит тестирование, поэтому я бы не рекомендовал его в этом случае. Если вы хотите оставаться как можно ближе к описанному вами подходу, создайте класс, который реализует FooCallback, а затем предоставьте экземпляр этого класса в качестве аргумента конструктору любого класса, содержащего methodA () и saveBar (). Вы ничего не теряете и получаете возможность предоставить фиктивный экземпляр другой реализацией doSomething () во время тестирования.

Я говорю, если бы вы хотели , потому что я не уверен, какое именно преимущество дает весь подход FooCallback в данном примере. Кажется, что все было бы лучше организовано, если бы вы полностью отбросили этот класс и его анонимную реализацию и просто имели метод methodA (), возвращающий логическое значение, используемое для определения необходимости использования saveBar (). Зачем создавать целый интерфейс только для того, чтобы он содержал метод, который возвращает логическое значение, вместо того, чтобы просто обрабатывать это непосредственно в FooSaveHandler или как там это все вызывается?

Изменить, чтобы перейти с комментарием:

Если у вас есть A-X отличные наборы логики, которые вам нужно вызывать для Foo в разных ситуациях, и у вас есть логика, которая условно вызывает эти отдельные A-X методы, почему бы не сделать что-то более простое?

handleFooBarLogic(Foo foo){
    boolean saveBar = false;
    //Logic that originally called methodA()
    if(something) {
        return saveBar = doSomethingOriginallyFromMethodA(foo);
    }
    //etc.
    return saveBar
}    

saveBar(Bar bar) {
       //Just saves a Bar
    }

Если мы говорим о лучших (или, по крайней мере, о лучших) практиках, ваш saveBar () делает гораздо больше, чем просто сохраняет Бар, и к этому моменту уже должно было быть определено, что Бар должен быть сохранен. Работа в обратном направлении от этой предпосылки полностью исключает необходимость обратного вызова и интерфейса вместе с ним. В итоге вы получите большое гудящее утверждение, но, по крайней мере, все станет более проверяемым, и в конечном итоге будет легче понять, что происходит.

...