可能重复:
‘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应该只能读取数据库的变量。
如何最好地实施?有没有一种很好的方法来强制查看者对数据库的只读访问权限?
答案 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!
}
}
我个人会选择“单独的项目”方法,它会使不受欢迎的访问变得明显,并且最近会在构建中弹出违规行为。我自己从未尝试过“令牌”方法。