我不了解优雅,但这是一个使用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
是丑陋的。
- 如果要向中添加新方法
Foo
,则必须在2个地方进行更改:实现类和接口。没什么大不了的,但是还有很多工作要做。
- 代理调用不是免费的。有一定的性能开销。对于一般用途,它不会引起注意。有关更多信息,请参见此处。
编辑:
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);
}
}
}