Oliver
Oliver

Reputation: 11617

Letting only the abstract class know about its inheritors

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

Answers (2)

Jamiec
Jamiec

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

Jon Skeet
Jon Skeet

Reputation: 1502806

A couple of options spring to mind:

  • Put this in a separate assembly from the client code, and make the implementations abstract
  • Put the implementations inside the PaymentProvider class as private nested classes. You can still separate the source code by making PaymentProvider a partial class - use one source file per implementation

The 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

Related Questions