简体   繁体   English

设计模式,无需重新编译/重新链接即可覆盖方法

[英]Design Patterns, override a method without need to re compile / relink

We are building a product that needs to run on production environments. 我们正在构建一种需要在生产环境上运行的产品。 We need to modify some of the functionality of a existing library. 我们需要修改现有库的某些功能。 The existing library has class's and methods, we need to override 1 or more methods so that the caller uses our overriden methods instead of the original library. 现有库具有类和方法,我们需要重写1个或多个方法,以便调用者使用我们的重写方法而不是原始库。

OriginalLibrary 原始图书馆

package com.original.library ;
public class OriginalLibrary {
    public int getValue() {
        return 1 ;
    }
    public int getAnotherValue() {
        return 1 ;
    }
}

Original Client 原始客户

public class MyClient {
    private OriginalLibraryClass originalLibraryObject ;
    public MyClient () {
        originalLibraryObject = new OriginalLibraryClass() ;
        System.out.println(originalLibraryObject.getValue()) ;
        System.out.println(originalLibraryObject.getAnotherValue()) ;
    }
}

Output 输出量

1 1个

2 2

Now, I need to change getValue() to return 3 , instead of 1 现在,我需要将getValue()更改为返回3 ,而不是1

Needed Output 所需输出

3 3

2 2

package com.original.library.improved ;
    public class OriginalLibrary extends com.original.library.OriginalLibrary {
        public int getValue() {
            return 3 ;
        }
        public int getAnotherValue() {
            return super.getAnotherValue() ;
        }
}

If I do the above, I need to tell my Original Client to reorder and use my new com.original.library.improved jar file before com.original.library . 如果执行上述操作,则需要告诉我的Original Client重新排序,并 com.original.library 之前使用新的com.original.library.improved jar文件。

I am almost convinced that this is the most non intrusive way to launch my improved services over and above the OriginalLibrary . 我几乎确信,这是在OriginalLibrary之上启动我的改进服务的最non intrusive way I would have preferred a solution where I need to tell the customer to just add my jar file, no need to recompile, relink your client code. 在需要告诉客户只需添加我的jar文件,无需重新编译,重新链接您的客户端代码的情况下,我会首选一个解决方案。

Similar (not same) questions on a google search here here 关于google搜索的类似(不同)问题,请点击 此处

java assist is excellent library for bytecode manipulation. Java助手是用于字节码操作的优秀库。 I have modified code below as per your sample code given, You have to explore javaassist more for your actual requirenment 我已根据您提供的示例代码在下面修改了代码,您必须根据实际需要更多地探索javaassist

CtClass etype = ClassPool.getDefault().get("com.original.library.OriginalLibrary");
// get method from class
CtMethod cm = etype.getDeclaredMethod("getValue");
// change the method bosy
cm.setBody("return 3;");
etype.rebuildClassFile();
// give the path where classes is placed, In my eclipse it is bin
etype.writeFile("bin");


OriginalLibrary originalLibraryObject;
originalLibraryObject = new OriginalLibrary();
System.out.println(originalLibraryObject.getValue());
System.out.println(originalLibraryObject.getAnotherValue());

Now output of getValue is 3 because I changed body of that method. 现在,由于我更改了该方法的主体,因此getValue的输出为3。

A couple of questions - 几个问题-

  • How is the client getting an instance of your library's class? 客户端如何获取您的类的实例?
    If they are using new OriginalLibrary() , then you're pretty much stuck with creating a new subclass of OriginalLibrary and then asking your client to use your new OriginalLibraryImproved class. 如果他们使用的是new OriginalLibrary() ,那么您就很难创建一个新的OriginalLibrary子类,然后要求您的客户端使用新的OriginalLibraryImproved类。 This is a common problem encountered in projects and is one reason why a library should not allow its clients to instantiate its classes directly using the new operator. 这是项目中经常遇到的问题,也是库不允许其客户使用new运算符直接实例化其类的原因之一。
    If instead, your client is instantiating OriginalLibrary using a factory method provided by the library (say, OriginalLibrary.getInstance() ), you may want to check if there are any hooks into the factory that allow you to change the object being returned. 相反,如果客户端使用库提供的工厂方法(例如OriginalLibrary.getInstance() )实例化OriginalLibrary ,则可能要检查工厂中是否有任何挂钩可以允许您更改要返回的对象。
  • Do you have full control of the source code of the original library? 您完全控制原始库的源代码吗?
    If yes, then you definitely should (and I cannot emphasize this strongly enough) provide factory methods for any class in the library that is instantiable. 如果是的话,那么您绝对应该(并且我不能足够强调这一点)为可实例化的库中的任何类提供工厂方法。 Doing this allows you to change the actual object being returned without modifying the client (as long as the returned object's class is a subclass of the return value from the factory method). 这样做允许您更改实际返回的对象而无需修改客户端(只要返回的对象的类是factory方法的返回值的子类)。
    If not, then I suggest you do the following. 如果没有,那么建议您执行以下操作。

    • Create a subclass of OriginalLibrary (say, OriginalLibraryImproved ). 创建OriginalLibrary的子类(例如, OriginalLibraryImproved )。
    • Create a Factory class named OriginalLibraryFactory that has a static method named getInstance() . 创建一个名为OriginalLibraryFactory的Factory类,该类具有一个名为getInstance()的静态方法。 Write code to return an instance of OriginalLibraryImproved from this method. 编写代码以返回通过此方法改进的OriginalLibraryImproved的实例。
    • Ask your client to replace all occurrences of new OriginalLibrary() with OriginalLibraryFactory.getInstance() . 要求您的客户将所有出现的new OriginalLibrary()替换为OriginalLibraryFactory.getInstance() Note that this approach will only involve adding an extra import for the factory class. 请注意,此方法仅涉及为工厂类添加额外的导入。 The client will still refer to the returned instance using the same OriginalLibrary reference as before. 客户端仍将使用与以前相同的OriginalLibrary引用来引用返回的实例。

The advantage of this approach is that it gives you complete flexibility to change the implementation details of OriginalLibraryImproved without affecting the client in anyway. 这种方法的优势在于,它为您提供了完全的灵活性,可以在不影响客户端的情况下更改OriginalLibraryImproved的实现细节。 You could also swap OriginalLibararyImproved with a newer version like OriginalLibraryImprovedVer2 and the client will be oblivious to the fact that it is using a new class. 您还可以将OriginalLibararyImproved与较新的版本(例如OriginalLibraryImprovedVer2交换,客户端将不会注意到它正在使用新类。 You'll just have to make sure that OriginalLibraryImprovedVer2 subclasses OriginalLibrary . 您只需要确保OriginalLibraryImprovedVer2子类OriginalLibrary

An even more flexible approach is to use the Wrapper or Decorator pattern to avoid the pitfalls of inheritance. 更加灵活的方法是使用包装器装饰器模式来避免继承的陷阱。 You can understand more about the Decorator pattern here . 您可以在此处了解更多有关Decorator模式的信息

In a nutshell, try to avoid forcing your clients to use new and try to avoid inheritance unless you have very compelling reasons. 简而言之,除非有非常令人信服的理由,否则请尽量避免强迫客户使用new并避免继承。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 结合工厂方法和单例设计模式 - Combining the Factory Method and Singleton Design Patterns 设计模式-不带DI框架的Webapp - Design patterns - webapp without DI framework 方法不会从父类型@override覆盖或实现方法编译错误 - method does not override or implement a method from a supertype @override compile error 需要重新设计用于动态输入的现有逻辑 - Need to Re Design existing logic for dynamic input 用抽象编译错误,并且不覆盖抽象方法 - Compile error with abstract and does not override abstract method 模板方法和策略设计模式之间有什么相似之处 - What are the similarities between the Template Method and Strategy design patterns 设计模式:工厂 vs 工厂方法 vs 抽象工厂 - Design Patterns: Factory vs Factory method vs Abstract Factory 如果我覆盖Java中的'equals'方法,为什么需要重写hashcode? - Why is there a need to override hashcode if I override the 'equals' method in Java? 我什么时候需要重新编译Jasper报告 - When do I need to re-compile Jasper reports Java Iterator实现编译错误:不覆盖抽象方法remove() - Java Iterator implementation compile error: does not override abstract method remove()
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM