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

在Java中,如何实现一个类对另外两个类的单独访问?,java,Java,可能的重复项: C++中有一个“朋友”的概念,它可以访问一个类的私有变量和函数。因此,如果你有: class Foo { friend class Bar; private: int x; } 然后,类Bar的任何实例都可以修改任何Foo实例的x成员,尽管它是私有的,因为Bar是Foo的朋友 现在,我在Java中遇到了这样一种情况:这个功能会派上用场,但当然,它在Java中并不存在 有三个类:数据库、修改器、查看器。数据库只是变量的集合(如结构)。修改器应该是数据库的“朋

可能的重复项:

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

class Foo {
    friend class Bar;
private:
    int x;
}
然后,类Bar的任何实例都可以修改任何Foo实例的
x
成员,尽管它是私有的,因为Bar是Foo的朋友

现在,我在Java中遇到了这样一种情况:这个功能会派上用场,但当然,它在Java中并不存在

有三个类:数据库、修改器、查看器。数据库只是变量的集合(如结构)。修改器应该是数据库的“朋友”;也就是说,它应该能够直接读写它的变量。但查看器应该只能读取数据库的变量


如何最好地实现这一点?有没有一种好方法可以强制查看器对数据库进行只读访问?

我认为这不是实现这种功能的好方法

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

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

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


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

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

如果你需要区分谁能读谁能写,你可以把接口加入其中。定义一个只包含getter的接口,您可以将查看器限制为只读


Robert Harvey添加了一条评论,指出了其他选项,例如对类或包级别的访问使用不同的访问修饰符。

内部类可以访问其容器类的所有私有成员。将getter设置为公共的,setter设置为私有的,将modifier设置为数据库的内部类。也使它只能通过工厂方法模式从数据库内部创建。可能也需要单身

一个包本地类与C++中的“朋友”差不多。将所有getter公开,将setter包本地化。使修改器与数据库位于同一个包中。第一个例子是更干净


另一个适用的习语是Memento模式

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

您可以创建一个嵌套类

假设您有这样的ViewClass:

class Viewer {
    Database db;
    Viewer( Database db ){
        this.db = db;
    }
    public void whatIsX(){
        System.out.println( db.x() );
    }
}
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();
    }

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

    public DatabaseWriteAccess getWriteAccess(Modifier modifier) { ... }
}
并使用修改该数据库的方法定义一个修饰符

abstract class Modifier {
    public void manipulate();
}
您可以使用嵌套类创建数据库,该类可以访问私有成员,与friend非常相似

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;
    Viewer( Database db ){
        this.db = db;
    }
    public void whatIsX(){
        System.out.println( db.x() );
    }
}
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();
    }

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

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

如果需要这种访问,请创建一个类接口,该接口封装了只允许少数几个方法调用的方法。然后创建一个方法,仅当请求类满足您强加的任何条件时,该类才会传递接口的私有实例

一个简单的例子,写得很刻薄:

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

}
更复杂的示例是可能的,但这改进了friend访问模式,因为即使是“潜在”friend类的单个实例也可以被单独挑选出来进行访问(或拒绝)。例如,在本例中,通过重写getWalletAccess(…)来检查妻子是否真的与这个特定的丈夫结婚是很简单的,如下所示:


我想说,接口绝对是解决您问题的方法

首先,不允许任何其他内容访问
数据库的实际字段。创建每个返回其一个字段的方法。然后,使
DatabaseView
成为一个接口,该接口声明了这些方法中的每一种,以供读取。然后让
数据库
实现
数据库视图
。最后,您可以添加写入(设置)应设置为
数据库的字段的方法


当您有一些类需要能够从数据库中读取时,例如您的
查看器
,让它使用
数据库视图
。然后可以传入
数据库
实例本身,但该类不知道写入该实例的方法。如果还需要读写其他内容,例如
修饰符
,则只需将
数据库
本身交给它即可。更好的是,
数据库
本身可以是一个扩展
数据库视图
的接口,而您的实际实现对象可以是一些不需要知道的类。

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

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

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

2.)将关注点分离为完全不同的项目。然后您可以设置项目依赖项,只有修改器才能访问数据库。这意味着您在某种程度上改变了设计,任一数据库都会变成两个项目(一个具有公共只读接口,另一个具有完全访问接口),或完全删除查看器和数据库之间的依赖关系,并使查看器仅访问数据库