3

考虑以下代码

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是一个包装样式类,调用者必须使用它来获取SqlInstanceFileInstance的实例。这里的问题是调用者可以直接调用类的GetInstance方法,而不是通过 DataManger 类。我们如何解决这个问题?具体来说,是否存在强制调用者通过DataManager的模式或机制?是否可以使两个Instance类仅对DataManager类“可见” 。

我知道将两个类设为DataManager类的内部类将有助于解决问题,但我想知道是否还有其他“更好”的方法可以做到这一点?

PS:请忽略类名和实现。这只是一个示例,并非来自任何现实生活中的代码。

语言是 C#

4

4 回答 4

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 让我朝着正确的方向思考。

于 2013-05-09T11:27:03.360 回答
0

我的 C# 生锈了,但是如果调用者代码在不同的程序集文件中(也就是说,您的代码是在调用者代码中导入的),您可能可以尝试使用internal修饰符。

编辑:找到一个更合适的:InternalVisibleTo

http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.internalsvisibletoattribute.aspx

于 2013-05-09T06:37:27.850 回答
0

由于调用者必须能够看到返回对象的类型,因此仅在此版本的代码上使用 internal 是行不通的,如另一个答案中所述。

我们可以通过创建 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
        }
    }

显然,任何依赖于具体实现的调用代码都可能在此时停止工作。

让我知道它是否有效,顺便说一句:D

于 2013-05-09T09:23:10.367 回答
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");
    }
}
于 2013-05-09T08:03:22.920 回答