DEV Community

Charles Koffler
Charles Koffler

Posted on

🗂️ Clprolf Directory Explorer — When Breadth-First Becomes Intuitive

Everyone knows that exploring directories can quickly turn into a messy technical exercise:
loops, recursion, stacks, file filters… and code that loses all readability.

With Clprolf, clarity is built-in.
You don’t just write code — you design agents, workers, and models that reflect what really happens.
Let’s see how a simple directory explorer can become a beautifully structured program.


💡 1. The Concept

We want to:

  • explore all subdirectories of a given folder,
  • in breadth-first order (level by level),
  • assign each directory a hierarchical ID,
  • and display the results cleanly.

Instead of mixing logic, display, and data handling,
Clprolf invites us to split them into roles.

Component Declension Responsibility
Launcher @Worker_agent Starts the exploration
DirectoryExplorerImpl @Agent Performs the exploration
DirectoryExplorerWorkerImpl @Worker_agent Displays results
Directory @Model Represents one directory node
DirectoryExplorer / DirectoryExplorerWorker @Version_inh Define contracts between roles

⚙️ 2. The Launcher

The entry point is as simple as it looks.
It prepares the environment and delegates the job to the proper agent.

@Worker_agent
public class Launcher {

    public static void main(String[] args) {
        @With_compat Path path = Paths.get(args.length > 0 ? args[0] : System.getProperty("user.home"));
        try { path = path.toRealPath(LinkOption.NOFOLLOW_LINKS); } catch (Exception ignored) {}

        if (!Files.isDirectory(path)) {
            System.err.println("Not a directory: " + path);
            System.exit(1);
        }

        @With_compat DirectoryExplorer explorer = new DirectoryExplorerImpl();
        explorer.breadthFirstFolders(path);
    }
}
Enter fullscreen mode Exit fullscreen mode

It’s clear who does what:
this worker doesn’t explore — it simply launches the agent.


🧭 3. The Agent — DirectoryExplorerImpl

Here lies the real exploration logic.
The agent collaborates with a worker, manipulates a model, and manages a queue.

@Agent
public class DirectoryExplorerImpl implements @Contracts DirectoryExplorer {

    private @With_compat DirectoryExplorerWorker worker;

    public DirectoryExplorerImpl() {
        this.worker = new DirectoryExplorerWorkerImpl();
    }

    public void breadthFirstFolders(@With_compat Path directoryPath) {
        directoryPath = directoryPath.normalize().toAbsolutePath();

        @With_compat List<Directory> foldersList = new ArrayList<>();
        @With_compat Queue<Directory> directoryToExplore = new LinkedList<>();

        directoryToExplore.add(new Directory(directoryPath, List.of(0)));

        while (!directoryToExplore.isEmpty()) {
            Directory current = directoryToExplore.poll();
            foldersList.add(current);

            File[] files = current.getPath().toFile().listFiles();
            if (files != null) {
                int index = 0;
                for (File file : files) {
                    if (file.isDirectory()) {
                        List<Integer> newId = new ArrayList<>(current.getHierarchicalId());
                        newId.add(index);
                        directoryToExplore.add(new Directory(file.toPath().normalize().toAbsolutePath(), newId));
                        index++;
                    }
                }
            }
        }

        worker.displayResult(foldersList);
    }
}
Enter fullscreen mode Exit fullscreen mode

Every part is crystal clear:

  • The queue defines a breadth-first traversal.
  • Each directory receives its own hierarchical ID.
  • The worker takes care of presentation.

No recursion, no confusion.


👷 4. The Worker Agent

Responsible for showing the result, not for computing it.
Again, we separate doing from showing.

@Worker_agent
public class DirectoryExplorerWorkerImpl implements @Contracts DirectoryExplorerWorker {

    public void displayResult(List<Directory> foldersList) {
        for (Directory dir : foldersList) {
            String display = formatId(dir.getHierarchicalId()) + " : " + dir.getPath();
            System.out.println(display);
        }
    }

    private String formatId(List<Integer> id) {
        return "(" + String.join(", ", id.stream().map(String::valueOf).toArray(String[]::new)) + ")";
    }
}
Enter fullscreen mode Exit fullscreen mode

Simple, explicit, human-readable.


📦 5. The Model

A @Model in Clprolf is always clear:
it represents data, and nothing more.

@Model
public class Directory {
    private Path path;
    private List<Integer> hierarchicalId;

    public Directory(Path path, List<Integer> id) {
        this.path = path;
        this.hierarchicalId = id;
    }

    public Path getPath() { return path; }
    public List<Integer> getHierarchicalId() { return hierarchicalId; }
}
Enter fullscreen mode Exit fullscreen mode

No logic, no side effects. Just structure.


🤝 6. The Contracts

@Agent
@Version_inh
public interface DirectoryExplorer {
    void breadthFirstFolders(Path directoryPath);
}

@Worker_agent
@Version_inh
public interface DirectoryExplorerWorker {
    void displayResult(List<Directory> foldersList);
}
Enter fullscreen mode Exit fullscreen mode

Contracts make the collaboration explicit.
No hidden dependencies, no tight coupling — just clear communication.


🪶 7. Why it matters

Breadth-first exploration is only the example.
What matters here is how naturally the architecture expresses itself:

  • The launcher launches.
  • The agent explores.
  • The worker displays.
  • The model represents.
  • The contract binds.

Clprolf doesn’t just help you code — it helps you think.
The structure emerges from the intention.


✨ Final Thoughts

In classical Java, you might have written a single class doing everything.

In Clprolf, each role finds its natural place.
The result is simple, explicit, and readable — even for someone who never wrote Java before.

Clprolf brings clarity back into architecture,
and even the smallest utilities become examples of well-designed software.


Top comments (0)