# ProxyA intermediate-level guide to Proxy: before-and-after java code and diagrams for a CS student.

## The Burden of Housekeeping

Imagine you are at a bank. You want to withdraw money from the vault, but you do not walk into the vault yourself. Instead, you talk to a teller. The teller performs "housekeeping" tasks: they check your ID, verify your signature, and ensure you actually have enough funds. Only after these checks are passed does the teller facilitate access to the actual money.

In software, we often face a similar problem where a single class tries to be both the "vault" (doing the core work) and the "teller" (managing security, logging, or resource loading). This leads to a violation of the Single Responsibility Principle (SRP), which states that a class should have only one reason to change. When your business logic is tangled with permission checks, the class becomes harder to test and maintain.

```java
public class DocumentService {
    // The service is cluttered with housekeeping/security logic
    public void openDocument(String userId, String documentId) {
        // Security check (Housekeeping)
        if ("ADMIN".equals(userId)) {
            System.out.println("Access granted to all documents.");
        } else if (!checkPermissions(userId, documentId)) {
            throw new SecurityException("Unauthorized access");
        }

        // Core business logic (Real Subject work)
        loadHeavyDocumentFromDisk(documentId);
    }

    private boolean checkPermissions(String userId, String docId) {
        return true; // Mock permission check
    }

    private void loadHeavyDocumentFromDisk(String docId) {
        System.out.println("Loading document " + docId + " from disk (heavy operation)...");
    }
}
```

In this example, `DocumentService` is doing two unrelated jobs: enforcing security rules via `checkPermissions` and performing the heavy work of loading files via `loadHeavyDocumentFromDisk`. The core logic is trapped inside a web of conditional checks.

```mermaid
classDiagram
    class DocumentService {
        +openDocument(String userId, String documentId) void
        -checkPermissions(String userId, String docId) boolean
        -loadHeavyDocumentFromDisk(String docId) void
    }
```

## The Proxy Solution

The Proxy pattern solves this by introducing an intermediary object. A Proxy is a structural design pattern that provides a surrogate or placeholder for another object to control access to it. 

To make this work seamlessly, the Proxy must implement the exact same interface as the real object. This allows the client (the code using the service) to interact with either the real service or the proxy without knowing anything has changed. We call the actual worker the "Real Subject" and the intermediary the "Proxy."

```java
interface DocumentService {
    void openDocument(String userId, String documentId);
}

class RealDocumentService implements DocumentService {
    @Override
    public void openDocument(String userId, String documentId) {
        loadHeavyDocumentFromDisk(documentId);
    }

    private void loadHeavyDocumentFromDisk(String docId) {
        System.out.println("Loading document " + docId + " from disk (heavy operation)...");
    }
}

class DocumentSecurityProxy implements DocumentService {
    private final DocumentService realService;

    public DocumentSecurityProxy(DocumentService realService) {
        this.realService = realService;
    }

    @Override
    public void openDocument(String userId, String documentId) {
        // The proxy handles the housekeeping/security concerns
        if ("ADMIN".equals(userId)) {
            System.out.println("Access granted to all documents.");
            realService.openDocument(userId, documentId);
        } else if (!checkPermissions(userId, documentId)) {
            throw new SecurityException("Unauthorized access");
        } else {
            realService.openDocument(userId, documentId);
        }
    }

    private boolean checkPermissions(String userId, String docId) {
        return true; // Mock permission check
    }
}
```

By extracting the security logic into `DocumentSecurityProxy`, we have achieved two things:
1. The `RealDocumentService` now only cares about loading documents.
2. The `DocumentSecurityProxy` handles the "housekeeping" of verifying users and managing access.

The following sequence shows how the client's call to `openDocument` is intercepted by the proxy, which then decides whether to delegate the work to the real service or throw an exception.

```mermaid
sequenceDiagram
    participant Client
    participant Proxy as DocumentSecurityProxy
    participant Real as RealDocumentService

    Client ->> Proxy: openDocument(userId, documentId)
    alt is user ADMIN?
        Proxy ->> Real: openDocument(userId, documentId)
        Real -->> Proxy: void
        Proxy -->> Client: void
    else checkPermissions fails
        Proxy -->> Client: throw SecurityException
    end
```

### Types of Proxies

While our example focuses on a **Protection Proxy** (which controls access based on permissions), proxies are used in several ways:

*   **Protection Proxy:** Controls access to an object based on access rights (like our security check).
*   **Virtual Proxy (Lazy Loading):** Delays the creation or loading of a "heavy" object until it is actually needed. This saves memory and startup time by not initializing expensive resources upfront.

## Distinguishing Proxies from Other Patterns

It is common to confuse the Proxy with other structural patterns like Decorator or Adapter. Here is how to tell them apart:

*   **Proxy vs. Decorator:** Both wrap an object, but their *intent* differs. A **Decorator** adds new responsibilities or behaviors to an object (like adding a "border" to a window). A **Proxy** manages the lifecycle or controls access to the object without changing its core behavior.
*   **Proxy vs. Adapter:** An **Adapter** changes an interface to make two incompatible things work together. A **Proxy** implements the *same* interface as the subject so it can stand in for it transparently.

```mermaid
classDiagram
    class DocumentService {
        <<interface>>
        +openDocument(String userId, String documentId) void
    }
    class RealDocumentService {
        +openDocument(String userId, String documentId) void
    }
    class DocumentSecurityProxy {
        -DocumentService realService
        +openDocument(String userId, String documentId) void
    }
    RealDocumentService ..|> DocumentService : implements
    DocumentSecurityProxy ..|> DocumentService : implements
    DocumentSecurityProxy --> RealDocumentService : associates
```

## When to Use It, and When Not To

The Proxy pattern is highly effective when you need to manage complex lifecycles or enforce cross-cutting concerns like security, logging, or caching without polluting your business logic. It keeps your "Real Subject" clean and focused on its primary task.

However, avoid using a Proxy if:
*   **The overhead is too high:** Every proxy adds an extra layer of method calls. In performance-critical loops, this tiny overhead can accumulate.
*   **It complicates simple logic:** If the housekeeping task is a single line of code that rarely changes, creating a whole new class hierarchy might be over-engineering.

## Takeaways

- A Proxy implements the same interface as the real object to ensure transparency for the client.
- Use a Proxy to separate "housekeeping" (security, lazy loading) from core business logic.
- The primary goal is control and structural organization, not adding new features like a Decorator does.

## Usage

**Using the Proxy instead of the real service to enforce security automatically**

```java
DocumentService service = new DocumentSecurityProxy(new RealDocumentService());
service.openDocument("USER_123", "DOC_001");
```
