我有一些BaseClass
方法void doSomething()
。
foSomething
有不同的方式,SubClass1
,SubClass2
和SubClass3
实施了这些方式。
现在我想向Boolean active
添加BaseClass
属性,以便在实例上调用doSomething
时,它将返回而不执行任何操作。
我知道我可以将BaseClass
编码为doSomething()
,其内容如下:
Void doSomething(){
if (this.getActive()) actuallyDoSomething();
}
然后在子类中@Override actuallyDoSomething()
而不是@Override doSomething()
。
但感觉不对......从某种意义上说,已经同意子类应该为doSomething()
提供一个实现,并且他们不知道actuallyDoSomething()
。
我也可以让每个子类在if (!this.getActive()) return;
的实现开始时添加doSomething()
,但这似乎也是错误的,因为它的常用功能我更喜欢保持常见。
这样做的常见/最佳做法是什么? 可以在不改变子类的情况下完成吗?
更新
Q的重点不在于设计此类功能的正确方法(这非常简单), 但是如何在不破坏任何内容的情况下添加这样的功能。
默认情况下, active
是真的,但是如果有人打电话给setActive(false)
,那么在任何所述子类的任何实例上都希望它变为非活动且连续调用{{1}不会做任何事......
答案 0 :(得分:3)
您想使用AspectJ的@Around
建议,并执行以下操作:
// 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([]) !!
在下面的几行中,我将描述如何使用注释实现这一点 我也会在这里使用Spring。它有助于使配置更快捷,更轻松。
工具和相关性
Java 7,AspectJ 1.7.4,Spring 4.0.2
项目结构
<强>的pom.xml 强>
<project ...>
<properties>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
<spring.version>4.0.2.RELEASE</spring.version>
<aspectj.version>1.7.4</aspectj.version>
</properties>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- AspectJ -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
</project>
<强> BaseClass.java 强>
public class BaseClass {
public void doSomething() {
}
public void say(String msg) {
System.out.println(msg);
}
}
<强> SubClassN.java 强>
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;
}
}
AspectJ及其@Around建议。当调用任何doSomething
方法时,我们首先要求AsjectJ调用特定方法。 doSomething
可以位于BaseClass
或其任何子类中的任何位置。
此特定方法称为changeExistingScenario
。它可以有任何名称。这里重要的是注释。
关于@Around值的一句话:
执行(* my.first.spring.aop.aspectj.BaseClass.doSomething(..))
此表达式仅表示我们要拦截的方法签名模式 无论如何,它都会拦截BaseClass或子类中的任何doSomething方法 有多少参数,返回类型和访问修饰符。
有关详细信息,请参阅:http://guptavikas.wordpress.com/2010/04/15/aspectj-pointcut-expressions/
<强> ChangeExistingCode.java 强>
@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()) //
);
}
}
}
<强> Main.java 强>
@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 :(得分:1)
我可能错了,但也许你正在寻找模板方法设计模式。 请看: http://www.oodesign.com/template-method-pattern.html
或者只是用谷歌搜索模式,有非常好的网站。 希望我能提供帮助。
答案 2 :(得分:1)
所以我要做的就是以下内容:
向基类添加doSomething
public final void doSomthing(boolean testBeforeDoing, ..../*original params*/) {
if (!testBeforeDoing || this.getIsActive()) {
doSomething(..../*original params*/)
}
}
和
public void doSomething(..../*original params*/) {}
当然在基类中添加对isActive
的支持。
所以现在任何曾经在基类或具有旧签名的子类上调用doSomething
的旧代码仍然会运行相同的(即忽略实例的活动/非活动状态。
任何想要使用新功能停用某个实例的代码都可以调用.setIsActive(bool)
并添加true
作为他对doSomething
进行的任何调用的第一个参数。
这样我获得了以下优势: 1)向后兼容性 2)根本不需要对子类进行任何更改 3)想要使用新功能时的最小变化
但这种方式有一些局限性,
例如,如果doSomething
已经有两个签名在开始时相差一个布尔值,那么这不起作用。
即。如果我有doSomething(boolean b,int i)
和doSomething(boolean a,boolean b,int i)
而不是在第一个签名中添加bool testBeforeDoing
会导致doSomething(boolean testBeforeDoing,boolean b,int i)
与doSomething(boolean a,boolean b,int i)
无法区分,因此无法完成。
所以我最终做的是以下内容:
我不会添加boolean testBeforeDoing
,而只会向基础类添加doSomthingIfItCanBeDone
..如果它知道doSomething()
以及所有上述内容,请让它someThingCanBeDone()
优势仍然没有缺点。
所以
public final void doSomthingIfItCanBeDone(..../*original params*/) {
if (this.someThingCanBeDone()) {
doSomething(..../*original params*/)
}
}
public boolean someThingCanBeDone() {
return this.isActive();
}
当然在基类中添加对isActive
的支持。
请注意,这种方式someThingCanBeDone()
也可以被子类覆盖,以便在何时可以调用doSomething以及何时不能调用doSomething时提供额外的限制。
@Override
public boolean someThingCanBeDone() {
return base.someThingCanBeDone() && someConditionLocalToTheSubclass;
}