The interface segregation principle states:
A client should never be forced to implement an interface that it doesn’t use, or clients shouldn’t be forced to depend on methods they do not use.
This essentially means that instead of a big interface with lot many methods… we should have smaller interfaces with specific methods that will be implemented in specific classes.
I will continue with the previous example that I used in LSP (Liskov Substitution Principle).
In that example, we only have UserRepositor which implements the interface UserRepositoryInterface.
RoleBasedUserRepository extends UserRepository, which means RoleBasedUserRepository also uses methods defined in UserRepositoryInterface. However, specialized repositories like RoleBasedUserRepository do not require all those general methods in UserRepositoryInterface.
So to make it ISP-compliant, we need to make afew changes.
Step 1:
Create a new interface with the following code
<?php
namespace App\Http\Repositories\Contracts;
interface RoleBasedUserRepositoryInterface{
public function getUsersByRole(string $role);
}
That means UserRepositoryInterface contains general CRUD operations.
RoleBasedUserRepositoryInterface contains role-specific methods.
Step 2: UserRepository will remain as it is without change.
Step 3: RoleBasedUserRepository will not extend UserRepository anymore. See below:
We imported the role-based interface and then implemented it.
<?php
namespace App\Http\Repositories;
use DB;
use App\Http\Repositories\Contracts\RoleBasedUserRepositoryInterface;
class RoleBasedUserRepository implements RoleBasedUserRepositoryInterface{
protected $table = 'users';
public function getUsersByRole($role){
return DB::table($this->table)->where('role', $role)->first();
}
}
Step 4: We are at the Service layer, where we will need to import both interfaces and then inject them into the constructor. Injecting both interfaces helps the controller call all methods in the service layer. The binding of the interface and repository makes this possible.
<?php
namespace App\Http\Services;
use App\Http\Repositories\Contracts\UserRepositoryInterface;
use App\Http\Repositories\Contracts\RoleBasedUserRepositoryInterface;
class UserService {
protected $userRepository, $roleBasedRepository;
public function __construct(
UserRepositoryInterface $objUserRepoInterface,
RoleBasedUserRepositoryInterface $objRoleBasedUserRepoInterface
){
$this->userRepository = $objUserRepoInterface;
$this->roleBasedRepository = $objRoleBasedUserRepoInterface;
}
public function getAllUsers(){
return $this->userRepository->getAllUsers();
}
public function getUserById($id){
return $this->userRepository->find($id);
}
public function createUser(array $data){
return $this->userRepository->create($data);
}
public function updateUser($id, array $data){
$user = $this->userRepository->find($id);
return $this->userRepository->update($user, $data);
}
public function deleteUser($id){
$user = $this->userRepository->find($id);
return $this->userRepository->delete($user);
}
// Role-Based Methods
public function getUsersByRole($role){
return $this->roleBasedRepository->getUsersByRole($role);
}
}
Step 5: in the binding service provider
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Http\Repositories\Contracts\UserRepositoryInterface;
use App\Http\Repositories\UserRepository;
use App\Http\Repositories\Contracts\RoleBasedUserRepositoryInterface;
use App\Http\Repositories\RoleBasedUserRepository;
class AppServiceProvider extends ServiceProvider {
public function register(): void{
$this->app->bind(UserRepositoryInterface::class, UserRepository::class);
$this->app->bind(RoleBasedUserRepositoryInterface::class, RoleBasedUserRepository::class);
}
}
Step 6: Update the Controller
Here we get rid of the specialized repository as we do not import it and only injecting service in the constructor does all the work.
<?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;
}
// Other methods remain Same as previous example
public function getUsersByRole($role){
return $this->service->getUsersByRole($role);
}
}
Discussion:
Both the interfaces are for specific tasks. UserService depends on these interfaces, adhering to ISP, as we have separation of concerns as required by ISP. Repositories have a single responsibility of doing db operations