Подтвердить что ты не робот

Возможно ли использовать патч для обезьян в Java?

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

Представьте, что у вас есть класс виджетов. Он имеет метод calculateHeight(), который возвращает высоту. Высота слишком велика - этот результат в кнопках (скажем), которые слишком велики. Вы можете расширить DefaultWidget, чтобы создать свой собственный NiceWidget, и реализовать свой собственный calculateHeight(), чтобы вернуть более удобный размер.

Теперь библиотечный класс WindowDisplayFactory создает экземпляр DefaultWidget довольно сложным способом. Вы хотите, чтобы он использовал ваш NiceWidget. Метод класса factory выглядит примерно так:

public IWidget createView(Component parent) {
    DefaultWidget widget = new DefaultWidget(CONSTS.BLUE, CONSTS.SIZE_STUPIDLY);

    // bunch of ifs ...
    SomeOtherWidget bla = new SomeOtherWidget(widget);
    SomeResultWidget result = new SomeResultWidget(parent);
    SomeListener listener = new SomeListener(parent, widget, flags);

    // more widget creation and voodoo here

    return result;
}

Это сделка. Результат имеет DefaultWidget глубоко внутри иерархии других объектов. Вопрос: как получить этот метод factory для использования моего собственного NiceWidget? Или, по крайней мере, получить там свой собственный calculateHeight(). В идеале, я бы хотел, чтобы обезьяна заплатила DefaultWidget так, чтобы его расчетHeight сделал правильную вещь...

public class MyWindowDisplayFactory {
    public IWidget createView(Component parent) {
        DefaultWidget.class.setMethod("calculateHeight", myCalculateHeight);
        return super.createView(parent);
    }
}

Что я могу сделать в Python, Ruby и т.д. Однако я придумал имя setMethod(). Остальные варианты доступны для меня:

  • Копирование и вставка кода метода createView() в мой собственный класс, который наследуется от класса factory
  • Жизнь с слишком большими виджетами

Класс factory не может быть изменен - ​​он является частью API основной платформы. Я попытался отразить полученный результат, чтобы добраться до виджета, который (в конце концов) был добавлен, но это несколько слоев виджета, и где-то он привыкает к инициализации других вещей, вызывая нечетные побочные эффекты.

Любые идеи? Мое решение до сих пор заключается в задании копирования-вставки, но в том, что компьютер, который требует отслеживания изменений родительского класса factory при обновлении до более новых версий платформы, и мне было бы интересно услышать другие варианты.

4b9b3361

Ответ 1

Возможно, вы могли бы использовать Aspect Oriented Programming для захвата вызовов этой функции и вместо этого вернете свою собственную версию?

Spring предлагает некоторые функции AOP, но есть и другие библиотеки, которые также делают это.

Ответ 2

Одним уродливым решением было бы поместить вашу собственную реализацию DefaultWidget (с тем же FQCN) ранее на пути класса, чем обычная реализация. Это ужасный взлом, но любой другой подход, о котором я могу думать, еще хуже.

Ответ 3

Только моя концепция,

Возможно, что AOP с использованием метода байт-кода может ввести аспект метода calculateHeight.

Затем вы можете включить исправление переменной ThreadLocal или else.

Ответ 4

cglib - это библиотека Java, которая может делать некоторые вещи, похожие на переключение на обезьяны - она ​​может манипулировать байт-кодом во время выполнения, чтобы изменить определенное поведение, Я не уверен, что он может сделать именно то, что вам нужно, но это стоит посмотреть...

Ответ 7

Объектно-ориентированный способ сделать это - создать оболочку, реализующую IWidget, делегируя все вызовы фактическому виджету, кроме calculateHeight, что-то вроде:

class MyWidget implements IWidget {
    private IWidget delegate;
    public MyWidget(IWidget d) {
        this.delegate = d;
    }
    public int calculateHeight() {
        // my implementation of calculate height
    }
    // for all other methods: {
    public Object foo(Object bar) {
        return delegate.foo(bar);
    }
}

Чтобы это сработало, вам нужно перехватить все творения виджета, который вы хотите заменить, что, вероятно, означает создание аналогичной обертки для WidgetFactory. И вы должны быть в состоянии настроить, какой WidgetFactory использовать.

Это также зависит от того, что клиент не пытается вернуть IWidget в DefaultWidget...

Ответ 8

Только предложения, о которых я могу думать:

  • Пройдите через API-интерфейс библиотеки, чтобы увидеть, есть ли способ переопределить значения по умолчанию и размер. Калибровка может запутаться в качелях (по крайней мере для меня), setMinimum, setMaximum, setdefault, setDefaultOnThursday,.... Это возможно. Если вы можете связаться с дизайнером библиотеки, вы можете найти ответ, который облегчит необходимость неприятного взлома.

  • Возможно, расширение factory только переопределяет некоторый параметр размера по умолчанию? зависит от factory, но это возможно.

Создание класса с тем же именем может быть единственным другим вариантом, так как другие указали на него уродливо, и вы можете его забыть и сломать материал при обновлении библиотеки api или развертывании в другой среде и забыть, почему вы создали путь к классам.

Ответ 9

Вы можете попробовать использовать такие инструменты, как PowerMock/Mockito. Если вы можете издеваться над тестами, вы можете издеваться над производством.

Однако эти инструменты не предназначены для использования таким образом, поэтому вам придется самостоятельно подготовить среду и не сможете использовать JUnit-бегуны, как в тестах...

Ответ 10

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

Лучшее решение, о котором я могу думать, - это подкласс WindowDisplayFactory, затем в подклассе createView(), сначала вызовите super.createView(), затем измените возвращаемый объект, чтобы полностью выкинуть виджет и заменить его экземпляром подкласс, который делает то, что вы хотите. Но виджет используется для инициализации материала, поэтому вам придется изменить все эти параметры.

Затем я думаю об использовании отражения возвращаемого объекта из createView() и пытается исправить ситуацию таким образом, но опять же, что волосатый, потому что так много вещей было инициализировано виджетами. Я думаю, что я попытаюсь использовать этот подход, хотя это было бы достаточно просто, чтобы оправдать его при копировании и вставке.

Я буду смотреть это, а также думать, могу ли я придумать какие-либо другие идеи. Очевидно, что Java Reflection хорош, но он не может победить динамическую интроспекцию, которую я видел, доступную на таких языках, как Perl и Python.