Design Principle: Encapsulate What Varies(EWV)

Summary

Principle NameEncapsulate What Varies
AcronymEWV
Principle Type Core Principle
PurposeIsolate part of the code that is likely to change later, by encapsulating them.
Implementing
Design Patterns
Factory Pattern
State Pattern
Strategy Pattern
Related Principles Open-Closed Principle(OCP)
Separation of Concerns(SoC)
Single Responsibility Principle(SRP)

Definition

In software development requirement changes over time. And to adopt that we have to make changes to our code. Keeping that in mind we have to write our code, so that it becomes easier to adopt any change later.

This Encapsulate What Varies(EWV) principle is-

Isolate the part of the code that is likely to be changed later, by encapsulating them. So that the encapsulation works as a shield for the rest of the code.

As it is difficult to predict which part of the code might change, due to requirement change. We should decide, for which parts we want to have the ability to change with redesigning.

Explanation

Certain parts of the code are more likely to be changed. Say there are multiple options available for doing the same job. It is highly possible that more options will be added later.

Like, there can be multiple storage systems(like, local storage, Google drive, AWS S3, etc.), and multiple payment options(like, card, stripe, PayPal, etc.). So we should not implement the solution for only one option.

In these cases, we should use polymorphism, and use an interface(or common parent class), and implement the interface for all those options.

Let’s do it for the storage. Here the varying part is the storage systems, as any storage system can be used at any point of time. So we are encapsulating the storage implementation using polymorphism.

Storage Interface

Create an interface for all the storage systems, so that all can implement the same interface. We have declared a few methods according to our requirements.

// Storage.java

package com.bigboxcode;

public interface Storage {
    int storeFile(String tempPath);
    String retrieveFile(int fileId);
    void printFileInfo(int fileId);
}
Java
# Storage interface

from abc import ABC, abstractmethod

class Storage(ABC):
    @abstractmethod
    def store_file(self, temp_path: str) -> int:
        pass

    @abstractmethod
    def retrieve_file(self, file_id: int) -> str:
        pass

    @abstractmethod
    def print_file_info(self, file_id: int):
        pass
Python
// storage.go

package main

import "fmt"

// Storage interface defines the methods for storing and retrieving files
type Storage interface {
	StoreFile(tempPath string) int
	RetrieveFile(fileID int) string
	PrintFileInfo(fileID int)
}
Go
// Storage.php

<?php

interface Storage {
    public function storeFile(string $tempPath): int;
    public function retrieveFile(int $fileId): string;
    public function printFileInfo(int $fileId): void;
}
PHP
// Storage.ts

export interface Storage {
    storeFile(tempPath: string): number;
    retrieveFile(fileId: number): string;
    printFileInfo(fileId: number): void;
}
TypeScript

Local Storage Class

Create the LocalStorage class and implement the Storage interface. Here we have implemented the methods with some dummy data for demo purposes.

// LocalStorage.java

package com.bigboxcode;

import java.util.Random;

public class LocalStorage implements Storage {
    @Override
    public int storeFile(String tempPath) {
        // Dummy random ID for demo purpose
        return new Random().nextInt(100);
    }

    @Override
    public String retrieveFile(int fileId) {
        // Dummy URL for demo
        return "https://bigboxcode.com/files/local/" + fileId;
    }

    @Override
    public void printFileInfo(int fileId) {
        System.out.println("Storage type: Local Storage");
        System.out.println("File ID: " + fileId);
        System.out.println("File URL: " + retrieveFile(fileId));
    }
}
Java
# Local Storage

import random

class LocalStorage(Storage):
    def store_file(self, temp_path: str) -> int:
        # Dummy random ID for demo purpose
        return random.randint(1, 100)

    def retrieve_file(self, file_id: int) -> str:
        # Dummy url for demo
        return f"https://bigboxcode.com/files/local/{file_id}"

    def print_file_info(self, file_id: int):
        print("Storage type: Local Storage")
        print(f"File ID: {file_id}")
        print(f"File URL: {self.retrieve_file(file_id)}")
Python
// local_storage.go

package main

import (
	"fmt"
	"math/rand"
)

// LocalStorage is a concrete implementation of Storage for local storage
type LocalStorage struct{}

// StoreFile generates a random file ID for local storage
func (l *LocalStorage) StoreFile(tempPath string) int {
	return rand.Intn(100)
}

// RetrieveFile returns a dummy URL for local storage
func (l *LocalStorage) RetrieveFile(fileID int) string {
	return fmt.Sprintf("https://bigboxcode.com/files/local/%d", fileID)
}

// PrintFileInfo prints the file information for local storage
func (l *LocalStorage) PrintFileInfo(fileID int) {
	fmt.Println("Storage type: Local Storage")
	fmt.Printf("File ID: %d\n", fileID)
	fmt.Printf("File URL: %s\n", l.RetrieveFile(fileID))
}
Go
// LocalStorage.php

<?php

class LocalStorage implements Storage {
    public function storeFile(string $tempPath): int {
        // Dummy random ID for demo purpose
        return rand(1, 100);
    }

    public function retrieveFile(int $fileId): string {
        // Dummy URL for demo
        return "https://bigboxcode.com/files/local/{$fileId}";
    }

    public function printFileInfo(int $fileId): void {
        echo "Storage type: Local Storage\n";
        echo "File ID: {$fileId}\n";
        echo "File URL: " . $this->retrieveFile($fileId) . "\n";
    }
}
PHP
// LocalStorage.ts

import { Storage } from './Storage';

export class LocalStorage implements Storage {
    storeFile(tempPath: string): number {
        // Dummy random ID for demo purpose
        return Math.floor(Math.random() * 100) + 1;
    }

    retrieveFile(fileId: number): string {
        // Dummy URL for demo
        return `https://bigboxcode.com/files/local/${fileId}`;
    }

    printFileInfo(fileId: number): void {
        console.log("Storage type: Local Storage");
        console.log(`File ID: ${fileId}`);
        console.log(`File URL: ${this.retrieveFile(fileId)}`);
    }
}
TypeScript

Google Drive Storage Class

Create the class GoogleDirveStorage and implement the Storage interface.

// GoogleDriveStorage.java

package com.bigboxcode;

import java.util.Random;

public class GoogleDriveStorage implements Storage {
    @Override
    public int storeFile(String tempPath) {
        // Dummy random ID for demo purpose
        return new Random().nextInt(1000);
    }

    @Override
    public String retrieveFile(int fileId) {
        // Dummy URL for demo
        return "https://drive.google.com/file/d/1234_9K7654hu6RT_9j7JKY3fK/view";
    }

    @Override
    public void printFileInfo(int fileId) {
        System.out.println("Storage type: Google Drive");
        System.out.println("File ID: " + fileId);
        System.out.println("File URL: " + retrieveFile(fileId));
    }
}
Java

class GoogleDriveStorage(Storage):
    def store_file(self, temp_path: str) -> int:
        # Dummy random ID for demo purpose
        return random.randint(1, 1000)

    def retrieve_file(self, file_id: int) -> str:
        # Dummy url for demo
        return "https://drive.google.com/file/d/1234_9K7654hu6RT_9j7JKY3fK/view"

    def print_file_info(self, file_id: int):
        print("Storage type: Google Drive")
        print(f"File ID: {file_id}")
        print(f"File URL: {self.retrieve_file(file_id)}")
Python
// google_drive_storage.go

package main

import (
	"fmt"
	"math/rand"
)

// GoogleDriveStorage is a concrete implementation of Storage for Google Drive
type GoogleDriveStorage struct{}

// StoreFile generates a random file ID for Google Drive storage
func (g *GoogleDriveStorage) StoreFile(tempPath string) int {
	return rand.Intn(1000)
}

// RetrieveFile returns a dummy URL for Google Drive
func (g *GoogleDriveStorage) RetrieveFile(fileID int) string {
	return "https://drive.google.com/file/d/1234_9K7654hu6RT_9j7JKY3fK/view"
}

// PrintFileInfo prints the file information for Google Drive storage
func (g *GoogleDriveStorage) PrintFileInfo(fileID int) {
	fmt.Println("Storage type: Google Drive")
	fmt.Printf("File ID: %d\n", fileID)
	fmt.Printf("File URL: %s\n", g.RetrieveFile(fileID))
}
Go
// GoogleDriveStorage.php

<?php

class GoogleDriveStorage implements Storage {
    public function storeFile(string $tempPath): int {
        // Dummy random ID for demo purpose
        return rand(1, 1000);
    }

    public function retrieveFile(int $fileId): string {
        // Dummy URL for demo
        return "https://drive.google.com/file/d/1234_9K7654hu6RT_9j7JKY3fK/view";
    }

    public function printFileInfo(int $fileId): void {
        echo "Storage type: Google Drive\n";
        echo "File ID: {$fileId}\n";
        echo "File URL: " . $this->retrieveFile($fileId) . "\n";
    }
}
PHP
// GoogleDriveStorage.ts

import { Storage } from './Storage';

export class GoogleDriveStorage implements Storage {
    storeFile(tempPath: string): number {
        // Dummy random ID for demo purpose
        return Math.floor(Math.random() * 1000) + 1;
    }

    retrieveFile(fileId: number): string {
        // Dummy URL for demo
        return "https://drive.google.com/file/d/1234_9K7654hu6RT_9j7JKY3fK/view";
    }

    printFileInfo(fileId: number): void {
        console.log("Storage type: Google Drive");
        console.log(`File ID: ${fileId}`);
        console.log(`File URL: ${this.retrieveFile(fileId)}`);
    }
}
TypeScript

S3 Storage Class

Create the class S3Storage and implement the Storage interface.

// S3Storage.java

package com.bigboxcode;

import java.util.Random;

public class S3Storage implements Storage {
    @Override
    public int storeFile(String tempPath) {
        // Dummy random ID for demo purpose
        return new Random().nextInt(10000);
    }

    @Override
    public String retrieveFile(int fileId) {
        // Dummy URL for demo
        return "https://bigboxcode.s3.amazonaws.com/pdf/UC-0e7654338-5697-4f99-b33-d89h87g5gf4gwfg.pdf";
    }

    @Override
    public void printFileInfo(int fileId) {
        System.out.println("Storage type: AWS S3");
        System.out.println("File ID: " + fileId);
        System.out.println("File URL: " + retrieveFile(fileId));
    }
}
Java
import random

class S3Storage(Storage):
    def store_file(self, temp_path: str) -> int:
        # Dummy random ID for demo purpose
        # In a real scenario, the file would be uploaded to 
        # AWS S3 and the ID from the database or S3 response would be returned
        return random.randint(1, 10000)

    def retrieve_file(self, file_id: int) -> str:
        # Dummy url for demo
        # This URL  dynamically generated or retrieved from S3
        return "https://bigboxcode.s3.amazonaws.com/pdf/UC-0e7654338-5697-4f99-b33-d89h87g5gf4gwfg.pdf"

    def print_file_info(self, file_id: int):
        print("Storage type: AWS S3")
        print(f"File ID: {file_id}")
        print(f"File URL: {self.retrieve_file(file_id)}")
Python
// s3_storage.go

package main

import (
	"fmt"
	"math/rand"
)

// S3Storage is a concrete implementation of Storage for AWS S3
type S3Storage struct{}

// StoreFile generates a random file ID for S3 storage
func (s *S3Storage) StoreFile(tempPath string) int {
	return rand.Intn(10000)
}

// RetrieveFile returns a dummy URL for S3 storage
func (s *S3Storage) RetrieveFile(fileID int) string {
	return "https://bigboxcode.s3.amazonaws.com/pdf/UC-0e7654338-5697-4f99-b33-d89h87g5gf4gwfg.pdf"
}

// PrintFileInfo prints the file information for S3 storage
func (s *S3Storage) PrintFileInfo(fileID int) {
	fmt.Println("Storage type: AWS S3")
	fmt.Printf("File ID: %d\n", fileID)
	fmt.Printf("File URL: %s\n", s.RetrieveFile(fileID))
}
Go
// S3Storage.php

<?php

class S3Storage implements Storage {
    public function storeFile(string $tempPath): int {
        // Dummy random ID for demo purpose
        return rand(1, 10000);
    }

    public function retrieveFile(int $fileId): string {
        // Dummy URL for demo
        return "https://bigboxcode.s3.amazonaws.com/pdf/UC-0e7654338-5697-4f99-b33-d89h87g5gf4gwfg.pdf";
    }

    public function printFileInfo(int $fileId): void {
        echo "Storage type: AWS S3\n";
        echo "File ID: {$fileId}\n";
        echo "File URL: " . $this->retrieveFile($fileId) . "\n";
    }
}
PHP
// S3Storage.ts

import { Storage } from './Storage';

export class S3Storage implements Storage {
    storeFile(tempPath: string): number {
        // Dummy random ID for demo purpose
        return Math.floor(Math.random() * 10000) + 1;
    }

    retrieveFile(fileId: number): string {
        // Dummy URL for demo
        return "https://bigboxcode.s3.amazonaws.com/pdf/UC-0e7654338-5697-4f99-b33-d89h87g5gf4gwfg.pdf";
    }

    printFileInfo(fileId: number): void {
        console.log("Storage type: AWS S3");
        console.log(`File ID: ${fileId}`);
        console.log(`File URL: ${this.retrieveFile(fileId)}`);
    }
}
TypeScript

Storage System Class

Create a class for managing storage systems, and performing operations. This class accepts an instance of the Storage interface, and then performs operations on that instance.

// StorageSystem.java

package com.bigboxcode;

public class StorageSystem {
    private Storage storage;

    public StorageSystem(Storage storage) {
        this.storage = storage;
    }

    public int uploadFile(String tempPath) {
        int fileId = storage.storeFile(tempPath);
        storage.printFileInfo(fileId);
        return fileId;
    }

    public String getFileUrl(int fileId) {
        return storage.retrieveFile(fileId);
    }
}
Java
class StorageSystem:
    def __init__(self, storage: Storage):
        self.storage = storage

    def upload_file(self, temp_path: str) -> int:
        file_id = self.storage.store_file(temp_path)
        self.storage.print_file_info(file_id)
        return file_id

    def get_file_url(self, file_id: int) -> str:
        return self.storage.retrieve_file(file_id)
Python
// storage_system.go

package main

// StorageSystem is a context that accepts different storage strategies
type StorageSystem struct {
	storage Storage
}

// NewStorageSystem creates a new storage system with the given storage strategy
func NewStorageSystem(storage Storage) *StorageSystem {
	return &StorageSystem{storage: storage}
}

// UploadFile stores the file using the provided storage strategy
func (ss *StorageSystem) UploadFile(tempPath string) int {
	fileID := ss.storage.StoreFile(tempPath)
	ss.storage.PrintFileInfo(fileID)
	return fileID
}

// GetFileURL retrieves the file URL using the provided storage strategy
func (ss *StorageSystem) GetFileURL(fileID int) string {
	return ss.storage.RetrieveFile(fileID)
}
Go
// StorageSystem.php

<?php

class StorageSystem {
    private Storage $storage;

    public function __construct(Storage $storage) {
        $this->storage = $storage;
    }

    public function uploadFile(string $tempPath): int {
        $fileId = $this->storage->storeFile($tempPath);
        $this->storage->printFileInfo($fileId);
        return $fileId;
    }

    public function getFileUrl(int $fileId): string {
        return $this->storage->retrieveFile($fileId);
    }
}
PHP
// StorageSystem.ts

import { Storage } from './Storage';

export class StorageSystem {
    private storage: Storage;

    constructor(storage: Storage) {
        this.storage = storage;
    }

    uploadFile(tempPath: string): number {
        const fileId = this.storage.storeFile(tempPath);
        this.storage.printFileInfo(fileId);
        return fileId;
    }

    getFileUrl(fileId: number): string {
        return this.storage.retrieveFile(fileId);
    }
}
TypeScript

Usage

We can create an instance of any of the storage classes(LocalStorage, GoogleDriveStorage, S3Storage), and pass that to StorageSystem. Then we can use the storage.

// Main.java

package com.bigboxcode;

public class Main {
    public static void main(String[] args) {
        // Usage example with LocalStorage
        Storage localStorage = new LocalStorage();
        StorageSystem storageSystem = new StorageSystem(localStorage);
        int fileId = storageSystem.uploadFile("/path/to/local/file");
        String url = storageSystem.getFileUrl(fileId);
        System.out.println("Retrieved file URL: " + url);

        // Usage example with GoogleDriveStorage
        Storage googleDriveStorage = new GoogleDriveStorage();
        storageSystem = new StorageSystem(googleDriveStorage);
        fileId = storageSystem.uploadFile("/path/to/drive/file");
        url = storageSystem.getFileUrl(fileId);
        System.out.println("Retrieved file URL: " + url);

        // Usage example with S3Storage
        Storage s3Storage = new S3Storage();
        storageSystem = new StorageSystem(s3Storage);
        fileId = storageSystem.uploadFile("/path/to/s3/file");
        url = storageSystem.getFileUrl(fileId);
        System.out.println("Retrieved file URL: " + url);
    }
}
Java
# Usage example with LocalStorage
local_storage = LocalStorage()
storage_strategy = StorageSystem(local_storage)
file_id = storage_strategy.upload_file("/path/to/local/file")
url = storage_strategy.get_file_url(file_id)
print(f"Retrieved file URL: {url}")


# Usage example with GoogleDriveStorage
google_drive_storage = GoogleDriveStorage()
storage_strategy = StorageSystem(google_drive_storage)
file_id = storage_strategy.upload_file("/path/to/drive/file")
url = storage_strategy.get_file_url(file_id)
print(f"Retrieved file URL: {url}")
Python
// main.go

package main

import "fmt"

func main() {
	// Usage example with LocalStorage
	localStorage := &LocalStorage{}
	storageSystem := NewStorageSystem(localStorage)
	fileID := storageSystem.UploadFile("/path/to/local/file")
	url := storageSystem.GetFileURL(fileID)
	fmt.Printf("Retrieved file URL: %s\n", url)

	// Usage example with GoogleDriveStorage
	googleDriveStorage := &GoogleDriveStorage{}
	storageSystem = NewStorageSystem(googleDriveStorage)
	fileID = storageSystem.UploadFile("/path/to/drive/file")
	url = storageSystem.GetFileURL(fileID)
	fmt.Printf("Retrieved file URL: %s\n", url)
}
Go
// main.php

<?php

require_once 'Storage.php';
require_once 'LocalStorage.php';
require_once 'GoogleDriveStorage.php';
require_once 'S3Storage.php';
require_once 'StorageSystem.php';

// Usage example with LocalStorage
$localStorage = new LocalStorage();
$storageSystem = new StorageSystem($localStorage);
$fileId = $storageSystem->uploadFile("/path/to/local/file");
$url = $storageSystem->getFileUrl($fileId);
echo "Retrieved file URL: {$url}\n";

// Usage example with GoogleDriveStorage
$googleDriveStorage = new GoogleDriveStorage();
$storageSystem = new StorageSystem($googleDriveStorage);
$fileId = $storageSystem->uploadFile("/path/to/drive/file");
$url = $storageSystem->getFileUrl($fileId);
echo "Retrieved file URL: {$url}\n";
PHP
// main.ts

import { LocalStorage } from './LocalStorage';
import { GoogleDriveStorage } from './GoogleDriveStorage';
import { S3Storage } from './S3Storage';
import { StorageSystem } from './StorageSystem';

// Usage example with LocalStorage
const localStorage = new LocalStorage();
const storageSystem = new StorageSystem(localStorage);
let fileId = storageSystem.uploadFile("/path/to/local/file");
let url = storageSystem.getFileUrl(fileId);
console.log(`Retrieved file URL: ${url}`);

// Usage example with GoogleDriveStorage
const googleDriveStorage = new GoogleDriveStorage();
const googleDriveStorageSystem = new StorageSystem(googleDriveStorage);
fileId = googleDriveStorageSystem.uploadFile("/path/to/drive/file");
url = googleDriveStorageSystem.getFileUrl(fileId);
console.log(`Retrieved file URL: ${url}`);
TypeScript

Leave a Comment


The reCAPTCHA verification period has expired. Please reload the page.