我不知道优雅,但这里有一个使用Java内置功能的工作实现,它强制所有方法调用都从检查状态开始。java.lang.reflect.Proxy
Foo
enabled
main
方法:
public static void main(String[] args) {
Foo foo = Foo.newFoo();
foo.setEnabled(false);
foo.bar(); // won't print anything.
foo.setEnabled(true);
foo.bar(); // prints "Executing method bar"
}
Foo
接口:
public interface Foo {
boolean getEnabled();
void setEnabled(boolean enable);
void bar();
void baz();
void bat();
// Needs Java 8 to have this convenience method here.
static Foo newFoo() {
FooFactory fooFactory = new FooFactory();
return fooFactory.makeFoo();
}
}
FooFactory
类:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class FooFactory {
public Foo makeFoo() {
return (Foo) Proxy.newProxyInstance(
this.getClass().getClassLoader(),
new Class[]{Foo.class},
new FooInvocationHandler(new FooImpl()));
}
private static class FooImpl implements Foo {
private boolean enabled = false;
@Override
public boolean getEnabled() {
return this.enabled;
}
@Override
public void setEnabled(boolean enable) {
this.enabled = enable;
}
@Override
public void bar() {
System.out.println("Executing method bar");
}
@Override
public void baz() {
System.out.println("Executing method baz");
}
@Override
public void bat() {
System.out.println("Executing method bat");
}
}
private static class FooInvocationHandler implements InvocationHandler {
private FooImpl fooImpl;
public FooInvocationHandler(FooImpl fooImpl) {
this.fooImpl = fooImpl;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getDeclaringClass() == Foo.class &&
!method.getName().equals("getEnabled") &&
!method.getName().equals("setEnabled")) {
if (!this.fooImpl.getEnabled()) {
return null;
}
}
return method.invoke(this.fooImpl, args);
}
}
}
正如其他人所指出的那样,如果您只有少数几种方法需要担心,那么对于您需要的东西来说,这似乎确实有点过分了。
也就是说,肯定有好处:
- 实现了某种关注点分离,因为 的方法实现不必担心检查横切问题。相反,方法的代码只需要担心方法的主要用途是什么,仅此而已。
Foo
enabled
- 无辜的开发人员无法向类中添加新方法并错误地“忘记”添加检查。任何新添加的方法都会自动继承检查行为。
Foo
enabled
enabled
- 如果您需要添加另一个跨领域问题,或者如果您需要增强检查,那么在一个地方安全地执行此操作非常容易。
enabled
- 您可以通过内置的Java功能获得这种类似AOP的行为,这真是太好了。您不必被迫集成其他一些框架,例如,尽管它们也绝对是不错的选择。
Spring
公平地说,一些缺点是:
- 处理代理调用的某些实现代码很丑陋。有些人还会说,拥有内部类来防止类的实例化是丑陋的。
FooImpl
- 如果要向 中添加新方法,则必须在 2 个位置进行更改:实现类和接口。没什么大不了的,但还需要做更多的工作。
Foo
- 代理调用不是免费的。存在一定的性能开销。但是,对于一般用途,它不会引起注意。有关详细信息,请参阅此处。
编辑:
Fabian Streitel的评论让我想到了上述解决方案的2个烦恼,我承认,我对自己不满意:
- 调用处理程序使用魔术字符串跳过“getEnabled”和“setEnabled”方法上的“enabled-check”。如果重构方法名称,这很容易中断。
- 如果存在需要添加不应继承“enabled-check”行为的新方法的情况,那么开发人员很容易犯错,至少,这意味着添加更多的魔术字符串。
为了解决第 1 点,并至少缓解第 2 点的问题,我将创建一个注释(或类似的东西),我可以使用该注释来标记我不想执行“启用检查”的界面中的方法。这样,我根本不需要魔术字符串,并且开发人员在这种特殊情况下正确添加新方法变得更加容易。BypassCheck
Foo
使用注释解决方案,代码将如下所示:
main
方法:
public static void main(String[] args) {
Foo foo = Foo.newFoo();
foo.setEnabled(false);
foo.bar(); // won't print anything.
foo.setEnabled(true);
foo.bar(); // prints "Executing method bar"
}
BypassCheck
注解:
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BypassCheck {
}
Foo
接口:
public interface Foo {
@BypassCheck boolean getEnabled();
@BypassCheck void setEnabled(boolean enable);
void bar();
void baz();
void bat();
// Needs Java 8 to have this convenience method here.
static Foo newFoo() {
FooFactory fooFactory = new FooFactory();
return fooFactory.makeFoo();
}
}
FooFactory
类:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class FooFactory {
public Foo makeFoo() {
return (Foo) Proxy.newProxyInstance(
this.getClass().getClassLoader(),
new Class[]{Foo.class},
new FooInvocationHandler(new FooImpl()));
}
private static class FooImpl implements Foo {
private boolean enabled = false;
@Override
public boolean getEnabled() {
return this.enabled;
}
@Override
public void setEnabled(boolean enable) {
this.enabled = enable;
}
@Override
public void bar() {
System.out.println("Executing method bar");
}
@Override
public void baz() {
System.out.println("Executing method baz");
}
@Override
public void bat() {
System.out.println("Executing method bat");
}
}
private static class FooInvocationHandler implements InvocationHandler {
private FooImpl fooImpl;
public FooInvocationHandler(FooImpl fooImpl) {
this.fooImpl = fooImpl;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getDeclaringClass() == Foo.class
&& !method.isAnnotationPresent(BypassCheck.class) // no magic strings
&& !this.fooImpl.getEnabled()) {
return null;
}
return method.invoke(this.fooImpl, args);
}
}
}