Шаблон строителя вместо перегрузки метода - PullRequest
1 голос
/ 04 июля 2019

У меня есть интерфейс TaxCalculator, который выглядит таки одна внутренняя система У меня есть третья реализация, которая действует как прокси-сервер - эффективно при каждом вызове он работает так:

@Override
    public SalesTaxRates getSalesTaxes(ShoppingCart cart, Address address)
    {
        Map<String,SalesTaxRates> cache = getTaxSessionCache();
        String cacheKey = generateCacheKey(cart,address);
        if(cache.containsKey(cacheKey))
            return cache.get(cacheKey);
        TaxCallable taxCallable = new TaxCallable() {
            @Override
            public SalesTaxRates call() {
                return this.getTaxCalculator().getSalesTaxes(cart,address);
            }
        };
        SalesTaxRates taxRates = callTaxCalculators(taxCallable);
        if(taxRates.isCacheable())
            cache.put(cacheKey, taxRates);
        return taxRates;
    }

И метод, определенный:

private static SalesTaxRates callTaxCalculators(TaxCallable callable)
    {
        for (TaxCalculator taxCalculator: TaxEngine.getTaxCalculators())
        {
            FutureTask<SalesTaxRates> futureTask = new FutureTask<>(callable.setTaxCalculator(taxCalculator));
            new Thread(futureTask).start();
            try {
                SalesTaxRates taxRates = futureTask.get(getTaxCalculatorTimeout(), TimeUnit.MILLISECONDS);
                if(taxRates!=null && taxRates.isCalculated())
                {
                    BHLogger.info("Used taxcalculator: " + taxRates.getTaxEngine() + "rate: " + taxRates.getTotalTaxRate());
                    return taxRates;
                }
            } catch (Exception e) {
                BHLogger.error(e);
            }
        }
        BHLogger.error("ShoppingCart.calculteSalesTax","No taxCalculator calculated taxes correctly");
        return SalesTaxRates.NOT_CALCULATED;
    }

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

Я думаю, это будет выглядеть так

class TaxRequest{
private Order order;
private Cart cart;
private Address address;
private String zipcode;
private SalesTaxRates send(TaxCalculator tc){
   //check cache
   //else
   tc.getSalesTaxes(this);
   //handlecaching
}
}

Интерфейсу нужен только один метод, и класс Forwarding становится очень простым, я думаю, реализация ApiTaxCalculator сейчаснеобходимо справиться с этим, имея такой метод

SalesTaxRates getSalesTax(TaxRequest taxRequest)
{
   if(taxRequest.hasOrder()
       returnGetSalesTaxes(taxRequest.getOrder);
  //if has address and cart... if has only address... if has just a zip...
}

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

Любой совет?

1 Ответ

0 голосов
/ 05 июля 2019

Посмотрите на это:

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

class StackOverflowQuestion56893388Scratch {
    static class SalesTaxRates {
        //TODO: don't know anything about it
    }

    interface WithZipCode {
        String getZipCode();
    }

    interface WithShipping {
        Optional<Integer> getShippingCosts();
    }

    interface WithDiscount {
        Optional<Integer> getDiscount();
    }

    static class ZipCode implements WithZipCode {
        /*TODO: return some field*/
        @Override
        public String getZipCode() { return null; }
    }

    //I chose arbitrary interfaces here as I don't know more about your domain
    static class ShoppingCart implements WithZipCode, WithShipping {
        /*TODO: return some field*/
        @Override
        public String getZipCode() { return null; }

        /*TODO: return some field*/
        @Override
        public Optional<Integer> getShippingCosts() { return Optional.empty(); }
    }

    //I chose arbitrary interfaces here as I don't know more about your domain
    static class Order implements WithZipCode, WithShipping, WithDiscount {
        /*TODO: return some field*/
        @Override
        public String getZipCode() { return null; }

        /*TODO: return some field*/
        @Override
        public Optional<Integer> getShippingCosts() { return Optional.empty(); }

        /*TODO: return some field*/
        @Override
        public Optional<Integer> getDiscount() { return Optional.empty(); }
    }

/////////////////////////////////TAXCALCULATOR///////////////////////////////////////////

    static class TaxCalculator {
        public SalesTaxRates getSaleTaxes(String zipCode) {
            return getSaleTaxes(() -> zipCode);
        }

        public SalesTaxRates getSaleTaxes(WithZipCode zipCode) {
            return getSaleTaxesInternal((AllTaxRelatedInformation) zipCode::getZipCode);
        }

        public SalesTaxRates getSaleTaxes(ShoppingCart cart) {
            return getSaleTaxesInternal(new AllTaxRelatedInformation() {
                @Override
                public String getZipCode() { return cart.getZipCode(); }

                @Override
                public Optional<Integer> getShippingCosts() { return cart.getShippingCosts(); }
            });
        }

        public SalesTaxRates getSaleTaxes(Order order) {
            return getSaleTaxesInternal(order);
        }

        private Map<CacheKey, SalesTaxRates> cache = new HashMap<>();
        private <T extends WithZipCode & WithShipping & WithDiscount> SalesTaxRates getSaleTaxesInternal(T input) {
            SalesTaxRates cachedValue = cache.get(new CacheKey(input, input, input));
            if(cachedValue != null) {
                return cachedValue;
            }

            //TODO: calculation here
            return null;
        }

        private interface AllTaxRelatedInformation extends WithZipCode, WithShipping, WithDiscount {
            @Override
            default Optional<Integer> getShippingCosts() { return Optional.empty(); }
            @Override
            default Optional<Integer> getDiscount() { return Optional.empty(); }
        }

        private class CacheKey {
            private final WithZipCode zipCode;
            private final WithShipping shipping;
            private final WithDiscount discount;

            private CacheKey(WithZipCode zipCode, WithShipping shipping, WithDiscount discount) {
                this.zipCode = zipCode;
                this.shipping = shipping;
                this.discount = discount;
            }

            @Override
            public boolean equals(Object obj) {
                //TODO: implement equals by shipping cost, delivery and zipCode
                return false;
            }

            @Override
            public int hashCode() {
                //TODO: implement hashCode by shipping cost, delivery and zipCode
                return -1;
            }
        }
    }
}

(Приведенный выше код компилируется автономно, удаляя все префиксы static для классов, когда вы реорганизуете его в свою кодовую базу).

Это был довольно большой вопрос, но, как я понял (после обсуждения в комментариях), вы хотели и расширяемый способ обработки, включающий несколько иногда необязательных значений в ваши вычисления. Приведенный выше код объединяет все различные варианты в одну функцию вычисления, которая должна обрабатывать наличие или отсутствие некоторых значений (я пометил эти Optional). Вам нужно только один раз реализовать логику вычислений и получить лучший ключ кеша. Я упустил реализацию для ZipCode, Order и ShoppingCart для сокращения кода (должно быть легко с каким-либо полем).

Надеюсь, я вас правильно понял, не стесняйтесь комментировать любые недоразумения или, если что-то неясно.

...