Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LARAVEL: How to use Open Close Principle of SOLID principles?

I have a following structure to use Open Close Principle

class Payment{ 

    //this is not a model class
    // according to OC principle this class should not focus on the implementation

    private $paymentInterface;

    public function __construct(PaymentInterface $paymentInterface)
    {
        $this->paymentInterface = $paymentInterface;
    }


    //so store method does not know which implementation it will get
    public function store($request,$id)
    {
        return $this->paymentInterface->store($request,$id);
    }

}

Interface

interface PaymentInterface{
    public function store($request,$id = null);
}

Payment Service Class containing implementation

class PaymentService implements PaymentInterface{
    public function store($request,$id = null){
        //payment store logic is here
    }
}

Controller

class PaymentsController extends Controller{

    protected $payment;

    public function __construct()
    {
        $this->payment = new Payment(new PaymentService);
    }

    public function storePayment(PaymentRequest $request, $id)
    {
        try {
             $response = $this->payment->store($request,$id);
             return redirect()->route($this->route.'.index')->with($response['status'],$response['message']);
        } catch (\Exception $e) {
            return $this->vendorDashboard($e);
        }
    }
}

My question is: Is it correct approach to use Open-Close-Principle ? Using above code I can tell controller that I can use PaymentService class for the implementation.

$payment = new Payment(new PaymentService);
return $payment->store($request,$id);

If later I want to make a payment in different way e.g. make a payment through invoice then I can create new controller, write new implementation in new class e.g. InvoicePaymentService and tell Payment class to use InvoicePaymentService as implementation

$payment = new Payment(new InvoicePaymentService);
return $payment->store($request,$id);

OR

$payment = new Payment(new PayPalPaymentService);
return $payment->store($request,$id);

OR

$payment = new Payment(new AliPayPaymentService);
return $payment->store($request,$id);

I know I can bind Interface with a class through a service provider but if I want to implement a different payment implementation then I will not be able to change the class, right ?

If I am doing it in wrong way please let me know.

like image 568
Afraz Ahmad Avatar asked Mar 07 '19 09:03

Afraz Ahmad


People also ask

How do you use the open-closed principle?

The Open-Close principle (OCP) is the O in the well known SOLID acronym. A module will be said to be open if it is still available for extension. For example, it should be possible to add fields to the data structures it contains, or new elements to the set of functions it performs.

What is solid principle in laravel?

Introduction. The SOLID design principles by Robert C. Martin, popularly known as Uncle Bob, are a set of principles that help a developer write clean, reusable, and maintainable code.

Why is the open-closed principle OCP important?

Open/closed principle is intended to mitigate risk when introducing new functionality. Since you don't modify existing code you can be assured that it wouldn't be broken. It reduces maintenance cost and increases product stability. Indeed, this is what OCP is about.

Which design pattern follows open-closed principle?

For example, the Decorator pattern offers us to follow the Open Close principle. Furthermore, we may use the Factory Method, Strategy pattern and the Observer pattern to design an application with minimum changes in the existing code. That's all about 'SOLID Principles : The Open Closed Principle'.


1 Answers

This is what service container stands for. You should use contextual binding

Assuming you have an interface: FooInterface

And you have two concrete implementations: GoodFoo and BadFoo

In order to inject different implementations to controllers (or other classes) you must tell it to laravel.

$this->app->when(GoodController::class)
      ->needs(FooInterface::class)
      ->give(function () {
            return new GoodFoo();
      });


$this->app->when(BadController::class)
      ->needs(FooInterface::class)
      ->give(function () {
            return new BadFoo();
      });

And controllers should be:

class GoodController extends Controller
{
    protected $foo;

    public function __construct(FooInterface $foo)
    {
        $this->foo = $foo;
    }
}

class BadController extends Controller
{
    protected $foo;

    public function __construct(FooInterface $foo)
    {
        $this->foo = $foo;
    }
}

Please note that most of the time laravel promotes bad software design principles and it rather hard to practise SOLID principles in laravel.

like image 158
Yarimadam Avatar answered Sep 21 '22 20:09

Yarimadam