Exercise: Proxy (Remote Thermometer)¶
Network Communication¶
Consider a Sensor
instance that is reachable over a network. That
sensor sure lives outside the address space of a possible client, so a
network protocol must be invented to ask it for its value.
In this exercise, the “network” is represented by the communication between a client thread and a “remote” thread in the same address space, expressed in the following class definitions,
#pragma once
#include "queue.h"
#include <string>
#include <thread>
#include <future>
#include <memory>
namespace jf::utilities
{
class ServerThread
{
public:
// in the remote thread, receives the "request", does with it
// whatever needs to be done, and returns a "response" that is
// sent back across the "network"
class RemoteAdapter
{
public:
virtual ~RemoteAdapter() {}
virtual std::string execute(const std::string& request) = 0;
};
public:
ServerThread(RemoteAdapter*);
~ServerThread();
// clients send a "request" packet across the "network". on the
// remote side, that request is handed over to a RemoteAdapter
// instance. that instance's return value, the "response" is then
// sent back and returned.
std::string write(const std::string& request);
private:
struct Packet
{
std::string request;
std::promise<std::string> response_promise;
};
RemoteAdapter* _adapter;
MTQueue<Packet*> _queue;
std::thread _thread;
};
}
Requirements¶
Use the “remote” form of the Proxy design pattern to
implement such a client, in the form of yet another Sensor
implementation that fulfills the following requirements.
#include <gtest/gtest.h>
#include <server-thread.h>
#include <sensor-const.h>
#include <sensor-remote.h>
#include <sensor-remote-adapter.h>
struct proxy_remote_suite : ::testing::Test // <--- fixture: server thread and sensor setup
{
proxy_remote_suite()
: _cs(42.0),
_adapter(&_cs),
server(&_adapter)
{}
ConstantSensor _cs;
RemoteSensorAdapter _adapter;
jf::utilities::ServerThread server;
};
TEST_F(proxy_remote_suite, protocol)
{
RemoteSensor rs(&server);
ASSERT_FLOAT_EQ(rs.get_temperature(), 42.0);
}
TEST_F(proxy_remote_suite, remote_sensor__is_a__sensor)
{
RemoteSensor rs(&server);
Sensor* s = &rs;
ASSERT_FLOAT_EQ(s->get_temperature(), 42.0);
}
Implementation Hints¶
The requirements (a.k.a. unit tests) above push you towards an implementation that takes place in the following class diagram. It’s the green parts that need to be implemented.
Note that the RemoteAdapter
is a specialized form of the
Adapter pattern: implementations of the
RemoteAdapter
interface are supposed to adapt to something by
implementing a protocol onto somthing that is already there. This is
not a requirement though - a simplistic implementation may not only
implement the protocol, but also the logic behind it (effectively
turning it into an example of Command)