In my current project, I am trying to implement an environment to perform simulations of different workflows in a range of programs, websites, and mobile applications. These simulation subjects can run in different environments, such as inside a docker container, inside a virtual machine, or on a remote physical node, controlled over some protocol.
I am interested in the reaction of these programs to different inputs and changing conditions, such as certain user actions, or constricted network access. These reactions should be recorded for later evaluation. To maximize code reuse among the different measurement cases, I devised the following scheme: First, an environment is selected. This defines which application is used for testing i.e. a website or an app, and where it is running, i.e. Docker or an actual device. The next step is the selection of applicable logging protocols, i.e. a container running tcpdump for Docker, or an Appium server connecting to the device. Each logging infrastructure is queried whether or not it supports logging in the specified environment, and applied accordingly.
This is where my problem lies. A tcpdump container will support everything that runs in a container but needs access to the Docker connection. It should get this from the environment, but for this, it has to cast the environment to a DockerEnvironment
(or something similar). Even avoiding this cast, the logger needs to check if it even supports the environment or not. This could be done with Enums denoting the type of environment, and a map with the appropriate information stored as Object, but this seems to me as just avoiding to call the devil by its name.
I looked at this question and thought the reasoning might apply here, but I still feel like there should be a better way that I am not seeing, an idiom or pattern, or maybe my approach might just be flawed to start with.
Some code for reference here:
interface Environment {}
interface DockerEnvironment {
public String getDockerURL();
}
interface AppiumEnvironment {
public String getAppiumURL();
}
class WebsiteInDockerEnvironment implements DockerEnvironment, AppiumEnvironment {
public String getDockerURL() {
return "unix:///var/run/docker.sock";
}
public String getAppiumURL() {
return "http://localhost:6060";
}
}
interface Logger {
boolean doesSupport(Environment environment);
}
class DockerTcpDumpLogger implements Logger {
public DockerTcpDumpLogger(DockerEnvironment environment) {
connectToDocker(environment.getDockerURL());
}
@Override
public boolean doesSupport(Environment environment) {
return environment instanceof DockerEnvironment;
}
}
The idea here is that further implementations of Environment
may be monitored by the DockerTcpDumpLogger
.
EDIT: Although optimally I would be looking for a general "best practice" on this or similar situation, it might help to know that I am working with Java.
1 Answer 1
You could do something like this:
interface Logger {
// Logger stuff
}
interface Environment {
Logger getLogger();
}
class WebsiteInDockerEnvironment implements Environment {
public String getDockerURL() {
return "unix:///var/run/docker.sock";
}
public String getAppiumURL() {
return "http://localhost:6060";
}
@Override
public DockerTcpDumpLogger getLogger() {
return new DockerTcpDumpLogger(this);
}
}
class DockerTcpDumpLogger implements Logger {
public DockerTcpDumpLogger(DockerEnvironment environment) {
connectToDocker(environment.getDockerURL());
}
@Override
public boolean doesSupport(Environment environment) {
return environment instanceof DockerEnvironment;
}
}
I removed the DockerEnvironment
and AppiumEnvironment
interfaces because they don't seem necessary, at least for this part. They also seem too specific but you might need them for other reasons. I would avoid creating interfaces just to have an interface.
Explore related questions
See similar questions with these tags.
DockerEnvironment
class (or related classes). It's hard to give a more detailed answer without some classes to refer to.