ฉันต้องการที่จะสามารถเขียนคลาส Java ในแพ็คเกจหนึ่งซึ่งสามารถเข้าถึงวิธีการที่ไม่ใช่แบบสาธารณะของคลาสในแพ็กเกจอื่นโดยไม่ต้องทำให้คลาสย่อยของคลาสอื่น เป็นไปได้ไหม
ฉันต้องการที่จะสามารถเขียนคลาส Java ในแพ็คเกจหนึ่งซึ่งสามารถเข้าถึงวิธีการที่ไม่ใช่แบบสาธารณะของคลาสในแพ็กเกจอื่นโดยไม่ต้องทำให้คลาสย่อยของคลาสอื่น เป็นไปได้ไหม
คำตอบ:
นี่เป็นเคล็ดลับเล็ก ๆ ที่ฉันใช้ใน JAVA เพื่อทำซ้ำกลไกเพื่อน C ++
ให้บอกว่าฉันมีชั้นเรียนRomeo
และชั้นเรียนอื่นJuliet
และชั้นอื่นพวกเขาอยู่ในแพ็คเกจที่แตกต่างกัน (ครอบครัว) ด้วยเหตุผลความเกลียดชัง
Romeo
ต้องการcuddle
Juliet
และJuliet
ต้องการให้เท่านั้นRomeo
cuddle
เธอเท่านั้น
ใน C ++ Juliet
จะประกาศRomeo
เป็น (คนรัก) friend
แต่ไม่มีสิ่งเช่นนี้ใน java
นี่คือคลาสและเคล็ดลับ:
สุภาพสตรีก่อน:
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?");
}
}
ดังนั้นวิธีการJuliet.cuddle
คือpublic
แต่คุณต้องRomeo.Love
เรียกมัน มันใช้นี้Romeo.Love
เป็น "ลายเซ็นการรักษาความปลอดภัย" เพื่อให้แน่ใจว่าRomeo
สามารถเรียกวิธีนี้และตรวจสอบว่าความรักที่เป็นจริงเพื่อให้รันไทม์จะโยนถ้ามันเป็นNullPointerException
null
ตอนนี้เด็ก ๆ :
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);
}
}
ชั้นRomeo.Love
เป็นที่สาธารณะ private
แต่คอนสตรัคของมันคือ ดังนั้นทุกคนสามารถเห็นได้ แต่Romeo
สามารถสร้างได้ ฉันใช้การอ้างอิงแบบคงที่ดังนั้นสิ่งRomeo.Love
ที่ไม่เคยใช้จะถูกสร้างเพียงครั้งเดียวและไม่ส่งผลกระทบต่อการปรับให้เหมาะสม
ดังนั้นRomeo
สามารถcuddle
Juliet
และมีเพียงเขาสามารถเพราะเพียง แต่เขาสามารถสร้างและเข้าถึงRomeo.Love
อินสแตนซ์ที่ถูกต้องตามJuliet
เพื่อcuddle
เธอ (หรืออื่น ๆ ที่เธอจะตบคุณด้วยNullPointerException
)
Romeo
's Love
สำหรับJulia
นิรันดร์โดยการเปลี่ยนlove
ข้อมูลให้เป็นfinal
;-)
นักออกแบบของ Java ปฏิเสธแนวคิดของเพื่อนอย่างชัดเจนเนื่องจากทำงานใน C ++ คุณใส่ "เพื่อน" ของคุณในแพ็คเกจเดียวกัน ความปลอดภัยส่วนบุคคลที่ได้รับการป้องกันและจัดแพคเกจนั้นมีการบังคับใช้เป็นส่วนหนึ่งของการออกแบบภาษา
James Gosling ต้องการให้ Java เป็น C ++ โดยไม่มีข้อผิดพลาด ฉันเชื่อว่าเขารู้สึกว่าเพื่อนคนนั้นเป็นความผิดพลาดเพราะมันละเมิดหลักการของ OOP แพคเกจให้วิธีการที่เหมาะสมในการจัดระเบียบส่วนประกอบโดยไม่พิถีพิถันเกินไปเกี่ยวกับ OOP
NR ชี้ให้เห็นว่าคุณสามารถโกงโดยใช้การสะท้อนได้ แต่แม้จะใช้งานได้ก็ต่อเมื่อคุณไม่ได้ใช้ SecurityManager หากคุณเปิดการรักษาความปลอดภัยมาตรฐานของ Java คุณจะไม่สามารถโกงการสะท้อนกลับได้เว้นแต่คุณจะเขียนนโยบายความปลอดภัยเพื่อให้อนุญาตเป็นการเฉพาะ
friend
เป็นการละเมิด OOP (โดยเฉพาะมากกว่าการเข้าถึงแพ็คเกจ) เขาก็ไม่เข้าใจจริงๆ (เป็นไปได้ทั้งหมดมีหลายคนเข้าใจผิด)
แนวคิด 'เพื่อน' มีประโยชน์ใน Java เช่นเพื่อแยก API ออกจากการใช้งาน เป็นเรื่องปกติที่คลาสการปรับใช้จะต้องเข้าถึง API ภายในคลาส แต่สิ่งเหล่านี้ไม่ควรสัมผัสกับไคลเอนต์ API สามารถทำได้โดยใช้รูปแบบ 'Friend Accessor' ดังรายละเอียดด้านล่าง:
คลาสที่เปิดเผยผ่าน API:
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();
}
}
}
ชั้นเรียนที่มีฟังก์ชั่น 'เพื่อน':
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);
}
ตัวอย่างการเข้าถึงจากชั้นเรียนในแพ็คเกจการใช้งาน 'เพื่อน':
package impl;
public final class FriendlyAccessExample {
public static void main(String[] args) {
Accessor accessor = Accessor.getInstance();
Exposed exposed = accessor.createExposed();
accessor.sayHello(exposed);
}
}
เท่าที่ฉันรู้มันเป็นไปไม่ได้
บางทีคุณอาจให้รายละเอียดเพิ่มเติมเกี่ยวกับการออกแบบของคุณ คำถามเช่นนี้น่าจะเป็นผลมาจากข้อบกพร่องในการออกแบบ
เพียงแค่พิจารณา
คำตอบของ eirikma นั้นง่ายและยอดเยี่ยม ฉันอาจเพิ่มอีกหนึ่งสิ่ง: แทนที่จะมีวิธีการเข้าถึงแบบสาธารณะ getFriend () เพื่อรับเพื่อนที่ไม่สามารถใช้งานได้คุณสามารถไปอีกขั้นหนึ่งแล้วไม่อนุญาตให้เพื่อนไม่มีโทเค็น: getFriend (Service.FriendToken) FriendToken นี้จะเป็นชั้นเรียนสาธารณะที่มีคอนสตรัคเตอร์ส่วนตัวเพื่อให้บริการเท่านั้นที่สามารถยกตัวอย่างได้
นี่คือตัวอย่างกรณีการใช้งานที่ชัดเจนพร้อมFriend
คลาสที่ใช้ซ้ำได้ ประโยชน์ของกลไกนี้คือใช้งานง่าย อาจดีสำหรับการให้คลาสทดสอบหน่วยเข้าถึงมากกว่าส่วนที่เหลือของแอปพลิเคชัน
เพื่อเริ่มต้นนี่คือตัวอย่างวิธีการใช้Friend
คลาส
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;
}
}
จากนั้นในแพ็คเกจอื่นคุณสามารถทำได้:
public class Other {
private final Friend friend = new Friend(this);
public void test() {
String s = new Owner().getMember(friend);
System.out.println(s);
}
}
Friend
ระดับมีดังนี้
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);
}
}
อย่างไรก็ตามปัญหาคือมันสามารถถูกทารุณกรรมเช่น:
public class Abuser {
public void doBadThings() {
Friend badFriend = new Friend(new Other());
String s = new Owner().getMember(badFriend);
System.out.println(s);
}
}
ตอนนี้มันอาจเป็นความจริงที่ว่าOther
คลาสไม่มีตัวสร้างสาธารณะใด ๆ ดังนั้นจึงไม่สามารถสร้างAbuser
โค้ดด้านบนได้ แต่ถ้าระดับของคุณไม่ได้สร้างสาธารณะแล้วมันอาจจะแนะนำให้ซ้ำชั้นเพื่อนเป็นระดับชั้น นำOther2
คลาสนี้มาเป็นตัวอย่าง:
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);
}
}
แล้วOwner2
ชั้นจะเป็นอย่างนี้:
public class Owner2 {
private final String member = "value";
public String getMember(final Other2.Friend friend) {
friend.check();
return member;
}
}
ขอให้สังเกตว่าOther2.Friend
ชั้นเรียนมีคอนสตรัคเตอร์ส่วนตัวจึงทำให้วิธีนี้ปลอดภัยยิ่งขึ้น
โซลูชันที่ให้มาอาจไม่ใช่วิธีที่ง่ายที่สุด วิธีการอื่นจะขึ้นอยู่กับแนวคิดเดียวกันกับใน C ++: สมาชิกส่วนบุคคลไม่สามารถเข้าถึงได้นอกขอบเขตของแพคเกจ / ส่วนตัวยกเว้นสำหรับคลาสเฉพาะที่เจ้าของเป็นเพื่อนของตัวเอง
คลาสที่ต้องการการเข้าถึงเพื่อนกับสมาชิกควรสร้าง "class class" แบบนามธรรมสาธารณะที่คลาสที่เป็นเจ้าของคุณสมบัติที่ซ่อนอยู่สามารถส่งออกการเข้าถึงโดยส่งคืนคลาสย่อยที่ใช้วิธีการใช้งานแบบเข้าถึง วิธี "API" ของคลาสเพื่อนสามารถเป็นแบบส่วนตัวดังนั้นจึงไม่สามารถเข้าถึงได้นอกคลาสที่ต้องการการเข้าถึงแบบเพื่อน คำสั่งเดียวของมันคือการโทรไปยังสมาชิกที่ได้รับการป้องกันที่เป็นนามธรรมที่ชั้นส่งออกดำเนินการ
นี่คือรหัส:
ก่อนการทดสอบที่ตรวจสอบว่าใช้งานได้จริง:
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());
}
}
จากนั้นบริการที่ต้องการให้เพื่อนเข้าถึงสมาชิกส่วนตัวของ Entity:
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();
}
}
ในที่สุด: คลาส Entity ที่ให้การเข้าถึงที่เป็นมิตรกับสมาชิกส่วนตัวของแพ็กเกจเฉพาะกับคลาส application.service.Service
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();
}
};
}
}
โอเคฉันต้องยอมรับมันนานกว่า "บริการเพื่อน :: บริการ;" แต่อาจเป็นไปได้ที่จะย่อให้สั้นลงในขณะที่ยังคงตรวจสอบเวลาคอมไพล์โดยใช้คำอธิบายประกอบ
ใน Java เป็นไปได้ที่จะมี "มิตรภาพที่เกี่ยวข้องกับแพ็คเกจ" สิ่งนี้สามารถเป็นประโยชน์สำหรับการทดสอบหน่วย หากคุณไม่ได้ระบุส่วนตัว / สาธารณะ / ป้องกันไว้ด้านหน้าของเมธอดมันจะเป็น "เพื่อนในแพ็คเกจ" คลาสในแพ็คเกจเดียวกันจะสามารถเข้าถึงได้ แต่จะเป็นส่วนตัวนอกชั้นเรียน
กฎนี้ไม่เป็นที่รู้จักเสมอและเป็นการประมาณที่ดีของคำสำคัญ "เพื่อน" C ++ ฉันคิดว่ามันเป็นการทดแทนที่ดี
ฉันคิดว่าคลาสเพื่อนใน C ++ เป็นเหมือนแนวคิดชั้นในใน Java การใช้คลาสภายในคุณสามารถกำหนดคลาสที่ล้อมรอบได้และคลาสที่ล้อมรอบ ชั้นที่ล้อมรอบมีการเข้าถึงแบบเต็มไปยังสมาชิกของรัฐและเอกชนของชั้นล้อมรอบ ดูลิงค์ต่อไปนี้: http://docs.oracle.com/javase/tutorial/java/javaOO/nested.html
ฉันคิดว่าวิธีการใช้รูปแบบการเข้าถึงของเพื่อนนั้นซับซ้อนเกินไป ฉันต้องเผชิญกับปัญหาเดียวกันและฉันแก้ไขโดยใช้ตัวสร้างสำเนาที่ดีเก่าที่รู้จักจาก C ++ ใน Java:
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;
}
}
ในใบสมัครของคุณคุณสามารถเขียนรหัสต่อไปนี้:
public class MyApp {
private static class ProtectedAccessor extends ProtectedContainer {
protected ProtectedAccessor() {
super();
}
protected PrivateAccessor(ProtectedContainer prot) {
super(prot);
}
public String exposeProtected() {
return iwantAccess;
}
}
}
ข้อดีของวิธีนี้คือมีเพียงแอปพลิเคชันของคุณเท่านั้นที่สามารถเข้าถึงข้อมูลที่ได้รับการป้องกัน มันไม่ใช่การแทนที่คำหลักเพื่อน แต่ฉันคิดว่ามันค่อนข้างเหมาะสมเมื่อคุณเขียนไลบรารี่ที่กำหนดเองและคุณต้องเข้าถึงข้อมูลที่ได้รับการป้องกัน
เมื่อใดก็ตามที่คุณต้องจัดการกับอินสแตนซ์ของ ProtectedContainer คุณสามารถห่อ ProtectedAccessor ที่อยู่รอบ ๆ และเข้าถึงได้
นอกจากนี้ยังทำงานร่วมกับวิธีการป้องกัน คุณกำหนดให้มันได้รับการป้องกันใน API ของคุณ ในภายหลังในใบสมัครของคุณคุณเขียนคลาส wrapper ส่วนตัวและเปิดเผยวิธีการป้องกันเป็นสาธารณะ แค่นั้นแหละ.
ProtectedContainer
สามารถ subclassed นอกแพ็คเกจ!
หากคุณต้องการเข้าถึงวิธีที่ได้รับการป้องกันคุณสามารถสร้างคลาสย่อยของคลาสที่คุณต้องการใช้เพื่อแสดงวิธีการที่คุณต้องการใช้แบบสาธารณะ (หรือภายในเนมสเปซเพื่อให้ปลอดภัยยิ่งขึ้น) และมีอินสแตนซ์ของคลาสนั้นในชั้นเรียนของคุณ (ใช้เป็นพร็อกซี)
เท่าที่วิธีการส่วนตัวมีความกังวล (ฉันคิดว่า) คุณจะโชคไม่ดี
ฉันยอมรับว่าในกรณีส่วนใหญ่คำหลักของเพื่อนนั้นไม่จำเป็น
และในที่สุดถ้าจำเป็นจริงๆมีรูปแบบการเข้าถึงของเพื่อนที่กล่าวถึงในคำตอบอื่น ๆ
ไม่ได้ใช้คำหลักหรือมากกว่านั้น
คุณสามารถ "โกง" โดยใช้การสะท้อนภาพ ฯลฯ แต่ฉันไม่แนะนำ "การโกง"
วิธีการที่ฉันพบในการแก้ปัญหานี้คือการสร้างวัตถุ accessor เช่น:
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()
"อ้างสิทธิ์ความเป็นเจ้าของ" ของ accessor โดยปกติแล้วนี่คือรหัสที่สร้างวัตถุ
Foo bar = new Foo(); //This object is safe to share.
FooAccessor barAccessor = bar.getAccessor(); //This one is not.
นอกจากนี้ยังมีความได้เปรียบกว่าเพื่อนกลไก c ++ 's เพราะมันช่วยให้คุณสามารถ จำกัด การเข้าถึงในต่ออินสแตนซ์ระดับเมื่อเทียบกับต่อระดับระดับ โดยการควบคุมการอ้างอิง accessor คุณสามารถควบคุมการเข้าถึงวัตถุ นอกจากนี้คุณยังสามารถสร้าง accessor หลายตัวและให้การเข้าถึงที่แตกต่างกันไปซึ่งอนุญาตให้ควบคุมอย่างละเอียดว่าโค้ดใดที่สามารถเข้าถึงอะไร:
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();
}
}
สุดท้ายหากคุณต้องการจัดระเบียบให้มากกว่านี้คุณสามารถสร้างออบเจ็กต์อ้างอิงซึ่งรวมทุกอย่างเข้าด้วยกัน สิ่งนี้ช่วยให้คุณสามารถอ้างสิทธิ์ accessors ทั้งหมดด้วยการเรียกใช้เมธอดเดียวรวมถึงเก็บอินสแตนซ์ที่เชื่อมโยงไว้ด้วย เมื่อคุณมีการอ้างอิงคุณสามารถส่ง accessors ออกไปยังรหัสที่ต้องการได้:
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();
}
}
หลังจากการต่อสู้ที่หัวมาก (ไม่ใช่คนดี) นี่เป็นคำตอบสุดท้ายของฉันและฉันชอบมันมาก มันมีความยืดหยุ่นใช้งานง่ายและช่วยให้สามารถควบคุมการเข้าถึงชั้นเรียนได้เป็นอย่างดี (ในที่มีการอ้างอิงเท่านั้นเข้าถึงเป็นประโยชน์อย่างมาก.) หากคุณใช้การป้องกันแทนส่วนตัวสำหรับ accessors / อ้างอิงย่อยชั้นเรียนของ Foo getReference
ยังสามารถกลับมาขยายการอ้างอิงจาก มันไม่ต้องการการสะท้อนใด ๆ ดังนั้นจึงสามารถใช้ในสภาพแวดล้อมใด ๆ
ในฐานะของ Java 9 โมดูลสามารถใช้เพื่อทำให้สิ่งนี้ไม่ใช่ปัญหาในหลายกรณี
ฉันชอบการมอบหมายหรือองค์ประกอบหรือคลาสโรงงาน (ขึ้นอยู่กับปัญหาที่ทำให้เกิดปัญหานี้) เพื่อหลีกเลี่ยงการทำให้เป็นคลาสสาธารณะ
หากเป็นปัญหา "อินเทอร์เฟซ / คลาสการใช้งานในแพ็คเกจที่แตกต่างกัน" ฉันจะใช้คลาสโรงงานสาธารณะที่จะอยู่ในแพคเกจเดียวกันกับแพ็คเกจ impl และป้องกันการเปิดเผยคลาส impl
ถ้าเป็น "ฉันเกลียดที่จะทำให้คลาส / วิธีนี้เป็นแบบสาธารณะเพียงเพื่อให้ฟังก์ชันนี้สำหรับคลาสอื่นในแพ็คเกจอื่น" ปัญหาจากนั้นฉันจะใช้คลาสตัวแทนสาธารณะในแพ็คเกจเดียวกันและแสดงเฉพาะส่วนนั้นของฟังก์ชัน ต้องการโดยคลาส "คนนอก"
การตัดสินใจเหล่านี้บางส่วนได้รับแรงผลักดันจากสถาปัตยกรรมการโหลดคลาสเซิร์ฟเวอร์เป้าหมาย (บันเดิล OSGi, WAR / EAR เป็นต้น) การปรับใช้และแบบแผนการตั้งชื่อแพ็คเกจ ตัวอย่างเช่นโซลูชันที่เสนอด้านบนรูปแบบ 'Friend Accessor' นั้นฉลาดสำหรับแอปพลิเคชัน Java ปกติ ฉันสงสัยว่ามันยากที่จะใช้ใน OSGi หรือไม่เนื่องจากความแตกต่างในสไตล์การโหลดคลาส
ฉันไม่รู้ว่ามันเป็นของใช้ที่ใครก็ได้ แต่ฉันจัดการมันด้วยวิธีต่อไปนี้:
ฉันสร้างส่วนต่อประสาน (AdminRights)
ทุกคลาสที่ควรจะเรียกใช้ฟังก์ชั่นที่กล่าวมานั้นควรใช้งาน AdminRights
จากนั้นฉันก็สร้างฟังก์ชั่น HasAdminRights ดังนี้
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;
}
}
ฉันเคยเห็นวิธีแก้ปัญหาแบบสะท้อนกลับซึ่งทำ "การตรวจสอบเพื่อน" ที่รันไทม์โดยใช้การสะท้อนและตรวจสอบการเรียกสแต็กเพื่อดูว่าการเรียกคลาสนั้นได้รับอนุญาตให้ทำเช่นนั้นหรือไม่ เป็นการตรวจสอบรันไทม์ แต่ก็มีข้อเสียเปรียบที่ชัดเจน