The journey from Class to Interface

Ashish Shakya
4 min readMay 29, 2020

We have been constantly using Class from the beginning of our OOP journey. It is easy and convenient to create a class and then add up methods and properties in it and carry out our requirements.

In this blog, I will share how to use Interface which enhances our coding structure.

Basically Interface is a technique used in programming where you build your application based on abstractions To code using Interface is a technique where our application depends on abstractions (interfaces) instead of concretions (classes).

So let us dive into code.

class Logger{
public function log(string $content){
// logic to log
}
}

Here we have added a Logger Class and a method to log .

We can simply use this class by creating an instance of it and call its respective method as shown below:

class LogController
{
public function log()
{
$logger = new Logger;
$logger->log('Interface is awesome!!!');
}
}

But problems occur when we want to log to the database, cloud server, Redis, or any other targets. In such a scenario, Interface is the best technique to achieve the requirement.

Let us see how we can refactor our code from using old pattern classes to interfaces.

Using methods within classes:

We simply update our Logger class with multiple methods that logs to the respective targets.

class Logger {

public function logToDatabase(string $content)
{
// logic to log content to db.
}

public function logToFile(string $content)
{
// logic to log content to file.
}

public function logToRedis(string $content)
{
// logic to log content to redis.
}

}

Similarly, we update LogController to address the target for logging the contents.

class LogController{
public function log(){
$logger = new Logger;
$target = 'file';
if ($target === 'file') {
$logger->logToFile($content);
}
else if ($target === 'redis') {
$logger->logToCloud($content);
}
else {
$logger->logToDatabase($content);
}
}
}

It seems pretty good. But let me say when does it backfire.

What if we want to add another target and log our contents to Cloud Server . We will have to register a new target to our Logger class and a new else-if condition in LogController . As the result, our code becomes messy for scalability.

So for this, we follow the SOLID principle and split the code as per the responsibility, where we create a separate class for each target as like this:

class DatabaseLogger
{
public function log(string $content)
{
// logic to log content to db.
}
}

class FileLogger
{
public function log(string $content)
{
// logic to log content into file
}
}

class RedisLogger
{
public function log(string $content)
{
// logic to log content log to redis
}
}

We then update the LogController class to:

class LogController
{
public function log()
{
$target = 'file';
$content= 'Interface is awesome!!!!'
if ($target === 'file') {
(new DatabaseLogger)->log($content);
}
else if ($target === 'redis') {
(new RedisLogger)->log($content);
}
else {
$logger->logToDatabase($content);
}
}
}

Though SOLID principle helped us to split our code, but our LogController is still responsible for determining the logger target.

Here comes the role of INTERFACE:

An interface is a list of methods that a class needs to implement. It’s a way to decouple how a class works from what services the class provides.

For this, we first create an interface and define a method.

interface Logger
{
public function log(string $content);
}

As you can see it contains only function declaration and not its implementation, which is why it is called as abstraction.

We then need to modify our target logger classes to implement this interface.

class DatabaseLogger implements Logger
{
public function log(string $content)
{
// logic to log content to db.
}
}

class FileLogger implements Logger
{
public function log(string $content)
{
// logic to log content into file
}
}

class RedisLogger implements Logger
{
public function log(string $content)
{
// logic to log content log to redis
}
}

And lastly, we simply call the logger class from our LogController class.

class LogController extends Controller
{
public function log()
{
$logger = new DatabaseLogger();
$logger->log($content);
}
}

In this way, the controller doesn’t care which type of logger is passed to it. All it needs to know is that it needs to create an instance of the Logger interface and call its subsequent method.

Not only the structure looks clean, but it also allows for better scalability.

We can now add more target loggers without touching the existing code. All we have to do is create a new class that implements our Logger interface.

class CloudLogger implements Logger
{
public function log(string $content)
{
// logic to log content to cloud server
}
}

Our code is now flexible and loosely coupled. We can swap the implementations any time we want without touching the existing code.

--

--