Reputation: 11617
I am making a payment system for my site. Users can select one of several payment providers to pay, but all should behave in the same way. I thought to represent this behavior like this:
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() {
}
}
You are supposed to use this by writing PaymentProvider.AllPaymentProviders["key"].pay()
. The idea is that the functions using this class don't need to know about how the underlying payment provider is implemented, they just need to know the key.
However, at the moment, if you have access to the PaymentProvider
class, you also have access to the inheriting classes. Its possible to instantiate a new copy of the inheriting classes, and make use of them in an unexpected way. I want to encapsulate the inheriting classes so that only the abstract PaymentProvider
knows about them.
How should I do this? Different protection levels like protected
don't work here - In Java, protected
means that only other classes in the namespace can use that class, but in C# it means something else.
Do I have the right idea here? Or should I use a different method?
Upvotes: 2
Views: 291
Reputation: 136154
Your inheritance heirachy is a bit wonky, I would be tempted to do it a similar but crucially different way.
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();
}
}
Then the usage would be something like PaymentHelper.Pay("paypal")
.
Obviously if there is more data to provide to the Pay
method this can be added to both the interface, and the helper. for example:
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);
}
Upvotes: 2
Reputation: 1502806
A couple of options spring to mind:
PaymentProvider
class as private nested classes. You can still separate the source code by making PaymentProvider
a partial class - use one source file per implementationThe first option is likely to be the cleanest if you don't mind separating the clients from the implementation in terms of assemblies.
Note that both of these are still valid options after the change proposed by Jamiec's answer - the "visibility" part is somewhat orthogonal to the inheritance part.
(As an aside, I hope the method is really called Pay()
rather than pay()
:)
Upvotes: 3