Как вы переписываете код, который использует дженерики и функционалы в Java 8 и смешивает oop и функциональное программирование, используя только объектно-ориентированное? - PullRequest
6 голосов
/ 08 января 2020

Я сталкивался с этим дизайном, и я не полностью gr asp смешивал объектно-ориентированное программирование с функциональным программированием в Java 8.

Мне было интересно смешивать как языковые парадигмы, так и большинство учебные пособия в inte rnet просты, поэтому я не смог найти пример крупномасштабного программного обеспечения, смешав их. Так что с этим примером я думаю, что у меня была возможность выкопать его для некоторых случаев.

Позвольте мне показать соответствующие части кода в этом случае. Этот код содержит обобщенный класс c FetchUtils, который реализует пользовательский итератор, но для краткости я удалил некоторые части.

public class FetchUtils<R, MSGIN, MSGOUT> {
    public SomeClass<R> getSomething(MSGIN query,
                                Function<MSGIN, MSGOUT> queryFunc,
                                Function<MSGOUT, List<R>> getResultList) {

               //...
                        MSGOUT callResult = queryFunc.apply(query);
                        buffer = getResultList.apply(callResult);
               //...
               //return someThing;
            }
            //...    
}

В клиенте есть определенная функция и лямбда-выражение, указывающее ссылка на getCustomer метод класса. Фактический вызов от клиента вышеупомянутому методу, который использует типы generi c, отправляет эти функционалы.

public class CustomerResponse {
   //...
   public List<Customer> getCustomer() {
        if (thing == null) {
            thing = new ArrayList<Customer>();
        }
        return this.customers;
    }
   //...
}

public class MyClient {

   //...

   @Autowired
   private FetchUtils<Customer, CustomerRequest, CustomerResponse> fetchUtils;

   //...

   public SomeClass<Customer> fetch() {

      //...

      Function<CustomerRequest, CustomerResponse> requestFunc = q -> {
            try {
                return myService.doSomething(q);
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
        };

      CustomerRequest query = new CustomerRequest(/*...*/);

      return fetchUtils.getSomething(query,
                                     requestFunc,
                                     r -> r.getCustomer());

      //...
   }
   //...
}

Как переписать этот код только с помощью объектно-ориентированного программирования, то есть без передачи функций более высокого порядка и только с использованием динамической c диспетчеризации или даже без шаблонов?

Будет ли этот проект возможен без обобщений?

Как здесь работает вывод типов с этими обобщенными c типами и функционалами?

Является ли этот проект возможным примером функционального смешения и объектно-ориентированное программирование или как вы оцениваете этот дизайн?

Ответы [ 2 ]

5 голосов
/ 01 апреля 2020

Как переписать этот код только с использованием объектно-ориентированного программирования, то есть без передачи функций более высокого порядка и только с использованием динамической диспетчеризации или даже без шаблонов?

class FuncImpl implements Function<CustomerRequest, CustomerResponse> {
    public CustomerResponse apply(CustomerResquest q) {
        try {
            return myService.doSomething(q);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }
}
Function<CustomerRequest, CustomerResponse> requestFunc = new FuncImpl();

class FuncImpl2 implements Function<CustomerResponse,List<Customer>> {
    public List<Customer> apply(CustomerResponse r) {
        return r.getCustomer();
    }
}

...
return fetchUtils.getSomething(query, requestFunc,new FuncImpl2());

Будет ли это Возможно ли проектирование без генериков?

Конечно, повсеместно применять конкретный тип. (См. Ниже вывод типа и сделайте его вручную ...).

Как вывод типов работает здесь с этими обобщенными c типами и функционалами?

Параметры Generics это просто переменные типа, поэтому ваш пример очень прост для понимания.

requestFun имеет тип Function<CustomerRequest, CustomerResponse>, тогда компилятор может легко определить, что MSGIN равно CustomerRequest и MSGOUT CustomerResponse.

Для r -> r.getCustomer() компилятор уже знает, что MSGOUT равен CustomerResponse, затем он смотрит на тип CustomerResponse и обнаруживает, что getCustomer возвращает List<Customer>, таким образом, лямбда имеет тип Function<CustomerResponse,List<Customer>>, а затем R это List<Customer>

Является ли этот проект возможным примером смешения функционального и объектно-ориентированного программирования или как вы оцениваете этот дизайн?

Да, это хорошо пример.

3 голосов
/ 04 апреля 2020

Если я правильно понимаю ваш вопрос, под функциями более высокого порядка вы подразумеваете Function, который вы передавали методу getSomething в вашем коде.

Один из способов обдумать это просто представить классы abstract, назначив каждому из них определенную роль. В одном примере следующий код выполняет ту же оценку, что и в совместно используемом вами коде, но назначая разные роли соответствующим отдельным классам.

abstract class Transform<I, O> {
    abstract O queryFunction(I input);
}

abstract class Group<O, R> {
    abstract List<R> groupIntoResult(O output);
}

abstract class FetchUtil<R, I, O> {
    Group<O, R> group;
    Transform<I, O> transform;

    public SomeClass<R> getSomething(I input) {
        O output = transform.queryFunction(input);
        List<R> grouped = group.groupIntoResult(transform.queryFunction(input));
        return new SomeClass<>(grouped);
    }
}

Нет сомнений, что вы также можете преобразовать их в interface в зависимости от использования этих API . Вот где исходный код, который вы имели, был ближе к , использующему класс FunctionalInterface с именем Function.


Чтобы ответить далее, эти представления , не будучи обобщенным c можно легко расширить для обеспечения реализации c, определенной пользователем, следующим образом

class Transformer extends Transform<CustomerRequest, CustomerResponse> {

    @AutoWired
    MySerice myService;

    @Override
    CustomerResponse queryFunction(CustomerRequest input) {
        try {
            return myService.doSomething(input);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }
}

class Grouping extends Group<CustomerResponse, Customer> {
    @Override
    List<Customer> groupIntoResult(CustomerResponse output) {
        return output.getCustomer();
    }
}

Обратите внимание на упрощение задач для вашего клиента с FetchUtil определение сейчас. Для этого нужно только предоставить объект запроса, а вы привязать реализацию преобразования и группировки к классу утилит, который вы написали для обработки ответа.

Ваш клиентский код в конечном итоге сокращается до следующих строк, и вы можете заметить, как это обременяет клиентов, потребляющих ваши услуги:

public class MyClient {
    @AutoWired
    private FetchUtil<Customer, CustomerRequest, CustomerResponse> fetchUtil;
    // bind the above to appropriate Group and Transform implementation

    public SomeClass<Customer> fetch() {
        CustomerRequest query = new CustomerRequest(/*...*/);
        return fetchUtil.getSomething(query);
    }
}
...