Java/06advanced13 min

Clean code and SOLID

SOLID is five object-oriented design principles that lead to code that's easy to change and test. Combined with the fundamentals of clean code, they are the foundation of maintainable Java services at ProfessNet.

S — Single Responsibility

A class has a single reason to change. If it does too much (logic + persistence

  • email), split it.
// bad — the service does everything
class ReportService {
  void generate() { /* data + format + save + send */ }
}

// good — responsibilities separated
class ReportBuilder { Report build(ScanResult r) { ... } }
class ReportStorage { void save(Report r) { ... } }
class ReportNotifier { void notify(Report r) { ... } }

O — Open/Closed

Classes open for extension, closed for modification. We add new variants through new implementations, not through if/else growing endlessly.

interface Exporter { String export(Report r); }

class JsonExporter implements Exporter { /* ... */ }
class CsvExporter implements Exporter { /* ... */ }
// a new format = a new class, without touching the existing ones

L — Liskov Substitution

A subtype must work everywhere the base type does — without surprises. We don't override a method so that it breaks the contract (e.g. throws where the base one doesn't).

ProfessNet standard: if a subclass must throw UnsupportedOperationException from a base method, that's a sign the hierarchy is wrong — rethink the interfaces.

I — Interface Segregation

Small, specialized interfaces instead of a single "fat" one. A client should not depend on methods it does not use.

// bad — the interface forces you to implement everything
interface ProbeOps { void scan(); void export(); void archive(); }

// good — separated roles
interface Scannable { void scan(); }
interface Exportable { String export(); }

D — Dependency Inversion

We depend on abstractions, not on concretes. High-level classes don't create dependencies themselves — they receive them through the constructor.

// good — a dependency on the interface, injected
class InventoryService {
  private final ProbeRepository repository;   // an interface, not a concrete class

  InventoryService(ProbeRepository repository) {
    this.repository = repository;
  }
}

Clean-code practices

PrincipleWhat it means
Expressive namesrunScan instead of proc, no magic numbers
Short methodsone method = one task
No duplication (DRY)shared code extracted, not copied
Immutabilityrecord, final fields, no setters where possible
Few parametersmore than 3-4 → wrap in an object
// magic number → named constant
private static final int STALE_AFTER_MINUTES = 1440;

if (probe.minutesSinceLastSeen() > STALE_AFTER_MINUTES) {
  probe.markStale();
}

Tip: SOLID is guidance, not dogma. Don't introduce an abstraction "just in case" for a single implementation — that's YAGNI. Refactor when a second implementation appears or a real need to change arises.


The five SOLID principles plus the discipline of clean code give services you change without fear and test without gymnastics. It's an investment that pays off with every new requirement.