Java 设计模式,重写方法而无需重新编译/重新链接

Java 设计模式,重写方法而无需重新编译/重新链接,java,design-patterns,Java,Design Patterns,我们正在构建一种需要在生产环境中运行的产品。我们需要修改现有库的一些功能。现有库具有类和方法,我们需要重写1个或多个方法,以便调用方使用我们重写的方法而不是原始库 原始图书馆 package com.original.library ; public class OriginalLibrary { public int getValue() { return 1 ; } public int getAnotherValue() { retu

我们正在构建一种需要在生产环境中运行的产品。我们需要修改现有库的一些功能。现有库具有类和方法,我们需要重写1个或多个方法,以便调用方使用我们重写的方法而不是原始库

原始图书馆

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

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

一,

二,

现在,我需要更改
getValue()
以返回
3
,而不是
1

所需输出

三,

二,

如果我做了上述操作,我需要告诉我的
原始客户端
com.Original.library
之前重新排序并使用我新的
com.Original.library.改良的
jar文件

我几乎确信,这是在
原始库
之上推出改进服务的最
非侵入式方式。我更喜欢一个解决方案,我需要告诉客户只添加我的jar文件,不需要重新编译,重新链接您的客户机代码

谷歌搜索中的类似(不相同)问题

java assist是字节码操作的优秀库。我已经根据您给出的示例代码修改了下面的代码,您必须根据您的实际需求进一步探索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());

现在getValue的输出是3,因为我更改了该方法的主体。

java assist是字节码操作的优秀库。我已经根据您给出的示例代码修改了下面的代码,您必须根据您的实际需求进一步探索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());

现在getValue的输出是3,因为我更改了该方法的主体。

有几个问题-

  • 客户端如何获取库类的实例?
    如果他们使用的是
    new OriginalLibrary()
    ,那么您就必须创建
    OriginalLibrary
    的新子类,然后要求您的客户使用新的
    OriginalLibrary改进的
    类。这是项目中遇到的常见问题,也是库不允许其客户端直接使用
    new
    操作符实例化其类的原因之一。
    相反,如果您的客户端正在使用库提供的工厂方法(例如,
    OriginalLibrary.getInstance()
    )实例化
    OriginalLibrary
    ),则可能需要检查工厂中是否有允许您更改返回对象的挂钩
  • 您是否完全控制原始库的源代码?
    如果是的话,那么您肯定应该为库中任何可实例化的类提供工厂方法(我再怎么强调也不为过)。这样做可以在不修改客户端的情况下更改返回的实际对象(只要返回对象的类是factory方法返回值的子类)。
    如果没有,那么我建议你做以下事情

    • 创建
      OriginalLibrary
      的子类(例如,
      OriginalLibraryImproved
    • 创建一个名为
      OriginalLibraryFactory
      的工厂类,它有一个名为
      getInstance()
      的静态方法。编写代码以返回此方法改进的
      originallibrarymproved
      的实例
    • 请您的客户端将所有出现的
      new OriginalLibrary()
      替换为
      OriginalLibraryFactory.getInstance()
      。请注意,这种方法只涉及为factory类添加额外的导入。客户端仍将使用与以前相同的
      原始库
      引用来引用返回的实例
这种方法的优点是,它使您能够完全灵活地更改
originallibrarymproved
的实现细节,而不会影响客户端。您还可以将
originallibrarymproved
与更新的版本(如
originallibrarymprovedver2
)进行交换,这样客户机就不会注意到它正在使用一个新类。您只需确保
originallibrarymprovedver2
子类
OriginalLibrary

更灵活的方法是使用包装器或装饰器模式来避免继承的陷阱。您可以进一步了解装饰图案


简而言之,除非你有非常令人信服的理由,否则尽量避免强迫你的客户使用
new
,并尽量避免继承

有几个问题-

  • 客户端如何获取库类的实例?
    如果他们使用的是
    new OriginalLibrary()
    ,那么您就必须创建
    OriginalLibrary
    的新子类,然后要求您的客户使用新的
    OriginalLibrary改进的
    类。这是项目中遇到的常见问题,也是库不允许其客户端直接使用
    new
    操作符实例化其类的原因之一。
    相反,如果您的客户端正在使用库提供的工厂方法(例如,
    OriginalLibrary.getInstance()
    )实例化
    OriginalLibrary
    ),则可能需要检查工厂中是否有允许您更改返回对象的挂钩
  • 您是否完全控制原始库的源代码?
    如果是的话,那么您肯定应该为库中任何可实例化的类提供工厂方法(我再怎么强调也不为过)。这样做可以在不修改客户端的情况下更改返回的实际对象(只要返回对象的类是factory方法返回值的子类)。
    如果没有,那么我建议你做以下事情

    • 创建
      OriginalLibrary的子类