3
\$\begingroup\$

In my code I have a base type which is OnlinePaymentTransaction:

public abstract class OnlinePaymentTransaction
{
     public abstract void Complete( PaymentGatewayCallbackArgs args );
}

The problem I am having is that each class that inherits from this base class require different dependency's in the complete method. Currently I have just added the dependency's as extra parameters to the complete method which doesn't seem right. For example my base class is now like this.

public abstract class OnlinePaymentTransaction
{
     public abstract void Complete( Dependency1 dep1, Dependency2 dep2, PaymentGatewayCallbackArgs args );
}

I cannot inject the dependency's in the constructor as the OnlinePaymentTransactions are retrieved from using nhibernate.

What would you recommend because I don't like using ServiceLocator as it hides the dependency and also makes it harder to test. An suggestions would be greatly appreciated.

| improve this question | | | | |
\$\endgroup\$
  • 1
    \$\begingroup\$ What does complete do that means its a method on the model? ie. Why isn't there a separate logic object that makes the decision/s on how the model has changed? \$\endgroup\$ – Graeme Bradbury Feb 26 '14 at 11:52
  • \$\begingroup\$ I am trying to follow the TellDontAsk coding practice and the OnlinePaymentTransaction is the only object that knows how to complete the transaction. \$\endgroup\$ – Jake Rote Feb 26 '14 at 20:20
5
\$\begingroup\$

I cannot inject the dependency's in the constructor as the OnlinePaymentTransactions are retrieved from using nhibernate.

There's your problem. Your OnlinePaymentTransaction class is/should-be a POCO whose job is to convey data. The Complete() method doesn't belong on that type, it's breaking SRP and making your life much harder than it needs to be.

I'd suggest to introduce another type, call it OnlinePaymentTransactionProcessor or whatever - that type will take the dependencies in its constructor, and have a Complete method that takes an OnlinePaymentTransaction instance.

Kudos for striving to avoid a Service Locator :)

| improve this answer | | | | |
\$\endgroup\$
  • \$\begingroup\$ I wad thinking of creating a processor but was just thinking that would need some kind of OnlinePaymentTransactionProcessorProvider. If i didnt have a provider how would I get a processor? \$\endgroup\$ – Jake Rote Feb 26 '14 at 20:27
  • \$\begingroup\$ Where Provider is an abstract factory? There's not a lot of code in your post, to see how the pieces would fit together. My answer essentially boils down to GraemeBradbury's comment: you're turning your model into something more than just a model - taking that functionality out into its own type should make it easier to figure out :) \$\endgroup\$ – Mathieu Guindon Feb 26 '14 at 20:31
  • \$\begingroup\$ Does this not break the TellDontAsk code practice as you have to ask the OnlinePaymentTransaction how to get the information to complete the online payment transaction. \$\endgroup\$ – Jake Rote Feb 27 '14 at 10:04
  • \$\begingroup\$ @JakeRote the reason it doesn't break the practice is that you're not asking anything. You tell the repo to get data X. You then tell the factory to create a transaction Y from data X. You then tell the transaction Y to complete using CallbackArgs Z. \$\endgroup\$ – Graeme Bradbury Feb 27 '14 at 13:51
3
\$\begingroup\$

Darn, at first glance this looked like a simple solution but you ruled out constructor injection.

The only suggestion I can come up with is to have a class that inherits from the same interface, DOES have the constructor injection is built up and added to the main object as a composite object fed in through a set only Property, maybe some kind of flagged function call like

Init(TheThingWithThePropertiesInAConstructorInitializer);

Then in your Complete function you could throw an exception if HasBeenInitialized is false.

You would then have access to your composite different parameters in your complete method while keeping your abstraction,

albeit with the caveat that your initialization is now a two stage process, that requires an initialization call;

*ps, no I do not propose that to be an actual argument name!

| improve this answer | | | | |
\$\endgroup\$
  • \$\begingroup\$ Do you think there is any other way than this? \$\endgroup\$ – Jake Rote Feb 26 '14 at 20:14
  • 1
    \$\begingroup\$ @JakeRote can't think of any. MAt's Mug is right up above.The problem stems from the overall design. your dependency injector should be injecting implementations of your creators and factories not your direct objects. That would leave your object free to use their constructor params and be initialized correctly. The above method is the only one I can think of that will keep your base design but yeah, it really should be more IOC with TransactionFactories being what is injected IMO. \$\endgroup\$ – apieceoffruit Feb 27 '14 at 11:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.