2017-01-03 12 views
1

オブジェクトの作成を特定のクラスのメソッドに限定することはできますか?オブジェクトの作成を特定のクラスのメソッドに限定する方法はありますか?

はexempleの場合:私は、クラスのトランザクションを持っている、と私はそれがAbstractServiceまたはIServiceから継承する任意のクラスのメソッドにオブジェクト作成制限したいと思います:

可シナリオ:

public class ServiceA : AbstractService (or IService) 
{ 
    public void MethodA() 
    { 
     var transaction = new Transaction(); 
    } 

    public void MethodB() 
    { 
     var transaction = new Transaction(); 
    } 
} 

禁止シナリオ:

public class ServiceB 
{ 
    public void MethodA() 
    { 
     var transaction = new Transaction(); // cannot create 
    } 

    public void MethodB() 
    { 
     var transaction = new Transaction(); // cannot create 
    } 
} 

ありアクセス修飾子などのシナリオをマウントすることができますか?

+1

'ServiceA'が' Transaction'と同じアセンブリにあり、 'ServiceB'とは別のアセンブリにある場合、' Transaction''のためのコンストラクタを 'internal'にすることができます。そうでなければ、それを 'private'にしてリフレクションを使ってインスタンスを作成し、他のクラスが例外をスローしてインスタンスを作成することを禁止することができます。私はそれがあなたができる最高だと思う。 –

+2

ソートの答え:いいえ。 – Jamiec

+1

これをどのように実装すればよいかをエレガントに考えることはできません。汚れたハックは実際に 'Transaction'のコンストラクタの' StackFrame'をチェックすることでしょう。しかし実際にはここでは間違ったデザインの決定をしていると思いますが、実際にあなたが達成しようとしていることを知らないと決定できません。なぜあなたはそれをしたいのですか?そして、 'ServiceA.MethodA()'が 'ServiceB.MethodA'を呼び出して' Transaction'を生成すると合法でしょうか?または、「ServiceA」からの「直接」コールのみですか?コンパイラ最適化後の "直接"の意味 –

答えて

2

このシナリオをマウントできるアクセス修飾子などがありますか?

はい、そのシナリオを「マウント」することができます他ものがありますが、それは私の意見では、非常に少ない報酬では、作業や抽象化の多くのです。これには、具体的な型ではなく、トランザクションのインタフェースを返す必要があります。 (私は確かにこの作品が、私はそれをコンパイルしていないと確信しています)。サイドノートとして

public abstract class AbstractService 
{ 
} 

public interface IService 
{ 
} 

public interface ITransaction 
{ 
} 


public static class TransactionFactory 
{ 
    // created them as extensions, but you could remove *this* 
    public static ITransaction CreateTransaction(this AbstractService instance) 
    { 
     return new Transaction(); 
    } 

    public static ITransaction CreateTransaction(this IService instance) 
    { 
     return new Transaction(); 
    } 

    private class Transaction : ITransaction 
    { 
     public Transaction() 
     { 
     } 
    } 
} 

それはメソッドのパラメータの追加チェックを行うのがベストだろうので、誰かが技術的には、ヌルに渡すことができます(ただし、その代わりに、コンパイル時の問題の実行時の問題になります) 。

あなたはたぶん私はあなたが何をしようとして誤解だ二つ目は

+1

少なくとも、ファクトリメソッド 'CreateTransaction'を呼び出す必要があります。そうでなければ、サービスを作成しているように見えます(' var transaction = myServiceInstance.Create() 'が奇妙に見えます) – Jamiec

+0

ExtensionMethodとして使用されているかどうかによって異なります。同意する。 –

2

短い答えはいいえ、「このオブジェクトは特定のインタフェースを実装するクラスからしか構築できません」というアクセス修飾子はありません。

あなたはこの制限を乗り切ることができますが、それはクリーンで安全です。

public class Transaction 
{ 
    private Transaction(){} // private important! 

    public static Transaction Create(object creator) 
    { 
     if(creator is IService) 
      return new Transaction(); 

     throw new InvalidOperationException(); 
    } 
} 

public class ServiceA : IService 
{ 
    public void MethodA() 
    { 
     var transaction = Transaction.Create(this); // works 
    } 
} 


public class ServiceB 
{ 
    public void MethodA() 
    { 
     var transaction = Transaction.Create(this); // fails 
    } 
} 

これは簡単に回避できます。私はあなたがXY Problemを持っていると思うとこれを解決する方法だったと思います。

+3

確かにXYの問題。 –

+0

解決策の問題は次のとおりです。クラス・コントローラーを想像してください。クラス・コントローラーは、その中にサービスを作成し、作成したサービスを同じメソッド内でトランザクションに渡すことができます。しかし、トランザクションは特定のサービスのメソッドの内部でしか作成できないため(継承されたクラス、インタフェースまたは他の方法で制限されるため)、トランザクションも禁止されています。 –

+0

@ErikPhilips私は同意します。時には、自分が持っていると思う問題を解決するためにコードがどれくらい悪いことが必要かを示すのに役立ちます。 – Jamiec

2

を動作するかどう

public interface ITransactionFactory { } 

public abstract class AbstractService : ITransactionFactory { } 

public interface IService : ITransactionFactory { } 

public static class TransactionFactory<T> 
    where T : ITransactionFactory 
{ 
    public static ITransaction CreateTransaction(this T instance) 
    { 
     return new Transaction(); 
    } 
    // .... 

かなりわからない...私はあなたが行うことができると思うのチェック時間をコンパイルしたい場合は、これはトリックを行う必要がありますように、そうです:

public abstract class AbstractService : IService 
{ 
    protected class Transaction 
    { 

    } 
} 

public class ServiceA : AbstractService 
{ 
    public void MethodA() 
    { 
     var transaction = new Transaction(); 
    } 

    public void MethodB() 
    { 
     var transaction = new Transaction(); 
    } 
} 

public class ServiceB 
{ 
    public void MethodA() 
    { 
     var transaction = new Transaction(); // cannot create 
    } 

    public void MethodB() 
    { 
     var transaction = new Transaction(); // cannot create 
    } 
} 

internal interface IService 
{ 
} 

あなたは誰にもTransactionを使用できるようにしたい場合、あなたはそれはいくつかのパブリックインターフェイスまたはinheを実装持っている必要がありますそれを別のパブリッククラスから削除することはできますが、他の誰もトランザクションオブジェクトを作成できないようにすることができます。

+0

私はこの解決策が好きです。 – Jamiec

+0

私は、インタフェースから派生したオブジェクトがトランザクションを作成することを許可しません。 –

+0

「インターフェイスから派生した」とはどういう意味ですか? AbstractServiceがIServiceインターフェイスの実装として宣言すると、すべての子サービスが同様にIServiceを実装する必要があるため、効果的に彼が望むものを得ることができます。また、IServiceを内部として宣言することで、外部ユーザーが独自のIServiceを実装できないようにすることもできます。 –