How to add control over method calls in existing subclasses without changing subclasses?

I have a BaseClass with some void doSomething() method.

There are different foSomething methods, and they are implemented using SubClass1 , SubClass2 and SubClass3 .

Now I want to add the Boolean active property to BaseClass so that when doSomething called on the instance, it just returns without doing anything.

I know that I can encode BaseClass to have doSomething() , which looks something like this:

 Void doSomething(){ if (this.getActive()) actuallyDoSomething(); } 

And then @Override actuallyDoSomething() instead of @Override doSomething() in subclasses. but it feels wrong ... in the sense that it has already been decided that subclasses should provide an implementation for doSomething() , and they are not aware of actuallyDoSomething() .

I can also have each subclass add if (!this.getActive()) return; at the beginning of its implementation of doSomething() , but it also seems to be incorrect as its general functionality, which I would rather keep normal.

What is the general / best way to do this? Can this be done without changing subclasses?

Update

Focus Q is not the right way to develop such functions (which is pretty simple), but how this functionality can be added to an existing scenario without breaking anything.

active will be true by default, but it is desirable that in any instance of any of the specified subclasses, if someone called setActive(false) , it will become inactive, and successive calls to .doSomething() do nothing ...

+6
source share
3 answers

You want to use @Around advice from AspectJ and do something like this:

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

This will output:

 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([]) !! 

In the following lines I will describe how to do this with annotation.
I will also use Spring here. This helps make the configuration faster and easier.


1- Configuration

Tools and Dependencies

Java 7, AspectJ 1.7.4, Spring 4.0.2

Project structure

AspectJ Around advice sample code - project structure

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> 

2 - Existing code

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

3 - Modify existing code (without breaking anything ...)

Here comes AspectJ and his @Around tips. First, we will ask AsjectJ to call a specific method when the doSomething method is doSomething . doSomething can be anywhere in BaseClass or in any of its child classes.

This particular method is called changeExistingScenario . It can have any name. Important here is the annotation posted on it.

A word about the meaning of @Around:

Execution (* my.first.spring.aop.aspectj.BaseClass.doSomething (..))

This expression simply indicates the signature template of the method we want to intercept.
It will intercept any doSomething method in the BaseClass or child class, regardless of how many parameters, return type and access modifier.

For more details see: 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()) // ); } } } 

4- Let all the magic appear ...

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

Output

 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([]) !! 

5 Links

+3
source

Maybe I'm wrong, but maybe you're looking for a design pattern template . Pls see: http://www.oodesign.com/template-method-pattern.html

or just find the template using google, there are very interesting sites about it. Hope I can help.

+1
source

So what I would do is this:

Add a new signature for doSomething to the base class

 public final void doSomthing(boolean testBeforeDoing, ..../*original params*/) { if (!testBeforeDoing || this.getIsActive()) { doSomething(..../*original params*/) } } 

and

 public void doSomething(..../*original params*/) {} 

and, of course, add support for isActive in the base class.

now any old code that was used to call doSomething in the base class or in any subclass with the old signature will still work the same way (i.e. ignores the active / inactive state of the instance.

Any code that wants to use the new ability to deactivate an instance can call .setIsActive(bool) and add true as the first parameter to any call it makes for doSomething .

Thus, I get the following benefits: 1) Backward compatibility 2) No changes required for subclass 3) Minimum changes if you want to use new functionality

But this method has some limitations, for example, if doSomething already has 2 signatures, which at the beginning differ from one Boolean, this does not work.

i.e. if I had doSomething(boolean b,int i) and doSomething(boolean a,boolean b,int i) than adding bool testBeforeDoing to the first signature would result in doSomething(boolean testBeforeDoing,boolean b,int i) , which is indistinguishable from doSomething(boolean a,boolean b,int i) and therefore cannot be executed.

So what I did is the following:

Instead of adding boolean testBeforeDoing I just add doSomthingIfItCanBeDone to the class based on the class .. and let it call doSomething() if it knows that someThingCanBeDone() and all of the above advantages remain without a drawback.

So

 public final void doSomthingIfItCanBeDone(..../*original params*/) { if (this.someThingCanBeDone()) { doSomething(..../*original params*/) } } public boolean someThingCanBeDone() { return this.isActive(); } 

and, of course, add support for isActive in the base class.

Note that this someThingCanBeDone() method can also be overridden by subclasses to provide additional restrictions when doSomething can be called and when it cannot.

 @Override public boolean someThingCanBeDone() { return base.someThingCanBeDone() && someConditionLocalToTheSubclass; } 
+1
source

All Articles