We already discussed SRP in our previous blog. Let’s discuss Open/Closed Principle (OCP) in this blog.
OCP is defined as “Software entities (classes, modules, functions) should be open for extension but closed for modification.”. This means one should be able to add new functionality to existing code without altering it.
For example, we can try to add new functionality like filtering users by role without modifying the existing repository or service classes.
We will keep UserRepository.php in the previous SRP example as it is and untouched. But for this new functionality, we can create a new repository like RoleBasedUserRepository
<?php
namespace App\Http\Repositories;
use DB;
use App\Http\Repositories\UserRepository;
class RoleBasedUserRepository{
protected $repository;
public function __construct(UserRepository $objRepo){
$this->repository = $objRepo;
}
public function getUsersByRole($role){
return DB::table('users')->where('role', $role)->first();
}
}
Extend the Service Layer
<?php
namespace App\Http\Services;
use App\Http\Repositories\UserRepository;
use App\Http\Repositories\RoleBasedUserRepository;
class UserService {
protected $userRepository, $roleBasedUserRepository;
public function __construct(
UserRepository $objUserRepo,
RoleBasedUserRepository $objUserRepoRole
){
$this->userRepository = $objUserRepo;
$this->roleBasedUserRepository = $objUserRepoRole;
}
public function getAllUsers(){// }
public function getUserById($id){// }
public function createUser(array $data){// }
public function updateUser($id, array $data){// }
public function deleteUser($id){// }
// Use specialized repositories
public function getUsersByRole($role){
return $this->roleBasedUserRepository->getUsersByRole($role);
}
}
?>
Extend the Controller
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests\UserRequest;
use App\Http\Services\UserService;
class UserController extends Controller{
protected $service;
public function __construct(UserService $objUS){
$this->service = $objUS;
}
public function index(){ // }
public function store(UserRequest $request){ // }
public function show($id){ // }
public function update(UserRequest $request, $id){ // }
public function destroy($id){ // }
public function getUsersByRole($role){
return $this->service->getUsersByRole($role);
}
}
Discussion
I have added a new method in the UserController and UserService classes. By adding a new method, I have modified the class in a way but still, this code is OCP compliant. How?
OCP prohibits modification in the existing code. Modification means a change in the behavior of the methods. So few things should be remembered for classes to be OCP compliant:
1. Existing code behavior must not change
2. New code should not disturb existing code
3. New code should not produce any ripple effects in the existing code
4. SRP should not break
So, as long as the existing features are working in the same way as before, we can add new methods, and this will still be OCP compliant. Consider it sort of “plug and play” the new feature without breaking SRP and without changing the behavior of existing methods in any way … and it will be OCP compliant.