Zarif hakkında bir şey bilmiyorum, ama burada Java'nın yerleşik yöntemini kullanarak tüm yöntem çağrılarının durumunu kontrol ederek başlamasını java.lang.reflect.Proxysağlayan bir uygulama var .Fooenabled
main yöntem:
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 arayüz:
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 sınıf:
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);
}
}
}
Diğerlerinin de belirttiği gibi, endişelenmeniz gereken bir avuç yönteminiz varsa ihtiyacınız olan şey için aşırıya kaçmış gibi görünüyor.
Bununla birlikte, kesinlikle faydaları var:
- Endişelerin belirli bir şekilde ayrılması sağlanır, çünkü
Foometot uygulamalarının enabledçapraz kesişme endişesini kontrol etmesine gerek yoktur . Bunun yerine, yöntemin kodu yalnızca yöntemin birincil amacının ne olduğu konusunda endişelenmelidir, başka bir şey değildir.
- Masum bir geliştiricinin
Foosınıfa yeni bir yöntem eklemesinin ve yanlışlıkla enabledçek eklemeyi "unutmanın" bir yolu yoktur . enabledKontrol davranışı otomatik olarak yeni eklenen bir yöntem tarafından devralınır.
- Başka bir çapraz kesilme endişesi eklemeniz gerekiyorsa veya
enabled kontrolü , bunu güvenli bir şekilde ve tek bir yerde yapmak çok kolaydır.
- Yerleşik Java işlevselliğiyle bu AOP benzeri davranışı elde edebilmeniz hoş bir şey.
SpringKesinlikle iyi seçenekler olabilmesine rağmen, başka bir çerçeveyi entegre etmek zorunda kalmazsınız .
Adil olmak gerekirse, bazı dezavantajları:
- Proxy çağrılarını işleyen uygulama kodlarından bazıları çirkin. Bazıları,
FooImplsınıfın örneklenmesini önlemek için iç sınıflara sahip olmanın çirkin olduğunu da söyleyebilir .
- Yeni bir yöntem eklemek istiyorsanız
Foo, 2 noktada değişiklik yapmanız gerekir: uygulama sınıfı ve arabirim. Büyük bir anlaşma değil, ama yine de biraz daha fazla iş.
- Proxy çağrıları ücretsiz değildir. Belirli bir performans yükü vardır. Genel kullanım için, fark edilmeyecektir. Daha fazla bilgi için buraya bakın .
DÜZENLE:
Fabian Streitel'in yorumu, yukarıdaki çözümümle 2 sıkıntıyı düşünmemi sağladı, itiraf edeceğim, kendimden mutlu değilim:
- Çağırma işleyici, "getEnabled" ve "setEnabled" yöntemlerindeki "etkin denetim" i atlamak için sihirli dizeler kullanır. Yöntem adları yeniden düzenlenirse bu kolayca kırılabilir.
- "Etkinleştir-kontrol" davranışını miras almaması gereken yeni yöntemlerin eklenmesi gerektiğinde, geliştiricinin bunu yanlış anlaması oldukça kolay olabilir ve en azından daha fazla büyü eklemek anlamına gelebilir. Teller.
Nokta # 1'i çözmek ve en azından nokta # 2 ile ilgili sorunu hafifletmek için BypassCheck,Foo arabirimdeki " etkinleştirilmiş kontrol ". Bu şekilde, sihirli dizelere hiç ihtiyacım yok ve bir geliştiricinin bu özel durumda doğru bir şekilde yeni bir yöntem eklemesi çok daha kolay hale geliyor.
Ek açıklama çözümünü kullanarak kod şöyle görünür:
main yöntem:
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 açıklama:
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BypassCheck {
}
Foo arayüz:
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 sınıf:
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);
}
}
}