考虑以下代码
class SqlInstance
{
private SqlInstance()
{
}
public void Connect(string username, string password)
{
//connect
}
public void Disconnect()
{
//disconnect
}
//This method is not a singleton. Its one instance per key
public static SqlInstance GetInstance(string key)
{
return new SqlInstance();
}
}
class FileInstance
{
private FileInstance()
{
}
//no this is not a mistake. This connect has no parameters
private void Connect()
{
//connect
}
public void Disconnect()
{
//disconnect
}
//This method is not a singleton. Its one instance per key
public static FileInstance GetInstance(string key)
{
return new FileInstance();
}
}
class DataManager
{
SqlInstance GetSqlChannelInstance()
{
//if some logic
return SqlInstance.GetInstance("dev.1");
//if some other logic
return SqlInstance.GetInstance("dev.2");
//...and so on
}
FileInstance GetFileInstance()
{
//if some logic
return FileInstance.GetInstance("fil.1");
//if some other logic
return FileInstance.GetInstance("fil.2");
//...and so on
}
}
DataManager 是一个包装样式类,调用者必须使用它来获取 SqlInstance 或 FileInstance 的实例。这里的问题是调用者可以直接调用类的 GetInstance 方法,而不是通过DataManger类。我们如何解决这个问题?具体来说,是否存在强制调用者通过 DataManager 的模式或机制?是否可以将两个实例类'可见'仅限于 DataManager 类。
我知道制作 DataManager 类的两个类内部类将有助于解决问题,但我想知道是否还有其他'更好的'方法此?
PS:请忽略类名和实现。这仅是一个示例,不能从任何现实代码中获取。
语言是C#
答案 0 :(得分:1)
class SqlInstanceManager:SqlInstance
{
private SqlInstanceManager(){ }
public static new GetInstance()
{
return SqlInstance.GetInstance("key");
}
}
class SqlInstance
{
protected SqlInstance()
{
}
public void Connect(string username, string password)
{
//connect
}
public void Disconnect()
{
//disconnect
}
//Make this protected. Now this class cannot be instantiated
//and it cannot be called without inheriting this class
//which is sufficient restriction.
protected static SqlInstance GetInstance(string key)
{
return new SqlInstance();
}
}
//And the same thing for FileInstance
class DataManager
{
SqlInstance GetSqlChannelInstance()
{
//if some logic
return SqlInstanceManager.GetInstance("dev.1");
//if some other logic
return SqlInstanceManager.GetInstance("dev.2");
//...and so on
}
}
现在调用者可以在SqlInstance上调用除GetInstance之外的所有方法,并且没有人直接在SqlInstance上调用GetInstance!
这也解决了另一个意想不到的问题:以前返回的SqlInstance可以进一步调用GetInstance来破坏工厂的整个目的!
感谢Dek Dekku让我思考正确的方向。
答案 1 :(得分:0)
我的C#生锈了,但如果调用者代码位于不同的程序集文件中(即,您的代码是在调用者代码中导入的),您可以尝试使用internal
修饰符。
编辑:找到更合适的一个: InternalVisibleTo
答案 2 :(得分:0)
嵌套SqlInstance和FileInstance的替代方法 - GetInstance对其他类型可见(但它们的签名暗示DataManager与它们相关联)并且只有DataManager且同一程序集中没有其他类型将能够获取FileInstance和SqlInstance的实例,只要DataManager不暴露Token。
public class SqlInstance
{
private SqlInstance() {}
internal static SqlInstance GetInstance(DataManager.Token friendshipToken, string key)
{
if (friendshipToken == null)
throw new ArgumentNullException("friendshipToken");
return new SqlInstance();
}
}
public class FileInstance
{
private FileInstance() {}
internal static FileInstance GetInstance(DataManager.Token friendshipToken, string key)
{
if (friendshipToken == null)
throw new ArgumentNullException("friendshipToken");
return new FileInstance();
}
}
public class DataManager
{
private static Token token;
static DataManager()
{
Token.SetToken();
}
public class Token
{
private Token() {}
public static void SetToken()
{
token = new Token();
}
}
public SqlInstance GetSqlChannelInstance()
{
return SqlInstance.GetInstance(token, "dev.1");
}
public FileInstance GetFileInstance()
{
return FileInstance.GetInstance(token, "fil.1");
}
}
答案 3 :(得分:0)
由于调用者必须能够看到返回对象的类型,因此只需在此版本的代码上使用internal就行了,如另一个答案中所述。
我们可以解决这个问题,创建一个接口或一个abstact类(这可能是少数几个可能更好的情况之一),SqlInstance和FileInstance从这个类中继承。然后我们可以隐藏调用者的这些具体实现。
interface AbstractInstance {
// Some stuff here
}
internal class SqlInstance : AbstractInstance {
// Ideally nothing changes here
}
internal class FileInstance : AbstractInstance {
// Ideally nothing changes here
}
使用internal
,具体类的可见性仅限于它们所在的程序集的范围,而接口可以全局使用。
现在我们只需要更改Factory,使其返回值依赖于抽象而不是实现
class DataManager
{
AbstractInstance GetSqlChannelInstance()
{
//if some logic
return SqlInstance.GetInstance("dev.1");
//if some other logic
return SqlInstance.GetInstance("dev.2");
//...and so on
}
AbstractInstance GetFileInstance()
{
//if some logic
return FileInstance.GetInstance("fil.1");
//if some other logic
return FileInstance.GetInstance("fil.2");
//...and so on
}
}
显然,任何依赖具体实现的调用代码都可能在此时停止工作。
让我知道它是否有效,BTW:D