SORU
29 HAZİRAN 2015, PAZARTESİ


Zarif bir şekilde belirli kod bloğu ile sınıf bir başlangıç yapmak için her yöntem var mı?

Her yöntem aynı şekilde başladığı bir Dersim var:

class Foo {
  public void bar() {
    if (!fooIsEnabled) return;
    //...
  }
  public void baz() {
    if (!fooIsEnabled) return;
    //...
  }
  public void bat() {
    if (!fooIsEnabled) return;
    //...
  }
}

Güzel bir şekilde (ve umarım her zaman yazmak değil) sınıftaki her kamu yöntemi için fooIsEnabled kısmı ihtiyaç var mı?

CEVAP
29 HAZİRAN 2015, PAZARTESİ


Zarif bilmem, ama burada çalışan bir Java uygulaması kullanarak yerleşik java.lang.reflect.Proxyzorlartüm yöntem enabled durumunu kontrol Foo başlar çağırmaları.

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ğerleri belirttiği gibi, eğer sadece endişelenecek bir yöntem bir avuç ise Ne için overkill gibi görünüyor.

Bu, kesinlikle faydası vardır " dedi

  • Endişeleri, belli bir ayırma Foo'In yöntemi uygulamaları enabled çapraz kontrol-kesme endişe hakkında endişelenmenize gerek yok. çünkü, elde edilir Bunun yerine, yöntem kod tek yöntem öncelikli amacının ne olduğunu, endişe edecek bir şey lazım.
  • Masum bir geliştirici Foo sınıfı için yeni bir yöntem eklemek ve yanlışlıkla "enabled eklemek için" kontrol edin. unutmak için bir yol var mı enabled onay davranış otomatik olarak yeni eklenen herhangi bir yöntem tarafından devralındı.
  • Eğer başka bir çapraz kesim endişe eklemeniz gerekiyorsa, ya da eğer enabled kontrol geliştirmek için ihtiyacınız varsa, çok kolay, çok güvenli ve tek bir yerde.
  • Yerleşik Java işlevselliği ile yapmayan gibi bu davranış, alabilirsiniz, o da güzel. Kesinlikle iyi seçenekler de olabilir ama Spring gibi başka bir çerçeve entegre olması zorunlu değildir.

Adil olmak gerekirse, bu olumsuzlukları şunlardır:

  • Proxy çağırmaları işleyen uygulama kodu bazı çirkin. Biraz da iç sınıflar FooImpl sınıf örnekleme önlemek zorunda çirkin olduğunu söyleyebilirim.
  • Eğer Foo, yeni bir yöntem eklemek istiyorsanız 2 pozisyon değişikliği yapmak zorunda: uygulama sınıfı ve arabirimi. Büyük bir anlaşma değil, ama yine de biraz daha çalışması gerekiyor.
  • Proxy çağırmaları özgür değildir. Belirli bir performans yükü var. Genel kullanım için olsa da, fark etmeyecek. Daha fazla bilgi için here bkz.

DÜZENLEME:

Fabian Streitel yorum, kabul ediyorum, kendimi mutlu değilim bu benim yukarıda çözüm 2 can sıkma konusunda beni düşünmeye sevk etti:

  1. Çağrı işleyicisi sihirli dizeleri "-kontrol etkin" "" ve "setEnabled" yöntemleri. getEnabled üzerine atlamak için kullanır Bu kolay yöntem adları refactored eğer kırabilir.
  2. Varsa bir durum, yeni yöntemler gerekiyor eklenecek değil miras "etkin kontrol" davranış, o zaman olabilir oldukça kolay bir geliştirici olsun, bu yanlış, ve en azından, değil mi yani ekleme daha büyülü dizeleri.

Çözmek noktası #1 ve en az kolay sorun noktası #2, I would oluşturmak bir açıklama BypassCheck (veya benzeri) bu olabilir mark yöntemleri Foo arayüz için hangi istemiyorum gerçekleştirmek için "etkin kontrol". Bu şekilde, sihirli dizeleri hiç ihtiyacım yok, ve çok doğru bir geliştirici bu özel durumda yeni bir yöntem eklemek için daha kolay olur.

Açıklama çözümü kullanarak, bu kod, bu gibi görünecektir:

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

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • Andrey Menshikov

    Andrey Mensh

    28 Ocak 2012
  • Caroline Martin

    Caroline Mar

    19 EYLÜL 2008
  • Max Lee

    Max Lee

    18 AĞUSTOS 2006