Java'da C ++ 'arkadaş' kavramını simüle etmenin bir yolu var mı?


197

Diğer sınıfın bir alt sınıfı yapmak zorunda kalmadan başka bir pakette bir sınıfın ortak olmayan yöntemlerine erişebilen bir pakette bir Java sınıfı yazabilmek istiyorum. Mümkün mü?

Yanıtlar:


468

İşte C ++ arkadaş mekanizmasını çoğaltmak için JAVA kullandığım küçük bir hile.

Diyelim ki bir sınıfım Romeove başka bir sınıfım var Juliet. Nefret nedenleriyle farklı paketler (aile) içindeler.

Romeoistediği cuddle Julietve Julietsadece izin istiyor Romeo cuddleonu.

C ++ ' da (sevgili) Julietilan eder Romeo, friendancak java'da böyle bir şey yoktur.

İşte sınıflar ve püf noktası:

Önce bayanlar :

package capulet;

import montague.Romeo;

public class Juliet {

    public static void cuddle(Romeo.Love love) {
        Objects.requireNonNull(love);
        System.out.println("O Romeo, Romeo, wherefore art thou Romeo?");
    }

}

Yani yöntem Juliet.cuddle, publicama Romeo.Loveonu çağırmak için a'ya ihtiyacınız var. Bu kullanır Romeo.Lovesadece emin olmak için bir "imza güvenlik" olarak Romeobu yöntemi ve sevgi gerçek böylece çalışma zamanı bir atmak olacağını olduğuna kontrolleri çağırabilir NullPointerExceptionEğer öyleyse null.

Şimdi çocuklar:

package montague;

import capulet.Juliet;

public class Romeo {
    public static final class Love { private Love() {} }
    private static final Love love = new Love();

    public static void cuddleJuliet() {
        Juliet.cuddle(love);
    }
}

Sınıf Romeo.Loveherkese açık, ancak kurucusu private. Bu nedenle herkes onu görebilir, ancak sadece Romeoinşa edebilir. Ben Romeo.Loveasla kullanılmayan sadece bir kez inşa ve optimizasyonu etkilemez böylece statik bir başvuru kullanın .

Bu nedenle, Romeocan cuddle Julietve sadece kendisinin çünkü sadece o bir inşa ve erişebilir edebilir Romeo.Lovegereklidir örneği, Julietiçin cuddleona (veya başka o size tokat edeceğiz NullPointerException).


107
+1 "bir NullPointerException ile tokat" için. Çok etkileyici.
Nickolas

2
@Steazy Var: NotNull, NonNull ve CheckForNull ek açıklamalarına bakın. Bu ek açıklamaların nasıl kullanılacağını ve uygulanacağını öğrenmek için IDE'nizin belgelerine bakın. IntelliJ varsayılan olarak bu gömer ve bu tutulma bir eklenti (FindBugs gibi) ihtiyacı olduğunu biliyorum.
Salomon BRYS

27
Sen yapabilir Romeo's Loveiçin Juliadeğiştirerek sonsuz loveolmasını alanını final;-).
Matthias

5
@Matthias Aşk alanı statik ... Son yapmak için cevabı düzenleyeceğim;)
Salomon BRYS

12
Mizah ve harika örnek için tüm cevaplar bu şekilde (Y) +1 olmalıdır.
Zia Ul Rehman Mughal

54

Java tasarımcıları C ++ 'da çalışırken arkadaş fikrini açıkça reddetti. "Arkadaşlarınızı" aynı pakete koydunuz. Özel, korumalı ve paketlenmiş güvenlik, dil tasarımının bir parçası olarak uygulanır.

James Gosling Java'nın hatasız C ++ olmasını istedi. Arkadaşının bir hata olduğunu düşündüğüne inanıyorum çünkü OOP ilkelerini ihlal ediyor. Paketler, OOP konusunda fazla saf olmadan bileşenleri organize etmek için makul bir yol sağlar.

NR, yansıma kullanarak hile yapabileceğinizi belirtti, ancak bu bile yalnızca SecurityManager'ı kullanmıyorsanız işe yarar. Java standart güvenliğini açarsanız, özellikle izin vermek için güvenlik ilkesi yazmadığınız sürece yansıma ile hile yapamazsınız.


11
Bilgiç olmak istemiyorum, ancak erişim değiştiriciler bir güvenlik mekanizması değil.
Greg D

6
Erişim değiştiriciler, java güvenlik modelinin bir parçasıdır. Özellikle yansıma için java.lang.RuntimePermission atıfta bulundu: accessDeclaredMembers ve accessClassInPackage.
David G

54
Gosling gerçekten OOP'u friendihlal ettiğini düşündüyse (özellikle paket erişiminden daha fazlası) gerçekten anlamadı (tamamen mümkün, birçok kişi yanlış anladı).
Konrad Rudolph

8
Sınıf bileşenlerinin bazen ayrılması gerekir (örn. Uygulama ve API, çekirdek nesne ve bağdaştırıcı). Paket düzeyinde koruma aynı zamanda bunu yapmak için çok izin vericidir ve çok kısıtlayıcıdır.
dhardy

2
@GregD Onlar olabilir onlar yanlış bir sınıf üyesi kullanmasını geliştiriciler önlemeye yardımcı anlamda bir güvenlik mekanizması düşünülebilir. Bence güvenlik mekanizması olarak adlandırılmaları daha iyi olur .
ezmek

45

'Arkadaş' kavramı Java'da, örneğin bir API'nın uygulanmasından ayrılması için kullanışlıdır. Uygulama sınıflarının API sınıfı iç öğelerine erişmesi yaygındır, ancak bunlar API istemcilerine maruz bırakılmamalıdır. Bu, aşağıda ayrıntıları verilen 'Friend Accessor' kalıbı kullanılarak gerçekleştirilebilir:

API aracılığıyla maruz kalan sınıf:

package api;

public final class Exposed {
    static {
        // Declare classes in the implementation package as 'friends'
        Accessor.setInstance(new AccessorImpl());
    }

    // Only accessible by 'friend' classes.
    Exposed() {

    }

    // Only accessible by 'friend' classes.
    void sayHello() {
        System.out.println("Hello");
    }

    static final class AccessorImpl extends Accessor {
        protected Exposed createExposed() {
            return new Exposed();
        }

        protected void sayHello(Exposed exposed) {
            exposed.sayHello();
        }
    }
}

'Arkadaş' işlevselliğini sağlayan sınıf:

package impl;

public abstract class Accessor {

    private static Accessor instance;

    static Accessor getInstance() {
        Accessor a = instance;
        if (a != null) {
            return a;
        }

        return createInstance();
    }

    private static Accessor createInstance() {
        try {
            Class.forName(Exposed.class.getName(), true, 
                Exposed.class.getClassLoader());
        } catch (ClassNotFoundException e) {
            throw new IllegalStateException(e);
        }

        return instance;
    }

    public static void setInstance(Accessor accessor) {
        if (instance != null) {
            throw new IllegalStateException(
                "Accessor instance already set");
        }

        instance = accessor;
    }

    protected abstract Exposed createExposed();

    protected abstract void sayHello(Exposed exposed);
}

'Arkadaş' uygulama paketindeki bir sınıftan örnek erişim:

package impl;

public final class FriendlyAccessExample {
    public static void main(String[] args) {
        Accessor accessor = Accessor.getInstance();
        Exposed exposed = accessor.createExposed();
        accessor.sayHello(exposed);
    }
}

1
Çünkü "Exposed" sınıfta "statik" ne anlama geldiğini bilmiyordum: Statik blok, bir Java sınıfı içinde bir sınıf JVM ilk yüklendiğinde yürütülecek bir ifade bloğudur.
Guy L

İlginç bir desen, ancak bir API uygulayan sınıfların (yani, bir dizi genel Java arabirimini uygulayan bir dizi Java sınıfı) "Exposed ve Accessor" sınıflarının herkese açık olmasını gerektirir, "varsayılan olarak korunur" ve böylece istemci için erişilemez türleri uygulamalarından ayırmak.
Yann-Gaël Guéhéneuc

8
Java'mda oldukça paslıyım, bu yüzden cehaletimi affedin. Salomon BRYS'nin yayınladığı "Romeo ve Juliet" çözümüne göre bunun avantajı nedir? Bir kod tabanı üzerinde tökezledi Eğer bu uygulama pantolonumu korkutmak olacaktır (açıklama ekli olmadan, yani ağır yorum). Romeo ve Juliet yaklaşımının anlaşılması çok basittir.
Steazy

1
Bu yaklaşım sorunları sadece çalışma zamanında görünür hale getirirken Romeo ve Juliet'in yanlış kullanımı, bunları geliştirirken derleme sırasında görünür hale getirecektir.
ymajoros

1
@ymajoros Romeo ve Juliet örneği, yanlış kullanımın derleme zamanında görünmesini sağlamaz. Doğru bir şekilde iletilen bir argümana ve bir istisna atılmasına dayanır. Her ikisi de çalışma zamanı eylemleridir.
Radiodef

10

Sorunuza, tüm sınıfları aynı pakette tutmayı içermeyen iki çözüm var.

Birincisi , (Pratik API Tasarımı, Tulach 2008) 'de açıklanan Arkadaş Erişimcisi / Arkadaş Paketi modelini kullanmaktır .

İkincisi OSGi kullanmaktır. Burada OSGi'nin bunu nasıl başardığını açıklayan bir makale var .

İlgili Sorular: 1 , 2 ve 3 .


7

Bildiğim kadarıyla mümkün değil.

Belki, bize tasarımınız hakkında daha fazla bilgi verebilirsin. Bu gibi sorular muhtemelen tasarım kusurlarının sonucudur.

Sadece düşün

  • Bu sınıflar, bu kadar yakından ilişkili iseler, neden farklı paketler içerisindedir?
  • A'nın B'nin özel üyelerine erişimi var mı yoksa işlem B sınıfına taşınmalı ve A tarafından mı tetiklenmeli?
  • Bu gerçekten mi arıyor veya olay yönetimi daha mı iyi?

3

eirikma'nın cevabı kolay ve mükemmeldir. Bir şey daha ekleyebilirim: halka açık bir yöntem yerine, kullanılamayacak bir arkadaş edinmek için getFriend () yerine, bir adım daha ileri gidebilir ve bir belirteç olmadan arkadaş almayı engelleyebilirsiniz: getFriend (Service.FriendToken). Bu FriendToken, özel bir kurucuya sahip bir iç kamu sınıfı olacaktır, böylece yalnızca Hizmet bir örneği başlatabilirdi.


3

Yeniden kullanılabilir bir Friendsınıf içeren açık bir kullanım örneği örneği . Bu mekanizmanın yararı kullanım kolaylığıdır. Birim test sınıflarına uygulamanın geri kalanından daha fazla erişim vermek için iyi olabilir.

Başlamak için, Friend sınıfın .

public class Owner {
    private final String member = "value";

    public String getMember(final Friend friend) {
        // Make sure only a friend is accepted.
        friend.is(Other.class);
        return member;
    }
}

Sonra başka bir pakette şunları yapabilirsiniz:

public class Other {
    private final Friend friend = new Friend(this);

    public void test() {
        String s = new Owner().getMember(friend);
        System.out.println(s);
    }
}

FriendŞöyle sınıftır.

public final class Friend {
    private final Class as;

    public Friend(final Object is) {
        as = is.getClass();
    }

    public void is(final Class c) {
        if (c == as)
            return;
        throw new ClassCastException(String.format("%s is not an expected friend.", as.getName()));
    }

    public void is(final Class... classes) {
        for (final Class c : classes)
            if (c == as)
                return;
        is((Class)null);
    }
}

Ancak, sorun şu şekilde kötüye kullanılmasıdır:

public class Abuser {
    public void doBadThings() {
        Friend badFriend = new Friend(new Other());
        String s = new Owner().getMember(badFriend);
        System.out.println(s);
    }
}

Şimdi, Othersınıfın herhangi bir kamu kurucuya sahip olmadığı doğru olabilir , bu nedenle yukarıdaki Abuserkodu imkansız hale getirir . Sınıf Ancak, does bir public kurucu sahip o zaman bir iç sınıf olarak Arkadaş sınıfını çoğaltmak için muhtemelen tavsiye edilir. Bu Other2sınıfı örnek olarak alın :

public class Other2 {
    private final Friend friend = new Friend();

    public final class Friend {
        private Friend() {}
        public void check() {}
    }

    public void test() {
        String s = new Owner2().getMember(friend);
        System.out.println(s);
    }
}

Ve sonra Owner2sınıf şöyle olurdu:

public class Owner2 {
    private final String member = "value";

    public String getMember(final Other2.Friend friend) {
        friend.check();
        return member;
    }
}

Other2.FriendSınıfın özel bir kurucuya sahip olduğuna dikkat edin , böylece bunu daha güvenli bir yol haline getirin.


2

Sağlanan çözüm belki de en basit değildi. Başka bir yaklaşım, C ++ ile aynı fikre dayanmaktadır: özel üyelerin, sahibinin kendisinin bir arkadaşı yaptığı belirli bir sınıf dışında, paket / özel kapsam dışında erişilemez.

Bir üyeye arkadaş erişimine ihtiyaç duyan sınıf, gizli özelliklere sahip sınıfın, erişim uygulama yöntemlerini uygulayan bir alt sınıf döndürerek erişimi dışa aktarabileceği bir iç genel soyut "arkadaş sınıfı" oluşturmalıdır. Arkadaş sınıfının "API" yöntemi özel olabilir, bu nedenle arkadaş erişimi gerektiren sınıfın dışında erişilebilir değildir. Tek ifadesi, ihracatçı sınıfın uyguladığı soyut korumalı bir üyeye yapılan çağrıdır.

İşte kod:

İlk olarak bunun gerçekten işe yaradığını doğrulayan test:

package application;

import application.entity.Entity;
import application.service.Service;
import junit.framework.TestCase;

public class EntityFriendTest extends TestCase {
    public void testFriendsAreOkay() {
        Entity entity = new Entity();
        Service service = new Service();
        assertNull("entity should not be processed yet", entity.getPublicData());
        service.processEntity(entity);
        assertNotNull("entity should be processed now", entity.getPublicData());
    }
}

Ardından, Varlık paket özel üyesine arkadaş erişimi gerektiren Hizmet:

package application.service;

import application.entity.Entity;

public class Service {

    public void processEntity(Entity entity) {
        String value = entity.getFriend().getEntityPackagePrivateData();
        entity.setPublicData(value);
    }

    /**
     * Class that Entity explicitly can expose private aspects to subclasses of.
     * Public, so the class itself is visible in Entity's package.
     */
    public static abstract class EntityFriend {
        /**
         * Access method: private not visible (a.k.a 'friendly') outside enclosing class.
         */
        private String getEntityPackagePrivateData() {
            return getEntityPackagePrivateDataImpl();
        }

        /** contribute access to private member by implementing this */
        protected abstract String getEntityPackagePrivateDataImpl();
    }
}

Son olarak: paket özel üyesine yalnızca sınıf application.service.Service sınıfına kolay erişim sağlayan Entity sınıfı.

package application.entity;

import application.service.Service;

public class Entity {

    private String publicData;
    private String packagePrivateData = "secret";   

    public String getPublicData() {
        return publicData;
    }

    public void setPublicData(String publicData) {
        this.publicData = publicData;
    }

    String getPackagePrivateData() {
        return packagePrivateData;
    }

    /** provide access to proteced method for Service'e helper class */
    public Service.EntityFriend getFriend() {
        return new Service.EntityFriend() {
            protected String getEntityPackagePrivateDataImpl() {
                return getPackagePrivateData();
            }
        };
    }
}

Tamam, itiraf etmeliyim ki "friend service :: Service" den biraz daha uzun; ancak ek açıklamaları kullanarak derleme zamanı denetimini sürdürürken kısaltmak mümkün olabilir.


Bu aynı paket içinde normal bir sınıf olarak sadece getFriend () ve daha sonra özel olanı atlayarak korumalı yöntemi çağırabilir gibi çalışmıyor.
user2219808

1

Java'da "paketle ilgili bir dostluğa" sahip olmak mümkündür. Bu birim testi için yararlı olabilir. Bir yöntemin önünde özel / genel / korumalı belirtmezseniz, "paketteki arkadaş" olur. Aynı paketteki bir sınıf bu pakete erişebilir, ancak sınıf dışında özel olacaktır.

Bu kural her zaman bilinmemektedir ve bir C ++ "arkadaş" anahtar kelimesi için iyi bir yaklaşımdır. İyi bir yedek buldum.


1
Bu doğru, ama gerçekten farklı paketlerdeki kodu soruyordum ...
Matthew Murdoch

1

C ++ arkadaş sınıfları Java iç sınıf kavramı gibi olduğunu düşünüyorum. İç sınıfları kullanarak aslında bir kapalı sınıf ve bir kapalı sınıf tanımlayabilirsiniz. Kapalı sınıf, kapalı sınıfın genel ve özel üyelerine tam erişime sahiptir. aşağıdaki bağlantıya bakın: http://docs.oracle.com/javase/tutorial/java/javaOO/nested.html


Er, hayır, değiller. Daha çok gerçek hayatta dostluk gibidir: Karşılıklı olabilir, ancak olması gerekmez, B'nin bir arkadaşı olmak, B'nin A'nın bir arkadaşı olduğu anlamına gelmez) ve siz ve arkadaşlarınız tamamen farklı olabilirsiniz. ve muhtemelen (ama zorunlu olarak değil) kendi arkadaş çevreleriniz vardır. (Sınıfları çok arkadaşımla görmek istediğimden değil. Yararlı bir özellik olabilir, ancak dikkatle kullanılmalıdır.)
Christopher Creutzig

1

Arkadaş erişim düzenini kullanma yaklaşımı çok karmaşık. Aynı sorunla yüzleşmek zorunda kaldım ve Java'da C ++ 'dan bilinen iyi, eski kopya oluşturucuyu kullanarak çözdüm:

public class ProtectedContainer {
    protected String iwantAccess;

    protected ProtectedContainer() {
        super();
        iwantAccess = "Default string";
    }

    protected ProtectedContainer(ProtectedContainer other) {
        super();
        this.iwantAccess = other.iwantAccess;
    }

    public int calcSquare(int x) {
        iwantAccess = "calculated square";
        return x * x;
    }
}

Başvurunuzda aşağıdaki kodu yazabilirsiniz:

public class MyApp {

    private static class ProtectedAccessor extends ProtectedContainer {

        protected ProtectedAccessor() {
            super();
        }

        protected PrivateAccessor(ProtectedContainer prot) {
            super(prot);
        }

        public String exposeProtected() {
            return iwantAccess;
        }
    }
}

Bu yöntemin avantajı, yalnızca uygulamanızın korunan verilere erişimi olmasıdır. Bu, tam olarak arkadaş anahtar kelimesinin yerine geçmez. Ancak, özel kütüphaneler yazdığınızda ve korumalı verilere erişmeniz gerektiğinde oldukça uygun olduğunu düşünüyorum.

ProtectedContainer örnekleri ile uğraşmanız gerektiğinde, ProtectedAccessor'unuzu etrafına sarabilirsiniz ve erişim elde edersiniz.

Ayrıca korumalı yöntemlerle de çalışır. Bunları API'nızda korumalı olarak tanımlarsınız. Daha sonra uygulamanızda özel bir sarmalayıcı sınıfı yazar ve korunan yöntemi herkese açık olarak ortaya koyarsınız. Bu kadar.


1
Ancak ProtectedContainerpaketin dışında alt sınıflara ayrılabilir!
Raphael

0

Korumalı yöntemlere erişmek istiyorsanız, kullanmak istediğiniz sınıfın herkese açık (veya daha güvenli olması için ad alanına dahili olarak) kullanmak istediğiniz yöntemleri gösteren ve sınıfınızda bu sınıfın bir örneğini içeren bir alt sınıf oluşturabilirsiniz. (proxy olarak kullanın).

Özel yöntemler söz konusu olduğunda (bence) şansınız kalmadı.


0

Çoğu durumda arkadaş anahtar kelimesinin gereksiz olduğunu kabul ediyorum.

  • Paket-özel (aka. Varsayılan), bir grup yoğun iç içe sınıfın olduğu çoğu durumda yeterlidir
  • İç kısımlara erişim isteyen hata ayıklama sınıfları için, genellikle yöntemi özel hale getirir ve yansıma yoluyla erişirim. Burada hız genellikle önemli değil
  • Bazen, "hack" olan veya değişikliğe tabi olan bir yöntemi uygularsınız. Herkese açık hale getiriyorum, ancak mevcut bu yönteme güvenmemeniz gerektiğini belirtmek için @Deprecated kullanın.

Ve son olarak, gerçekten gerekliyse, diğer cevaplarda bahsedilen arkadaş erişimci modeli var.


0

Bir anahtar kelime kullanmamak.

"Yansıma vb kullanarak" hile ", ama" hile "tavsiye etmem.


3
bunu öyle kötü bir fikir olarak görüyorum ki, bunu önermek bile benim için iğrenç. Açıkçası bu en iyi ihtimalle bir ipucudur ve herhangi bir tasarımın parçası olmamalıdır.
shsteimer

0

Bu sorunu çözmek için bulduğum bir yöntem şöyle bir erişimci nesnesi oluşturmaktır:

class Foo {
    private String locked;

    /* Anyone can get locked. */
    public String getLocked() { return locked; }

    /* This is the accessor. Anyone with a reference to this has special access. */
    public class FooAccessor {
        private FooAccessor (){};
        public void setLocked(String locked) { Foo.this.locked = locked; }
    }
    private FooAccessor accessor;

    /** You get an accessor by calling this method. This method can only
     * be called once, so calling is like claiming ownership of the accessor. */
    public FooAccessor getAccessor() {
        if (accessor != null)
            throw new IllegalStateException("Cannot return accessor more than once!");
        return accessor = new FooAccessor();
    }
}

getAccessor()" Kod sahibi" olarak adlandırılan ilk kod erişimcinin sahipliğini talep eder. Genellikle, nesneyi oluşturan kod budur.

Foo bar = new Foo(); //This object is safe to share.
FooAccessor barAccessor = bar.getAccessor(); //This one is not.

Bu, C ++ 'ın arkadaş mekanizmasına göre bir avantaja sahiptir, çünkü sınıf başına bir seviyenin aksine, örnek başına düzeyde erişimi sınırlamanıza izin verir . Erişimci referansını denetleyerek, nesneye erişimi denetlersiniz. Ayrıca, birden çok erişimci oluşturabilir ve her birine farklı erişim verebilirsiniz; bu, hangi kodun neye erişebileceği üzerinde hassas bir denetime izin verir:

class Foo {
    private String secret;
    private String locked;

    /* Anyone can get locked. */
    public String getLocked() { return locked; }

    /* Normal accessor. Can write to locked, but not read secret. */
    public class FooAccessor {
        private FooAccessor (){};
        public void setLocked(String locked) { Foo.this.locked = locked; }
    }
    private FooAccessor accessor;

    public FooAccessor getAccessor() {
        if (accessor != null)
            throw new IllegalStateException("Cannot return accessor more than once!");
        return accessor = new FooAccessor();
    }

    /* Super accessor. Allows access to secret. */
    public class FooSuperAccessor {
        private FooSuperAccessor (){};
        public String getSecret() { return Foo.this.secret; }
    }
    private FooSuperAccessor superAccessor;

    public FooSuperAccessor getAccessor() {
        if (superAccessor != null)
            throw new IllegalStateException("Cannot return accessor more than once!");
        return superAccessor = new FooSuperAccessor();
    }
}

Son olarak, işlerin biraz daha organize olmasını istiyorsanız, her şeyi bir arada tutan bir referans nesne oluşturabilirsiniz. Bu, tüm erişimcileri bir yöntem çağrısıyla talep etmenizi ve bağlantılı örnekleriyle birlikte tutmanızı sağlar. Referansı aldıktan sonra, erişimcileri ihtiyacı olan koda iletebilirsiniz:

class Foo {
    private String secret;
    private String locked;

    public String getLocked() { return locked; }

    public class FooAccessor {
        private FooAccessor (){};
        public void setLocked(String locked) { Foo.this.locked = locked; }
    }
    public class FooSuperAccessor {
        private FooSuperAccessor (){};
        public String getSecret() { return Foo.this.secret; }
    }
    public class FooReference {
        public final Foo foo;
        public final FooAccessor accessor;
        public final FooSuperAccessor superAccessor;

        private FooReference() {
            this.foo = Foo.this;
            this.accessor = new FooAccessor();
            this.superAccessor = new FooSuperAccessor();
        }
    }

    private FooReference reference;

    /* Beware, anyone with this object has *all* the accessors! */
    public FooReference getReference() {
        if (reference != null)
            throw new IllegalStateException("Cannot return reference more than once!");
        return reference = new FooReference();
    }
}

Çok kafa vurduktan sonra (iyi değil), bu benim son çözümümdü ve çok beğendim. Esnek, kullanımı basittir ve sınıf erişimi üzerinde çok iyi kontrol sağlar. ( Yalnızca başvuru ile erişimi çok kullanışlıdır.) Erişimciler / referanslar için özel yerine korumalı kullanırsanız, Foo'nun alt sınıfları genişletilmiş referansları bile döndürebilir getReference. Ayrıca herhangi bir yansıma gerektirmez, bu nedenle herhangi bir ortamda kullanılabilir.


0

Java 9'dan itibaren, birçok durumda bunu bir sorun haline getirmek için modüller kullanılabilir.


0

Ben genel bir sınıf yapmaktan kaçınmak için (bu sorunla sonuçlanan soruna bağlı olarak) temsilci seçme veya kompozisyon veya fabrika sınıfı tercih ederim.

"Farklı paketlerdeki arabirim / uygulama sınıfları" sorunu ise, o zaman impl paketi ile aynı pakette olacak ve impl sınıfının maruz kalmasını önleyen bir genel fabrika sınıfı kullanırdım.

"Farklı bir pakette başka bir sınıf için bu işlevselliği sağlamak için bu sınıf / yöntem genel yapmaktan nefret ediyorum" sorun varsa, o zaman aynı pakette ortak bir delege sınıf kullanır ve işlevsellik yalnızca o kısmını ortaya "yabancı" sınıfın ihtiyacı.

Bu kararlardan bazıları, hedef sunucu sınıf yükleme mimarisi (OSGi paketi, WAR / EAR vb.), Dağıtım ve paket adlandırma kuralları tarafından yönlendirilir. Örneğin, yukarıda önerilen çözüm olan 'Friend Accessor' deseni normal java uygulamaları için akıllıdır. Sınıf yükleme stilindeki fark nedeniyle OSGi'de uygulamak zorlaşıyor mu acaba?


0

Kimseye herhangi bir yarar olup olmadığını bilmiyorum ama ben şu şekilde ele:

Bir arayüz oluşturdum (AdminRights).

Bu işlevleri çağırabilmesi gereken her sınıfın AdminRights uygulaması gerekir.

Sonra HasAdminRights aşağıdaki gibi bir fonksiyon yarattım:

private static final boolean HasAdminRights()
{
    // Gets the current hierarchy of callers
    StackTraceElement[] Callers = new Throwable().getStackTrace();

    // Should never occur with me but if there are less then three StackTraceElements we can't check
    if (Callers.length < 3)
    {
        EE.InvalidCode("Couldn't check for administrator rights");
        return false;

    } else try
    {

        // Now we check the third element as this function is the first and the function wanting to check for the rights the second. We try to use it as a subclass of AdminRights.
        Class.forName(Callers[2].getClassName()).asSubclass(AdminRights.class);

        // If everything worked up to now, it has admin rights!
        return true;

    } catch (java.lang.ClassCastException | ClassNotFoundException e)
    {
        // In the catch, something went wrong and we can deduce that the caller has no admin rights

        EE.InvalidCode(Callers[1].getClassName() + " doesn't have administrator rights");
        return false;
    }
}

-1

Bir keresinde yansıma kullanarak ve yöntemi çağırmak sınıf bunu yapmak için izin verilmiş olup olmadığını görmek için çağrı yığını kontrol çalışma zamanında "arkadaş kontrol" yaptı yansıma tabanlı bir çözüm gördüm. Bir çalışma zamanı kontrolü olarak, belirgin bir dezavantajı vardır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.