选择性方法可见性的设计模式

时间:2013-05-09 06:16:04

标签: c# methods visibility

考虑以下代码

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#

4 个答案:

答案 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

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

答案 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