Checking the callers of a method is a quite common request from inexperienced programmers. I'm surprised that it doesn't appear more often on SO. But it's a really incredibly bad idea (just check out the Java 2 (and perhaps worse earlier) Security Model).
There are few circumstances where it's important to implement this restriction. As Oli Charlesworth say, just don't do it. But let's assume that it is important, but we're not going to do stack inspection.
Let's start by assuming we trust the group. A working but nonsensical approach is to pass to the group an object standing in for the person that only the person can create. (Note Java language access restrictions are class-based. A different instance could create such a stand in, but the code would have to be within the Person
class.)
public final class Group { // Can't have a malicious subclass.
public void deregisterPerson(Person.Standin standin) {
Person person = standin.person();
...
}
}
public class Person { // May be subclassed.
public final class Standin { // Could be one per Person.
private Standin() { // Hide constructor from other outer classes.
}
public Person person() {
return Person.this;
}
}
private void groupDeregister(Group group) {
group.deregisterPerson(new Standin());
}
}
If we don't trust the group, then we could extend the stand-in to reference the group. This prevents a malicious group deregistering a person from other groups.
public class Group { // Can have malicious subclasses.
public void deregisterPerson(Person.GroupDeregister deregister) {
if (deregister.group() != this) { // Not equals...
throw new IllegalArgumentException();
}
Person person = deregister.person();
...
}
}
public class Person { // May be subclassed.
public final class GroupDeregister {
private final Group group;
private GroupDeregister(Group group) { // Hidden.
this.group = group;
}
public Person person() {
return Person.this;
}
public Group group() {
return group;
}
}
private void groupDeregister(Group group) {
group.deregisterPerson(new GroupDeregister(group));
}
}
Another approach is to make a "public" version of Person that can be exposed to others.
public class Person { // "PrivatePerson"
public PublicPerson publicPerson() {
return new PublicPerson(this);
}
private void groupRegister(Group group) {
group.registerPerson(this);
}
private void groupDeregister(Group group) {
group.deregisterPerson(this);
}
...
}
public class PublicPerson {
private final Person person;
public PublicPerson(Person person) {
this.person = person;
}
@Override public final boolean equals(Object obj) {
return obj instanceof Person && (Person)obj.person == person;
}
@Override public final int hashCode() {
return person.hashCode();
}
...methods, but no raw registration...
}
public class Group {
private final Set<Person> members = new IdentityHashSet<>(); // No Object.equals.
public void registerPerson(Person person) {
members.add(person);
}
public void deregisterPerson(Person person) {
members.remove(person);
}
public Set<PublicPerson> members() {
// This will be more concise in Java SE 8.
Set<PublicPerson> publics = new HashSet<>();
for (Member member : members) {
publics.add(member.publicPerson());
}
return unmodifiableSet(publics);
}
}
(Objects.requireNonNull
left out for "brevity".)
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…