I am currently working on a network health monitoring system that allows to watch multiple kind of resources, diagnose based on facts and react according to the diagnostic.
Initial thoughs...
My initial thoughts was to have an abstract Watcher
class, which can be configured with a Diagnosis
object (which is responsible for producing a Diagnostic
based on Facts
) and a list of DiagnosticHandler
objects. This Watcher
class would have a abstract protected Facts _check()
template method overridden by subclasses such as HttpWatcher
or MemoryWatcher
.
Facts
is a marker interface and represents what can be observed to generate a Diagnostic
. For example, the _check
method of HttpWatcher
would return HttpFacts
which is basically the http response details.
Here's what the HttpWatcher
constructor may look like:
public HttpWatcher(
Duration interval,
Diagnosis<HttpFacts> diagnosis,
List<DiagnosticHandler> handlers,
URL url
)
As we can see, since this class's _check
method returns HttpFacts
, it also only allows Diagnosis
strategies that can diagnose based on HttpFacts
and that is what we want. This way it is possible for the compiler to complain if an incompatible Diagnosis
strategy is used.
Missing concept?
However, there is something that bother's me with this design because I noticed that in terms of behavior, the only thing that Watcher
subclasses overrides is the _check
method. Even worse, the _check
algorithm could not be reused somewhere else. This made me thinking that perhaps I was missing a Resource
concept in my design that could encapsulate the algorithm to retrieve Facts
rather than having to subclass Watcher
.
This being said, I would only need a concrete Watcher
class which would be configured with a Resource
, a Diagnosis<T extends Facts>
and a List<DiagnosticHandler>
.
This design makes much more sense to me, but then I would lose the type-safety that prevents incompatible strategies to be used together like below:
new Watcher(
new Duration(...),
new HttpResource(...),
new SimpleMemoryDiagnosis(...), /*<- incompatible with HttpResource*/
...
)
Solution?
It's been a while since I programmed in a strongly-typed language and I want to make sure that I use the types to my advantage, but at the same time I do not want my design to suffer.
One idea that I had was to create a new class such as HttpWatchedResource
which would encapsulate HttpResource
and Diagnosis<HttpFacts>
objects.
Something like:
public abstract class WatchedResource {
private final Resource resource;
private final Diagnosis diagnosis;
public WatchedResource(Resource resource, Diagnosis diagnosis) {
//null checks
this.resource = resource;
this.diagnosis = diagnosis;
}
//called by Watcher
public final Diagnostic checkHealth() {
return diagnosis.diagnose(resource.facts());
}
}
public final class HttpWatchedResource {
public HttpWatchedResource(HttpResource resource, Diagnosis<HttpFacts> diagnosis) {
super(resource, diagnosis);
}
}
The Watcher
constructor would then look like:
public Watcher(
Duration interval,
WatchedResource resource,
List<DiagnosticHandler> handlers,
URL url
)
I would like to know if there's a widely adopted pattern used when an object is composed of multiple strategies that could potentially be incompatible and/or if I'm heading in the right direction with the proposed solution?