From procedural Function to OOP Class (one specific situation)

423 Views Asked by At

Still learning OOP, and trying to change my perspective from procedural way of life. I've found a lot of advantages when refactoring, but now I am stuck in a paradigm:

I am refactoring a shopping cart. When it goes to checkout, in my old php scripts I had a function in my functions file that takes the user to the different payment methods (PayPal, cards, money transfers, etc), each of them with different acctions, values, functions, url, etc.

So, can I see a "payment method" as a Class?

In my old scripts the checkout was lineal, action after action, but now I want to reuse the payment Class for reservations, deferred payments, subscriptions, and so on, not following the clicks of a human buyer. I think this is when OOP shines, isn´t it?

I think the Class can have Attributes like total, subtotal, discount, tpv selection, result, error messages... but... some of them are already part of my Basket class.

And how can use it? calling its functions from outside and sending a lot of parameters such as credit card requirements? or forcing the Class to obtain those values outside, in the preferences file?

A different class for each payment method, or a big class with all of them...

Really I can't see the paradigm... but I am almost sure it's there :-)

2

There are 2 best solutions below

0
On BEST ANSWER

The question is vague at parts, so some of this answer is likewise necessarily vague.

So, can I see a "payment method" as a Class?

In pure OOP, everything is a class or a method. A Payment Method class would make sense, especially as there are distinct payment methods.

I think the Class can have Attributes like total, subtotal, discount, tpv selection, result, error messages... but... some of them are already part of my Basket class.

Boil payment methods down to their essential constituents and behaviors. Ask yourself questions like: are monetary quantities really a part of how someone pays, or are they a part of what is being paid? (Answer to this specific question: they are part of a payment, not a payment method) When a payment is made, payment info will be needed, but it can be passed to whichever methods need it (perhaps in a Payment object, which represents a monetary amount sent from a buyer to a seller, or in an Order object, or an Order object could implement a Payment interface, though the last wouldn't model the real world injectively).

And how can use it? calling its functions from outside and sending a lot of parameters such as credit card requirements? or forcing the Class to obtain those values outside, in the preferences file?

If payment requirements are part of payment methods, they should be properties of payment methods. As for how payment method objects are initialized, the dependency injection technique (where a separate class is responsible for determining which payment method, among other specific classes, should be involved in a transaction and set them up) could be applied, though it isn't always the best choice. You could also have a controller that responds to an action from the user (i.e. selecting a payment method) by reading the configuration data and creating the payment method object, passing said data to the payment method.

There are a couple valid approaches to using a payment method object. Any time an action involves multiple objects and one isn't a clear actor (i.e. the one that should perform the action), you could use a free function (i.e. a non-method function) that calls the necessary methods of the appropriate objects. If behavior of the function varies depending on the run-time types of multiple objects, you'd use multi-methods (if possible; not many languages support multi-methods natively, though you can simulate it in many languages with certain patterns, such as double dispatch). Other possibilities include having a controller as the primary actor.

One concept that can help when implementing designs are the access rules made explicit in capabilities programming: objects can access other objects that they create, are created knowing (i.e. passed to their constructor) or are introduced to. These rules inform techniques such as dependency injection, Model-View-Controller (MVC) and so on.

A different class for each payment method, or a big class with all of them...

Somewhere in between. Anything that is common to all payment methods (such as an action URL) should go in a base class. If a payment method needs to override the common behavior or needs a field that other methods don't, subclass the base payment method. For consistency's sake, you could extend the base class for all payment methods, though some subclasses might not override anything.

0
On

Based on what you're describing, an OOP architecture that I think would fit would be along these lines

  • ShoppingCart - Stores Purchases and calculates total cost/refunds/tax/etc.
  • Purchase - Stores what is being purchased and the cost.
  • PaymentMethod - An interface for payment methods that takes a shopping cart and enacts the purchases.
  • PayPalPaymentMethod - A derived class of PaymentMethod that pays purchases through the PayPal service.
  • CreditCardPaymentMethod - A derived class of PaymentMethod that pays purchases through a credit card.

The ShoppingCart really only needs needs to know about the Purchases it contains and a set of PaymentMethods available to it. You could require each PaymentMethod to have a name and logo to be displayed when selecting payment methods on the checkout page. You could also require each PaymentMethod to have a method that displays the GUI necessary to provide it whatever information it requires in order to process the set of Purchases.

I'm writing this based on how I'd architect it, as a primarily C++ game programmer though, so you may have to adapt some of my terminology and methodologies for web development.

Don't consider your procedural way of thinking as a problem, just recognize that certain problems are best solved with different paradigms. In this case, I think the object-oriented paradigm is well suited for this kind of problem. Not everything has to be OOP, not everything has to be procedural though.