如何用Java实现另外两个类对一个类的单独访问?

时间:2010-06-08 16:36:42

标签: java

  

可能重复:
  ‘Friends’ equivalent for Java?
  Is there a way to simulate the C++ ‘friend’ concept in Java?

在C ++中有一个“朋友”的概念,它可以访问类的私有变量和函数。所以如果你有:

class Foo {
    friend class Bar;
private:
    int x;
}

然后类Bar的任何实例都可以修改任何Foo实例的x成员,尽管它是私有的,因为Bar是Foo的朋友。

现在我遇到了Java的情况,这个功能会派上用场,但当然它在Java中不存在。

有三个类:Database,Modifier,Viewer。数据库只是变量的集合(如结构)。修饰符应该是数据库的“朋友”;也就是说,它应该能够直接读取和写入其变量。但是Viewer应该只能读取数据库的变量。

如何最好地实施?有没有一种很好的方法来强制查看者对数据库的只读访问权限?

8 个答案:

答案 0 :(得分:2)

关于你应该多么纯洁,你有不同的意见,但对我来说,你不应该让任何人访问任何领域。使用getter和setter。

如果您需要隔离谁可以阅读以及谁可以编写,您可以将界面放入混合中。仅使用getter定义界面,您可以将查看器限制为只读。

罗伯特·哈维(Robert Harvey)添加了一条注释,指出其他选项,例如使用不同的访问修饰符进行类或包级访问。

答案 1 :(得分:1)

我认为这不是实施此类事情的好方法。

您应该有一个隐藏数据库的数据访问层。它应该公开CRUD操作。

从界面开始。创建一个只有finder方法的ReaderDao。然后创建一个扩展ReaderDao的GenericDao并添加save,insert和delete方法。

实现类应根据需要实现一个接口或另一个接口。

任何人都不需要在幕后公开它是关系数据库这一事实。

答案 2 :(得分:1)

内部类可以访问其容器类的所有私有成员。让你的getter公开,你的setter私有,使Modifer成为数据库的内部类。还使它只能通过Factory Method模式从Database内部创建。可能也需要成为单身人士。

包本地类与C ++中的“朋友”差不多。让所有的getter公开,让你的setter包本地化。 Make Modifier与Database位于同一个包中。第一个例子是清洁。

其他成语适用于Memento模式。

答案 3 :(得分:1)

你确定这是一个好模式吗?它直接绕过了封装,这是OO设计的主要原则之一。

答案 4 :(得分:1)

您可以创建一个嵌套类。

想象一下你有这样的ViewClass:

class Viewer {
    Database db;
    Viewer( Database db ){
        this.db = db;
    }
    public void whatIsX(){
        System.out.println( db.x() );
    }
}

使用修改该数据库的方法定义修饰符。

abstract class Modifier {
    public void manipulate();
}

您可以使用嵌套类创建数据库,该类可以访问私有成员,非常像朋友。

class Database {
    private int x;
    class DatabaseModifier extends Modifier {
        public void manipulate(){
            x++;
        }
    }
    public int x(){
        return x;
    }
}

//看看是否有效:

class Main{
    public static void main( String [] args ) {
        Database database = new Database();
        Modifier modifier = database.new DatabaseModifier();
        Viewer  viewer = new Viewer( database );
        viewer.whatIsX();
        modifier.manipulate();
        viewer.whatIsX();
    }

}

您也可以选择静态内部类。它可能是这样的:

class Viewer {
    Database db;
    public Viewer( Database db ){
        this.db = db;
    }
    public void whatIsX(){
        System.out.println( db.x() );
    }
}
class Modifier {
    Database db;
    public Modifier( Database db ){
        this.db = db;
    }
    public void manipulate(){
        //db.x++; doesn't work because db.x is private 
    }
}
class Database {
    private int x;
    static class DatabaseModifier extends Modifier {
        public DatabaseModifier( Database db ){
            super(db);
        }
        @Override 
        public void manipulate(){
            db.x++;
        }
    }
    // public accessor to attribute X
    public int x(){// should be getX() 
        return x;
    }
}

class Main{
    public static void main( String [] args ) {
        Database database = new Database();
        Modifier modifier = new Database.DatabaseModifier( database );
        Viewer  viewer = new Viewer( database );
        viewer.whatIsX();
        modifier.manipulate();
        viewer.whatIsX();
    }

}

答案 5 :(得分:1)

如果您想要这种访问,请创建一个类的接口,该类封装了您打算只允许少数几个人调用的方法。然后创建一个方法,只有当请求类符合您所施加的任何条件时,类才会传递接口的私有实例。

一个简单的例子,用舌头牢牢地写在脸颊上:

public class Husband {

  private Spouse wife;

  private int cashInWallet;

  public Husband(Wife wife) {
    this.cashInWallet = 20;
    this.wife = wife;
  }

  public WalletAccess getWalletAccess(Object other) {
    if (other instanceof Wife) {
      return new WalletAccessImpl(this);
    }
    return null;
  }

  public interface WalletAccess {
    public int withdrawCash(int requested);
  }

  private WalletAccessImpl implements WalletAccess {

    private Husband hubby;

    private WalletAccessImpl(Husband hubby) {
      this.hubby = hubby;
    }

    public int withdrawCach(int requested) {
      if (this.hubby.wallet > requested) {
         this.hubby.wallet -= requested;
         return requested;
      } else {
         int allCash = this.hubby.wallet;
         this.hubby.wallet = 0;
         return allCash;
      }
  }

}

public class Wife {

  private Husband husband;

  public Wife(Husband husband) {
    this.husband = husband;
  }

  public void consumeCash() {
    Husband.WalletAccess access = husband.getWalletAccess(this);
    int cash = access.withdrawCash(20);
  }

}

更复杂的例子是可能的,但这改善了朋友访问模式,因为即使是“可能”朋友类的个别实例也可以被挑出来访问(或拒绝)。例如,在这种情况下通过重写getWalletAccess(...)来检查妻子是否真的与这个特定的丈夫结婚是微不足道的:

public WalletAccess getWalletAccess(Object other) {
  if (other instanceof Wife) {
    Wife someone = (Wife)other;
    if (someone.getHusband() == this) {
      return new WalletAccessImpl(this);
    }
  }
  return null;
}

答案 6 :(得分:1)

我认为接口肯定是解决问题的方法。

首先,请勿允许其他任何内容访问Database的实际字段。创建每个返回其中一个字段的方法。然后,使DatabaseView成为一个接口,声明每个方法的读取。然后让Database实施DatabaseView。最后,您可以添加用于编写(设置)应设置为Database的字段的方法。

如果某个类需要能够从数据库中读取,例如Viewer,则需要DatabaseView。然后,您可以传入Database实例本身,但该类不会知道写入它的方法。如果其他内容需要同时读取和写入,例如您的Modifier,则可以将Database本身提供给它。更好的是,Database本身可以是一个扩展DatabaseView的接口,您的实际实现对象可能是一些无需了解的类。

答案 7 :(得分:0)

你应该重新考虑你真正想要多少分离,这个“修饰符应该是数据库的”朋友“;也就是说,它应该能够直接读取和写入它的变量。”是不可能的Java - 直接字段访问不能有不同的访问权限。

我认为你真正想要的是强烈反对不受欢迎的访问模式。有几种方法可以实现这一目标:

1。)将修改器和数据库放在同一个包中并使“Setters”包受到保护,因此set-methods对于Viewer是不可见的(只要Viewer不在同一个包中)。对于较大的设计,这或多或少是不切实际的。

2.)将问题分成完全不同的项目。然后,您可以设置项目依赖项,只有Modifier才能访问数据库。这意味着您稍微改变了设计,数据库变为两个项目(一个具有public-readonly接口,另一个具有完全访问接口),或者完全删除Viewer和Database之间的依赖关系,并且只允许Viewer通过Modifier访问数据库。该解决方案的优点是在物理上不可能违反访问边界(它不会在构建中编译)。

3.)更接近实际“朋友”类概念的解决方案是拥有两个接口,一个用于读取,一个用于写入,数据库使用内部类实现这些接口。然后,您可以使用以客户端作为参数的getter“保护”对内部类实例的访问:

public class Database {
    public DatabaseReadAccess getReadAccess(Viewer viewer) { ... }

    public DatabaseWriteAccess getWriteAccess(Modifier modifier) { ... }
}

这不会阻止恶意访问,但有些阻止它们。如果你想更进一步,为Viewer和Modifier定义“Token”类,并要求数据库中的访问getter的令牌实例(然后编译器将强制执行限制):

public class ModifierToken {
    ModifierToken(Modifier modifier) {
        // constructor is package protected, so no outsiders can create tokens!
    }
}

我个人会选择“单独的项目”方法,它会使不受欢迎的访问变得明显,并且最近会在构建中弹出违规行为。我自己从未尝试过“令牌”方法。