In a file storage system, we have several key entities:
File: Represents a file with its name, size, metadata, versions, and permissions.
User: Represents a user interacting with the file system.
FileVersion: Represents different versions of a file.
FileMetadata: Stores metadata information about the file (e.g., size, created date).
Permission: Represents user permissions (read/write/share) for a file.
StorageStrategy: Represents different storage mechanisms (e.g., local storage, cloud storage).
FileSystem: Manages all file operations and ensures that only one instance exists, implementing Singleton.
NotificationSystem: Notifies users of file changes, utilizing Observer Pattern.
Design Patterns Used
Singleton Pattern: Used in the FileSystem class to ensure a single instance manages file operations.
Factory Pattern: Used in FileFactory to create instances of files and file versions.
Strategy Pattern: Used to switch between different storage strategies (local or cloud).
Observer Pattern: Used in the NotificationSystem to notify users of changes or updates to files.
Code Implementation
1. Singleton Pattern - FileSystem Class
The Singleton Pattern ensures only one instance of FileSystem exists in the application.
import java.util.List;
import java.util.ArrayList;
// Singleton pattern for FileSystem to manage file operations
class FileSystem {
private static FileSystem instance;
private List<File> files;
private StorageStrategy storageStrategy;
private FileSystem(StorageStrategy storageStrategy) {
this.files = new ArrayList<>();
this.storageStrategy = storageStrategy;
}
public static FileSystem getInstance(StorageStrategy storageStrategy) {
if (instance == null) {
instance = new FileSystem(storageStrategy);
}
return instance;
}
public void addFile(File file) {
files.add(file);
storageStrategy.uploadFile(file); // Delegate file storage to strategy
}
public boolean downloadFile(File file) {
return storageStrategy.downloadFile(file); // Delegate file download to strategy
}
}
2. Factory Pattern - FileFactory Class
The Factory Pattern abstracts the creation of File and FileVersion objects.
// Factory pattern to create File and FileVersion
class FileFactory {
public static File createFile(String fileName, long fileSize, User owner) {
return new File(fileName, fileSize, owner);
}
public static FileVersion createFileVersion(String versionId, byte[] content) {
return new FileVersion(versionId, content);
}
}
3. Strategy Pattern - StorageStrategy and Concrete Implementations (LocalStorage, CloudStorage)
The Strategy Pattern allows dynamic switching between storage mechanisms.
// Strategy pattern to define storage types
interface StorageStrategy {
boolean uploadFile(File file);
boolean downloadFile(File file);
}
// Concrete strategy for local storage
class LocalStorage implements StorageStrategy {
@Override
public boolean uploadFile(File file) {
System.out.println("Uploading file to local storage: " + file.getFileName());
return true;
}
@Override
public boolean downloadFile(File file) {
System.out.println("Downloading file from local storage: " + file.getFileName());
return true;
}
}
// Concrete strategy for cloud storage
class CloudStorage implements StorageStrategy {
@Override
public boolean uploadFile(File file) {
System.out.println("Uploading file to cloud storage: " + file.getFileName());
return true;
}
@Override
public boolean downloadFile(File file) {
System.out.println("Downloading file from cloud storage: " + file.getFileName());
return true;
}
}
4. Observer Pattern - NotificationSystem and FileObserver
The Observer Pattern is used to notify users of changes (like updates or sharing) in the files.
import java.util.ArrayList;
import java.util.List;
// Observer pattern
interface Observer {
void update(String message);
}
class NotificationSystem implements Observer {
private String userName;
public NotificationSystem(String userName) {
this.userName = userName;
}
@Override
public void update(String message) {
System.out.println(userName + " received notification: " + message);
}
}
class File {
private String fileName;
private long fileSize;
private User owner;
private List<FileVersion> versions;
private List<Observer> observers; // To notify users when file is updated
private FileMetadata metadata;
public File(String fileName, long fileSize, User owner) {
this.fileName = fileName;
this.fileSize = fileSize;
this.owner = owner;
this.versions = new ArrayList<>();
this.observers = new ArrayList<>();
this.metadata = new FileMetadata(fileName, owner);
}
// Add new version of the file
public void addVersion(FileVersion version) {
versions.add(version);
}
// Register observers for notifications
public void addObserver(Observer observer) {
observers.add(observer);
}
// Update file and notify observers
public void updateFile(String newVersion) {
System.out.println("File " + fileName + " has been updated to version " + newVersion);
for (Observer observer : observers) {
observer.update("File " + fileName + " has been updated.");
}
}
// Getter methods
public String getFileName() {
return fileName;
}
public long getFileSize() {
return fileSize;
}
public User getOwner() {
return owner;
}
public List<FileVersion> getVersions() {
return versions;
}
}
5. File Class, Versioning, Permissions, and Other Entities
These are the key entities involved in the file storage system. They represent user-related data and metadata for file management.
// User class to represent users in the system
class User {
private String userName;
private String email;
public User(String userName, String email) {
this.userName = userName;
this.email = email;
}
public String getUserName() {
return userName;
}
public String getEmail() {
return email;
}
}
// FileVersion class to represent versions of a file
class FileVersion {
private String versionId;
private byte[] content;
public FileVersion(String versionId, byte[] content) {
this.versionId = versionId;
this.content = content;
}
public String getVersionId() {
return versionId;
}
public byte[] getContent() {
return content;
}
}
// Metadata class to store file-related metadata
class FileMetadata {
private String fileName;
private User owner;
private long createdAt;
public FileMetadata(String fileName, User owner) {
this.fileName = fileName;
this.owner = owner;
this.createdAt = System.currentTimeMillis(); // Current timestamp
}
public String getFileName() {
return fileName;
}
public User getOwner() {
return owner;
}
public long getCreatedAt() {
return createdAt;
}
}
Testing the System
In this test scenario, we create instances of users and files, perform operations like adding files, updating versions, and downloading files while notifying users of changes.
public class Main {
public static void main(String[] args) {
// Using Singleton pattern for FileSystem
StorageStrategy cloudStorage = new CloudStorage();
FileSystem fileSystem = FileSystem.getInstance(cloudStorage);
// Create users and files
User user1 = new User("John", "john@example.com");
User user2 = new User("Alice", "alice@example.com");
File file1 = FileFactory.createFile("report.pdf", 1024, user1);
FileVersion version1 = FileFactory.createFileVersion("v1", new byte[1024]);
file1.addVersion(version1);
// Add observers
NotificationSystem observer1 = new NotificationSystem(user1.getUserName());
file1.addObserver(observer1);
// Add file to the file system
fileSystem.addFile(file1);
// Update file
file1.updateFile("v2");
// Download file
fileSystem.downloadFile(file1);
}
}
Summary of Design Patterns Implemented
Singleton Pattern: Ensures that only one instance of the FileSystem class exists, managing the centralized file operations.
Factory Pattern: Provides a simple interface for creating File and FileVersion objects, abstracting the object creation logic.
Strategy Pattern: Enables the dynamic switching between LocalStorage and CloudStorage without modifying the core logic.
Observer Pattern: Notifies users (observers) when a file is updated, enhancing the interactivity and responsiveness of the system.
Conclusion
This low-level design provides a flexible, scalable, and modular structure for a file storage system. By utilizing design patterns, we ensure that the system is easy to maintain and extend, while also providing flexibility in storage solutions, file versioning, and user notifications.