Java 如何在不修改子类的情况下添加对现有子类中方法调用的控制?
我有一些Java 如何在不修改子类的情况下添加对现有子类中方法调用的控制?,java,class,methods,Java,Class,Methods,我有一些基类和一些方法void doSomething() 有不同的方法来foSomething,它们由子类1、子类2和子类3实现 现在,我想在基类中添加一个Boolean active属性,以便在实例上调用doSomething时,它只返回而不做任何操作 我知道我可以将基类编码为doSomething(),它看起来像: Void doSomething(){ if (this.getActive()) actuallyDoSomething(); } 然后在子类中重写实际的doSome
基类
和一些方法void doSomething()
有不同的方法来foSomething
,它们由子类1
、子类2
和子类3
实现
现在,我想在基类
中添加一个Boolean active
属性,以便在实例上调用doSomething
时,它只返回而不做任何操作
我知道我可以将基类
编码为doSomething()
,它看起来像:
Void doSomething(){
if (this.getActive()) actuallyDoSomething();
}
然后在子类中重写实际的doSomething(),而不是重写doSomething()。
但是感觉不对。。。在某种意义上,已经同意子类应该为doSomething()
提供一个实现,并且它们不知道实际的doSomething()
如果(!this.getActive())返回,我还可以让每个子类添加一个
在它的doSomething()
实现之初,但这似乎也是错误的,因为它的公共功能我更愿意保持通用
实现这一点的常见/最佳做法是什么?
可以在不更改子类的情况下完成吗
更新
Q的重点不是设计这种功能的正确方法(非常简单),
但是关于如何在不破坏任何东西的情况下将此类功能添加到现有场景中
active
在默认情况下为true,但如果有人调用setActive(false)
,则在任何所述子类的任何实例上,它将变为不活动,并连续调用.doSomething()
不会做任何事情…我可能是错的,但可能您正在搜索模板方法设计模式。
请参阅:
或者只是用谷歌搜索模式,有很多很好的网站。
希望我能提供帮助。您想使用AspectJ提供的
@建议并执行以下操作:
// Let subClass instances run normally...
cec.setActive(true);
letThemDoSomething("BEFORE", sbc1, sbc2, sbc3);
// Now change existing scenario...
cec.setActive(false);
letThemDoSomething("AFTER", sbc1, sbc2, sbc3);
这将输出:
BEFORE ======
SubClass1: doSomething() called.
SubClass2: doSomething() called.
SubClass3: doSomething() called.
AFTER ======
Blocking instance<1> method: my.first.spring.aop.aspectj.SubClassN#doSomething([]) !!
Blocking instance<2> method: my.first.spring.aop.aspectj.SubClassN#doSomething([]) !!
Blocking instance<3> method: my.first.spring.aop.aspectj.SubClassN#doSomething([]) !!
子类java
public class BaseClass {
public void doSomething() {
}
public void say(String msg) {
System.out.println(msg);
}
}
public class SubClassN extends BaseClass {
private Integer index;
public SubClassN(Integer index) {
this.index = index;
}
@Override
public void doSomething() {
say("SubClass" + index + ": doSomething() called.");
}
public Integer getIndex() {
return index;
}
}
@Aspect // Mark ChangeExistingCode as the class for modifying the code
@Component
public class ChangeExistingCode {
private boolean active;
public void setActive(boolean active) {
this.active = active;
}
/**
*
* This method will be called by AspectJ anytime a `doSomething` method is called.
*
* This will give us a chance to decide whether the `doSomething` method should
* be called or not.
*
*/
@Around("execution(* my.first.spring.aop.aspectj.BaseClass.doSomething(..))")
public void changeExistingScenario(ProceedingJoinPoint joinPoint) throws Throwable {
// Is active ?
if (active) { // Yes, let doSomething() run as usual
joinPoint.proceed();
} else {// No, block doSomething() invokation
Signature s = joinPoint.getSignature();
System.out.format( //
"Blocking instance<%d> method: %s#%s(%s) !!\n", //
((SubClassN)joinPoint.getTarget()).getIndex(), //
s.getDeclaringTypeName(), //
s.getName(), //
Arrays.toString(joinPoint.getArgs()) //
);
}
}
}
@Configuration // Mark the Main class as the class where Spring will find its configuration
@ComponentScan // Ask Spring to look for other components within the Main class package
@EnableAspectJAutoProxy // Let Spring auto configure AspectJ aspects for us...
public class Main {
private static int subClassCounter;
public static void main(String[] args) {
subClassCounter=0;
GenericApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
SubClassN sbc1 = context.getBean(SubClassN.class);
SubClassN sbc2 = context.getBean(SubClassN.class);
SubClassN sbc3 = context.getBean(SubClassN.class);
ChangeExistingCode cec = context.getBean(ChangeExistingCode.class);
// Let subClass instances run normally...
cec.setActive(true);
letThemDoSomething("BEFORE", sbc1, sbc2, sbc3);
// Now change existing scenario...
cec.setActive(false);
letThemDoSomething("AFTER", sbc1, sbc2, sbc3);
context.close();
}
private static void letThemDoSomething(String prefix, SubClassN... existingClasses) {
System.out.format("%s ======\n", prefix);
for (SubClassN subClassInstance : existingClasses) {
subClassInstance.doSomething();
}
System.out.println();
}
@Bean // Tell Spring to use this method for creating SubClassN instances
@Scope(BeanDefinition.SCOPE_PROTOTYPE) // Scope prototype force creation of multiple instances
private static SubClassN buildSubClassN() {
subClassCounter++;
return new SubClassN(subClassCounter);
}
}
3-更改现有代码(不破坏任何内容…)
下面是AspectJ及其@Around建议。当调用任何doSomething
方法时,我们将首先要求AsjectJ调用特定的方法doSomething
可以位于基类
或其任何子类中的任何位置
此特定方法称为changeExistingScenario
。它可以有任何名称。这里最重要的是放在上面的注释
关于@Around值的一句话:
执行(*my.first.spring.aop.aspectj.BaseClass.doSomething(..)
此表达式仅指示我们要拦截的方法签名模式。
它将拦截基类或子类中的任何doSomething方法
有多少个参数,返回类型和访问修饰符
有关更多详细信息,请参阅:
ChangeExistingCode.java
public class BaseClass {
public void doSomething() {
}
public void say(String msg) {
System.out.println(msg);
}
}
public class SubClassN extends BaseClass {
private Integer index;
public SubClassN(Integer index) {
this.index = index;
}
@Override
public void doSomething() {
say("SubClass" + index + ": doSomething() called.");
}
public Integer getIndex() {
return index;
}
}
@Aspect // Mark ChangeExistingCode as the class for modifying the code
@Component
public class ChangeExistingCode {
private boolean active;
public void setActive(boolean active) {
this.active = active;
}
/**
*
* This method will be called by AspectJ anytime a `doSomething` method is called.
*
* This will give us a chance to decide whether the `doSomething` method should
* be called or not.
*
*/
@Around("execution(* my.first.spring.aop.aspectj.BaseClass.doSomething(..))")
public void changeExistingScenario(ProceedingJoinPoint joinPoint) throws Throwable {
// Is active ?
if (active) { // Yes, let doSomething() run as usual
joinPoint.proceed();
} else {// No, block doSomething() invokation
Signature s = joinPoint.getSignature();
System.out.format( //
"Blocking instance<%d> method: %s#%s(%s) !!\n", //
((SubClassN)joinPoint.getTarget()).getIndex(), //
s.getDeclaringTypeName(), //
s.getName(), //
Arrays.toString(joinPoint.getArgs()) //
);
}
}
}
@Configuration // Mark the Main class as the class where Spring will find its configuration
@ComponentScan // Ask Spring to look for other components within the Main class package
@EnableAspectJAutoProxy // Let Spring auto configure AspectJ aspects for us...
public class Main {
private static int subClassCounter;
public static void main(String[] args) {
subClassCounter=0;
GenericApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
SubClassN sbc1 = context.getBean(SubClassN.class);
SubClassN sbc2 = context.getBean(SubClassN.class);
SubClassN sbc3 = context.getBean(SubClassN.class);
ChangeExistingCode cec = context.getBean(ChangeExistingCode.class);
// Let subClass instances run normally...
cec.setActive(true);
letThemDoSomething("BEFORE", sbc1, sbc2, sbc3);
// Now change existing scenario...
cec.setActive(false);
letThemDoSomething("AFTER", sbc1, sbc2, sbc3);
context.close();
}
private static void letThemDoSomething(String prefix, SubClassN... existingClasses) {
System.out.format("%s ======\n", prefix);
for (SubClassN subClassInstance : existingClasses) {
subClassInstance.doSomething();
}
System.out.println();
}
@Bean // Tell Spring to use this method for creating SubClassN instances
@Scope(BeanDefinition.SCOPE_PROTOTYPE) // Scope prototype force creation of multiple instances
private static SubClassN buildSubClassN() {
subClassCounter++;
return new SubClassN(subClassCounter);
}
}
输出
BEFORE ======
SubClass1: doSomething() called.
SubClass2: doSomething() called.
SubClass3: doSomething() called.
AFTER ======
Blocking instance<1> method: my.first.spring.aop.aspectj.SubClassN#doSomething([]) !!
Blocking instance<2> method: my.first.spring.aop.aspectj.SubClassN#doSomething([]) !!
Blocking instance<3> method: my.first.spring.aop.aspectj.SubClassN#doSomething([]) !!
之前======
子类1:doSomething()被调用。
子类2:doSomething()被调用。
子类3:doSomething()被调用。
之后======
阻塞实例方法:my.first.spring.aop.aspectj.subclass#doSomething([])!!
阻塞实例方法:my.first.spring.aop.aspectj.subclass#doSomething([])!!
阻塞实例方法:my.first.spring.aop.aspectj.subclass#doSomething([])!!
5-参考文献
-
下载完整代码:
-
-
-
有助于撰写此答案的其他有用资源
因此,我想我应该做以下几点:
为
doSomething
public final void doSomthing(boolean testBeforeDoing, ..../*original params*/) {
if (!testBeforeDoing || this.getIsActive()) {
doSomething(..../*original params*/)
}
}
及
当然,还要在基类中添加对isActive
的支持
因此,现在任何用于调用基类或任何具有旧签名的子类上的doSomething
的旧代码仍将运行相同的(即忽略实例的活动/非活动状态)
任何想要使用新功能停用实例的代码都可以调用.setIsActive(bool)
,并将true
作为他对doSomething
所做任何调用的第一个参数
通过这种方式,我可以获得以下优势:
1) 向后兼容性
2) 子类完全不需要更改
3) 在希望使用新功能时进行最小更改
但这种方式有一些局限性,
例如,如果doSomething
已经有两个签名在开始时相差一个布尔值,则这不起作用
i、 e.如果我有doSomething(布尔b,int i)
和doSomething(布尔a,布尔b,int i)
在第一个签名中添加bool testbeforedo
将导致doSomething(boolean testbeforedo,boolean b,int i)
,这与doSomething(boolean a,boolean b,int i)
无法区分,因此无法完成
因此,我最终做了以下几点:
我将只向基类添加一个dosomthingingifitcanbedone
,而不是在执行之前添加boolean测试。。并让它调用doSomething()
所以
当然,还要在基类中添加对isActive
的支持
注意,这种方式someThingCanBeDone()
也可以被子类覆盖,以提供关于何时可以调用doSomething和何时不能调用doSomething的额外限制
@Override
public boolean someThingCanBeDone() {
return base.someThingCanBeDone() && someConditionLocalToTheSubclass;
}
使实际成为方法()
摘要和pro