나는 우아한 것에 대해 잘 모르지만 여기에 모든 메소드 호출 이 상태 를 확인하여 시작 java.lang.reflect.Proxy하도록 강제 하는 Java 내장 기능을 사용하여 작동하는 구현이 있습니다.Fooenabled
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하려면 구현 클래스와 인터페이스의 두 지점을 변경해야합니다. 큰 문제는 아니지만 여전히 조금 더 많은 작업입니다.
- 프록시 호출은 무료가 아닙니다. 특정 성능 오버 헤드가 있습니다. 그러나 일반적인 용도로는 눈에 띄지 않습니다. 자세한 내용은 여기 를 참조하십시오.
편집하다:
Fabian Streitel의 의견은 위의 해결책으로 2 가지 성가심에 대해 생각하게 만들었습니다.
- 호출 핸들러는 매직 문자열을 사용하여 "getEnabled"및 "setEnabled"메소드에서 "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);
}
}
}