2012-01-16 11 views
2

私は自分のサイトの支払いシステムを作成しています。ユーザーは、支払う複数の支払いプロバイダの1つを選択できますが、すべて同じ方法で動作する必要があります。私はこのように、この動作を表現するために考えた:抽象クラスのみに継承者を知らせる

public abstract class PaymentProvider { 
    private static var methods = Dictionary<String,PaymentProvider> 
    { 
     {"paypal",new PaymentProviderPaypal()}, 
     {"worldpay",new PaymentProviderWorldpay()} 
    } 

    public static Dictionary<String,PaymentProvider> AllPaymentProviders 
    { 
     get {return methods;} 
    } 

    public abstract pay(); 
} 

public class PaymentProviderPaypal : PaymentProvider { 
    public override pay() { 

    } 
} 

public class PaymentProviderWorldpay : PaymentProvider { 
    public override pay() { 

    } 
} 

あなたはPaymentProvider.AllPaymentProviders["key"].pay()を書くことで、これを使うことになっています。このクラスを使用する関数は、基本的な支払いプロバイダがどのように実装されているかを知る必要はなく、単にキーを知る必要があるという考えがあります。

ただし、PaymentProviderクラスにアクセスすると、継承クラスにアクセスすることもできます。継承クラスの新しいコピーをインスタンス化し、それらを予期しない方法で使用することが可能です。継承クラスをカプセル化して、抽象的なPaymentProviderだけがそれらについて知っているようにしたいと思います。

どうすればよいですか? protectedのような異なる保護レベルはここでは機能しません - Javaでは、protectedは、名前空間の他のクラスだけがそのクラスを使用できることを意味しますが、C#では何か他のものを意味します。

私はここに適切なアイデアはありますか?あるいは私は別の方法を使うべきですか?

+0

ない質問への答え:たとえば、.NETで '現在のアセンブリ(すべての名前空間)ですべてのものへのアクセスを提供しますinternal'、' protected'は相続にのみアクセス可能です。 – Jamiec

+0

タイプを公開する上で 'AllPaymentProviders'に渡すために使用するキーを知っているユーザーに頼らなければならないという追加の利点は何ですか? –

+0

@Jamiec私はMSDNでそれを見ました。しかし、このサイトはビジュアルスタジオの下にある「ウェブサイト」プロジェクトであり、アセンブリを使ったものであるため、予期せぬことが起こった場合は「内部」を使用したくありませんでした。 – Oliver

答えて

3

心にオプションの春のカップル:

  • クライアントコードから別のアセンブリでこれを入れて、そして
  • 実装は、抽象的にするには、専用のネストされたクラスとしてPaymentProviderクラスの内部実装を入れてください。あなたはまだPaymentProvider部分クラスを作ることによって、ソースコードを分離することができます - 実装ごとに1つのソースファイルを使用し

あなたがの面で実装からクライアントを分離する気にならないのであれば、最初のオプションはクリーンである可能性が高いですアセンブリ。

Jamiecの回答で提案された変更後も、これらの両方が有効なオプションであることに注意してください。 - 「可視性」の部分は継承部分とやや直交しています。

(余談として、私はこの方法が本当にむしろpay()よりPay()と呼ばれている願っています:)

+0

答えをありがとう。私はあなたの2番目の提案に行くと思います。現時点でのサイトの構造は、決済コードを別のアセンブリに入れることにはなりません(最終的に解決する予定のもの) – Oliver

2

あなたの継承heirachyが少しグラグラですが、私はそれに似ていますが、決定的に異なる方法を行うように誘惑されるだろう。

public interface IPaymentProvider 
{ 
    void Pay() 
} 

// Implementations of IPaymentProvider for PaypalPaymentProvider & WorldpayPaymentProvider 

public static class PaymentHelper 
{ 
    private static var providers = Dictionary<String,IPaymentProvider> 
    { 
     {"paypal",new PaymentProviderPaypal()}, 
     {"worldpay",new PaymentProviderWorldpay()} 
    } 


    public static void Pay(string provider) 
    { 
     if(!providers.Containskey(provider)) 
      throw new InvalidOperationException("Invalid provider: " + provider); 

     providers[provider].Pay(); 
    } 

} 

次に、使用方法はPaymentHelper.Pay("paypal")のようになります。

Payメソッドに提供するデータがさらにある場合は、これをインターフェイスとヘルパーの両方に追加できます。

public interface IPaymentProvider 
{ 
    void Pay(double amount); 
} 

public static void Pay(string provider, double amount) 
{ 
    if(!providers.Containskey(provider)) 
     throw new InvalidOperationException("Invalid provider: " + provider); 

    providers[provider].Pay(amount); 
} 
+0

抽象クラスではなくインターフェイスを使用する方が良い理由は何ですか?それはそれをより保守性のある/より速くしますか? – Oliver

+0

違いはありません。抽象クラスを使用する唯一の理由は、基本的な実装がある場合です。デモンストレーションの目的でインターフェイスを使用しました。 – Jamiec

関連する問題