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.
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.
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*/
...
)
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?
I might be missing something, but based on my current understanding of the problem I would do something like:
public class Watcher<T> {
public Watcher(Resource<T> res, Diagnosis<T> diag) {
...
}
}
and
public class HttpResource implements Resource<HttpFacts> {
...
}
That would guarantee that the Watcher and the Diagnosis are always of compatible types, and still allow you to override either.
I might very well be missing something crucial though.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.