Pergunta

Eu não quero discutir os méritos desta abordagem, apenas se for possível. Creio que a resposta seja "não". Mas talvez alguém vai me surpreender!

Imagine que você tem uma classe Widget núcleo. Tem um calculateHeight() método, que retorna uma altura. A altura é muito grande - este resultado em botões (por exemplo) que são grandes demais. Você pode estender DefaultWidget para criar seu próprio NiceWidget e implementar seu próprio calculateHeight() para retornar um tamanho agradável.

Agora, um WindowDisplayFactory classe biblioteca, instancia DefaultWidget em um método bastante complexo. Você gostaria de usar o seu NiceWidget. Da classe de fábrica método é algo como isto:

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;
}

É o negócio. O resultado tem a profunda DefaultWidget dentro de uma hierarquia de outros objetos. A pergunta - como obter este método de fábrica para usar o meu próprio NiceWidget? Ou pelo menos ter meu próprio calculateHeight() lá. Idealmente, eu gostaria de ser capaz de monkey patch do DefaultWidget de modo que sua calculateHeight fez a coisa certa ...

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

O que é o que eu poderia fazer em Python, Ruby, etc. Eu inventei o nome setMethod() embora. As outras opções abertas para mim são:

  • Copiar e colar o código do método createView() em minha própria classe que herda da classe de fábrica
  • Viver com widgets que são demasiado grandes

A classe de fábrica não pode ser mudado - é parte de uma API plataforma central. Tentei reflexão sobre o resultado retornado para chegar ao widget que (eventualmente) adicionado, mas é várias widget-camadas para baixo e em algum lugar ele é usado para inicializar outras coisas, causando efeitos colaterais estranhos.

Todas as idéias? Minha solução até agora é o trabalho de cópia e colar, mas isso é um policial que requer rastreamento das mudanças na classe de fábrica pai quando a atualização para versões mais recentes da plataforma, e eu estaria interessado em ouvir outras opções.

Foi útil?

Solução

Talvez você poderia usar Aspect Oriented Programming às chamadas armadilhas para essa função e retornar a sua própria versão em vez disso?

Spring oferece algumas funcionalidades AOP, mas há outras bibliotecas que fazem isso bem.

Outras dicas

Uma solução feia seria colocar sua própria implementação de DefaultWidget (com o mesmo FQCN) mais cedo no classpath do que a implementação normal. É um terrível corte, mas cada outra abordagem que eu posso pensar é ainda pior.

Apenas meu conceito idéia,

É possível que o uso AOP, com forma de engenharia bytecode, para injetar um aspecto ao método calculateHeight.

Em seguida, você pode permitir que você corrigir por ThreadLocal ou variável mais.

cglib é uma biblioteca Java que pode fazer algumas coisas semelhantes a patching macaco - ele pode manipular o bytecode no tempo de execução para alterar determinados comportamentos. Eu não tenho certeza se ele pode fazer exatamente o que você precisa, mas vale a pena um olhar ...

É totalmente possível monkeypatch em Java, utilizando Unsafe.putObject e um localizador de classe. Escreveu um post aqui:

https://tersesystems.com/blog/2014/ 02/03 / monkeypatching-java-aulas /

A maneira orientada a objeto de fazer isso seria a criação de um invólucro implementação iwidget, delegando todas as chamadas para o widget real, exceto calculateHeight, algo como:

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);
    }
}

Para que isso funcione, você precisa interceptar todas as criações do widget que pretende substituir, o que provavelmente significa a criação de um invólucro semelhante para o WidgetFactory. E você deve ser capaz de configurar que WidgetFactory de usar.

Também depende de nenhum cliente tentando lançar a volta iwidget para DefaultWidget ...

Apenas sugestões que posso pensar:

  1. Dig através da API biblioteca para ver se há alguma maneira de substituir os padrões e dimensionamento. Dimensionamento pode ser confuso no balanço (pelo menos para mim), setMinimum, setMaximum, setdefault, setDefaultOnThursday, .... É possível que há uma maneira. Se você pode contactar o desenhista de biblioteca (s) que você pode encontrar uma resposta que vai aliviar a necessidade de pirataria desagradável.

  2. Talvez estender fábrica somente substituindo algum parâmetro dimensionamento padrão? depende da fábrica, mas pode ser possível.

Criar uma classe com o mesmo nome pode ser a única outra opção, como outros apontaram que é feio e você está sujeito a esquecê-lo e coisas pausa quando você atualizar a biblioteca API ou implantar em um ambiente diferente e esquecer por você tinha o classpath configurado dessa forma.

Você pode tentar usar ferramentas como PowerMock / Mockito. Se você pode zombar em testes, você pode zombar da produção também.

No entanto, estas ferramentas não são realmente concebido para ser usado dessa maneira, então você vai ter que preparar o ambiente de si mesmo e não será capaz de usar os corredores JUnit como você faz em testes ...

Bem, eu continuo tentando postar sugestões e então eu vejo que não vai funcionar ou que você já mencionou que você tentou-los.

A melhor solução que eu posso pensar é a subclasse WindowDisplayFactory, em seguida, no método (), primeiro super.createView call () CreateView da subclasse, em seguida, modificar o objeto retornado completamente jogar fora o widget e substituí-la por uma instância a subclasse que faz o que quiser. Mas o widget é usado para inicializar coisas, então você teria que ir para mudar tudo isso.

Então eu pensar em usar a reflexão sobre o objeto retornado de CreateView () e tentar consertar as coisas dessa forma, mas novamente, dessa cabeludo porque tanta coisa foi inicializado com o widget. Eu acho que vou tentar usar essa abordagem, porém, se foi o suficiente simples para justificá-la ao longo de copiar e colar.

Eu estarei assistindo isso, além de pensar para ver se eu posso vir acima com outras ideias. Java Reflection certeza é bom, mas não pode bater a dinâmica introspecção eu vi disponível em linguagens como Perl e Python.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top