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は、呼び出し元がSqlInstanceまたはFileInstanceのインスタンスを取得するために使用する必要があるラッパー スタイル クラスです。ここでの問題は、DataManger クラスを経由する代わりに、呼び出し元がクラスの GetInstance メソッドを直接呼び出すことができることです。この問題をどのように解決しますか?具体的には、呼び出し元がDataManagerを通過することを強制するパターンまたはメカニズムはありますか? 2 つのインスタンスクラスをDataManagerクラスだけに「見える」ようにすることは可能ですか。

2 つのクラスを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
            }


        }

これで、呼び出し元は GetInstance を除く SqlInstance のすべてのメソッドを呼び出すことができ、誰も SqlInstance で GetInstance を直接呼び出すことはありません!

これにより、別の予期しない問題も解決されました。以前は、返された SqlInstance がそれ自体でさらに GetInstance を呼び出し、ファクトリの目的全体を無効にする可能性がありました。

正しい方向に考えさせてくれた Dek Dekku に感謝します。

于 2013-05-09T11:27:03.360 に答える
0

呼び出し元は返されたオブジェクトの型を認識できる必要があるため、他の回答で述べたように、このバージョンのコードで internal を使用するだけでは機能しません。

これは、SqlInstance と FileInstance が派生するインターフェイスまたは抽象クラスを作成することで解決できます (これはおそらく、後者が望ましい数少ないケースの 1 つです)。次に、これらの具体的な実装を呼び出し元から隠すことができます。

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 に答える