Кодирование себя в угол

У нас есть набор классов, которые вытекают из общего набора интерфейсов, так что

IFoo-> BasicFoo, ReverseFoo, ForwardFoo
IBar -> UpBar, DownBar, SidewaysBar
IYelp -> Yip, Yap, Yup

где конструктор для Foo выглядит как Foo(IBar, IYelp) Эти элементы используются во всем проекте.

Существует еще один класс, у которого есть метод, подпись которого public double CalcSomething(IFoo, IAnotherClass), который применяется в некоторой точке к каждому Foo. У нас был запрос сверху вниз, что одна конкретная композиция объекта, скажем, BasicFoo(UpBar,Yip), использует другой алгоритм, отличный от найденного в CalcSomething.

Мой первый инстинкт должен был сказать, пусть изменить интерфейс IFoo таким образом мы можем переместить логику вплоть до уровня класса Foo, измените конструктор быть Foo(IBar, IYelp, IStrategy) и затем объекты Foo инкапсулировать эту логику. К сожалению, нам также сказали, что дизайн архитектуры предусматривает отсутствие зависимостей между IFoo, его реализациями и IAnotherClass. Они непреклонны в этом.

Хорошо, конечно, тогда я подумал, что могу использовать шаблон посетителя, но... как? Весь смысл создания композиции состоял в том, чтобы ни один другой класс не смог увидеть детали реализации. Отражение, чтобы заглянуть внутрь объектов, полностью нарушая инкапсуляцию? Ах, черт возьми.

Итак, я пришел сюда, потому что я в недоумении. Есть ли у кого-нибудь какие-либо предложения, как мы могли бы рассматривать частный случай одной из композиций без изменения состава или разрушения инкапсуляции? Должно быть простое решение, которое я пересматриваю.

Edit:

Удалено оскорбительное начало. Изменено "обрабатывается специально" в более описательное значение.

Ответ 1

A CalculationFactory, который выбирает подходящий алгоритм, основанный на типе IFoo, который вы предоставили, разрешит проблему (за счет условного):

interface ICalcSomethingStrategy {
    public double CalcSomething(IFoo, IAnotherClass);
}

CalcSomethingStrategyFactory {
    ICalcSomethingStrategy CreateCalcSomethingStrategy(IFoo foo) {
        // I'm not sure whether this is the idiomatic java way to check types D:
        if (foo.Bar instanceof UpBar && foo instanceof Yip) {
            return new UnusualCalcSomethingStrategy();
        } else {
            return new StandardCalcSomethingStrategy();
        }
    }
}

Ответ 2

В духе KISS я бы добавил метод isSpecial() в IFoo и использовал это, чтобы решить, какой алгоритм использовать в CalcSomething().

Это предполагает, что это единственный частный случай.

Ответ 3

Нет никакого способа для calcSomething избежать знания, необходимого для выполнения "специального" поведения, но помимо этого вы можете поддерживать большую часть своей инкапсуляции таким образом.

Создайте интерфейс маркера IQualifyForSpecialTreatment, который расширяет IFoo. Расширьте BasicFoo до SpecialBasicFoo и попробуйте реализовать IQualifyForSpecialTreatment.


    interface IQualifyForSpecialTreatment extends IFoo {
    }
    class SpecialBasicFoo extends BasicFoo implements IQualifyForSpecialTreatment {
        ...
    }

Затем вы можете добавить еще один аромат calcSomething:


    calcSomething (IQualifyForSpecialTreatment foo, IAnotherClass whatever) {
        ... perform "special" variant of calculation
    }
    calcSomething (IFoo foo, IAnotherClass whatever) {
        ... perform "normal" variant of calculation
    }