Diagram of the adapter pattern
The Adapter pattern is a wrapper class around an external component. The wrapper class adapts the external component to match how the rest of the program behaves so the program doesn't know it is dealing with an external component. The wrapper class is responsible for initializing and disposing of the external component, as well as adapting procedure calls, arguments, and error handling.
A function can also act as an adapter, where the function adapts the calling conventions of the external component to the conventions of the program. One common use of adapter functions is to hide differences in external libraries that arise from different implementations, such as the C library as it appears on different operating systems.
How to Use
In the Adapter pattern example, the DataReaderWriter class is the Adapter. It hides the details of getting data into and out of some external entity. In this example, some data is created, the DataReaderWriter class is used to write the data out (to the external component), followed by using the DataReaderWriter class to read the data back.
For all programming languages, the "external entity" is the Adapter_BackEnd DLL. This DLL is written in C and exposes its interface as C-style functions. The functions are used to access one of several memory blocks, and the access is in discrete 32-bit values. Error codes are returned from each function to indicate success or failure.
In addition, each language needs to adapt to the external DLL, thus demonstrating that language's capabilities of connecting to an external dynamic library. Naturally, the C and C++ examples can call directly into the DLL, while the other languages need to go through a translation layer provided by those languages (C# uses the Platform/Invoke or P/Invoke layer, Python uses the ctypes package, and rust uses Foreign Function Interface or FFI).
C++
void Adapter_Exercise()
{
std::cout << std::endl;
std::cout << "Adapter Exercise" << std::endl;
try
{
DataReaderWriter dataReaderWriter(DataReaderWriter::Memory_Block_0);
uint32_t memoryBlockSize = dataReaderWriter.GetMemoryBlockByteSize();
std::vector<uint8_t> readData = dataReaderWriter.Read(0, memoryBlockSize);
std::string dataDump =
dataReaderWriter.BufferToString(readData, memoryBlockSize, 2);
std::cout << " Initial memory block contents:" << std::endl;
std::cout << dataDump << std::endl;
uint32_t dataSize = 16;
int byteOffset = 41;
std::vector<uint8_t> writeData(dataSize);
for (uint32_t index = 0; index < dataSize; ++index)
{
writeData[index] = static_cast<uint8_t>(index+1);
}
dataDump = dataReaderWriter.BufferToString(writeData, dataSize, 2);
std::cout << " Data to be written to memory block:" << std::endl;
std::cout << dataDump << std::endl;
std::cout << " Writing data to byte offset " << byteOffset << "..." << std::endl;
dataReaderWriter.Write(byteOffset, writeData, dataSize);
std::cout << " Reading back the memory block..." << std::endl;
readData = dataReaderWriter.Read(0, memoryBlockSize);
std::cout << std::endl;
dataDump = dataReaderWriter.BufferToString(readData, memoryBlockSize, 2);
std::cout << " Current memory block contents:" << std::endl;
std::cout << dataDump << std::endl;
}
catch (DataReaderWriterInitException& e)
{
std::cout << "Error with startup or shutdown! " << e.what()
<< std::endl;
}
catch (DataReaderWriterException& e)
{
std::cout << "Error with reading or writing! " << e.what()
<< std::endl;
}
std::cout << " Done." << std::endl;
}
C#
public void Run()
{
Console.WriteLine();
Console.WriteLine("Adapter Exercise");
try
{
using (var dataReaderWriter = new DataReaderWriter(DataReaderWriter.MemoryBlockNumber.Memory_Block_0))
{
uint memoryBlockSize = dataReaderWriter.MemoryBlockByteSize;
byte[] readData = dataReaderWriter.Read(0, memoryBlockSize);
string dataDump = dataReaderWriter.BufferToString(readData, memoryBlockSize, 2);
Console.WriteLine(" Initial memory block contents:{0}{1}", Environment.NewLine, dataDump);
uint dataSize = 16;
int byteOffset = 41;
byte[] writeData = new byte[dataSize];
for (int index = 0; index < dataSize; ++index)
{
writeData[index] = (byte)(index + 1);
}
dataDump = dataReaderWriter.BufferToString(writeData, dataSize, 2);
Console.WriteLine(" Data to be written to memory block:{0}{1}", Environment.NewLine, dataDump);
Console.WriteLine(" Writing data to byte offset {0}...", byteOffset);
dataReaderWriter.Write(byteOffset, writeData, dataSize);
Console.WriteLine(" Reading back the memory block...");
readData = dataReaderWriter.Read(0, memoryBlockSize);
Console.WriteLine();
dataDump = dataReaderWriter.BufferToString(readData, memoryBlockSize, 2);
Console.WriteLine(" Current memory block contents:{0}{1}", Environment.NewLine, dataDump);
}
}
catch (DataReaderWriterInitException e)
{
Console.WriteLine("Error with startup or shutdown! {0}", e.Message);
}
catch (DataReaderWriterException e)
{
Console.WriteLine("Error with reading or writing! {0}", e.Message);
}
Console.WriteLine(" Done.");
}
Python
def Adapter_Exercise():
print()
print("Adapter Exercise")
try:
with DataReaderWriter(MemoryBlock.MEMORY_BLOCK_0) as dataReaderWriter:
memoryBlockSize = dataReaderWriter.MemoryBlockByteSize
dataSize = 16
byteOffset = 41
writeData = []
for index in range(0, dataSize):
writeData.append(index + 1)
readData = dataReaderWriter.Read(0, memoryBlockSize)
dataDump = dataReaderWriter.BufferToString(readData, memoryBlockSize, 2)
print(" Initial memory block contents:")
print(dataDump)
dataDump = dataReaderWriter.BufferToString(writeData, len(writeData), 2)
print(" Data to be written to memory block:")
print(dataDump)
print(" Writing data to byte offset {}...".format(byteOffset))
dataReaderWriter.Write(byteOffset, writeData, len(writeData))
print(" Reading back the memory block...")
readData = dataReaderWriter.Read(0, memoryBlockSize)
print()
dataDump = dataReaderWriter.BufferToString(readData, memoryBlockSize, 2)
print(" Current memory block contents:")
print(dataDump)
except DataReaderWriterInitException as ex:
print("Error with startup or shutdown! {0}".format(ex))
except DataReaderWriterException as ex:
print("Error with reading or writing! {0}".format(ex))
print(" Done.")
C
void Adapter_Exercise(void)
{
printf("\nAdapter_Exercise\n");
int dataHandle = -1;
{
uint8_t writeData[16] = { 0 };
uint8_t readData[128] = { 0 };
int bufferOffset = 41;
uint32_t dataSize =
_countof(writeData);
int bytesWritten = 0;
int bytesRead = 0;
int memoryBlockSize = 0;
const char* hexdump = NULL;
{
{
if (hexdump != NULL)
{
printf(" Initial memory block contents:\n");
printf("%s\n", hexdump);
for (uint32_t index = 0; index < dataSize; index++)
{
writeData[index] = (uint8_t)(index + 1);
}
if (hexdump != NULL)
{
printf(" Data to be written to memory block:\n");
printf("%s\n", hexdump);
printf(" Writing data to byte offset %d\n", bufferOffset);
{
printf(" Reading back the memory block...\n");
{
if (hexdump != NULL)
{
printf(" Current memory block contents:\n");
printf("%s\n", hexdump);
}
else
{
}
}
else
{
}
}
else
{
}
}
else
{
}
}
else
{
}
}
else
{
}
}
else
{
}
{
}
}
else
{
}
printf(" Done.\n");
}
bool Adapter_CloseMemory(int dataHandle)
Closes a memory block from access.
bool Adapter_ReadMemory(int dataHandle, int byteOffset, uint8_t *buffer, int maxBytes, int *bytesRead)
Read a requested number of bytes from the memory block associated with the given handle.
bool Adapter_OpenMemory(MemoryBlockNumber blockNumber, int *dataHandle)
Open a memory block for access.
bool Adapter_WriteMemory(int dataHandle, int byteOffset, const uint8_t *buffer, int maxBytes, int *bytesWritten)
Write a requested number of bytes to the memory block associated with the given handle.
bool Adapter_GetMemorySize(int dataHandle, int *sizeInBytes)
Retrieve the number of bytes in the memory block associated with the specified data handle.
const char * Adapter_BufferToString(const uint8_t *data, uint32_t maxBytes, int indent)
Convert the specified data up to the specified number of bytes into a string by performing a "hex dum...
const char * Adapter_GetLastErrorMessage(void)
Retrieve a string describing the last error that occurred in the Adapter.
@ Memory_Block_0
First block.
Rust
(Apologies. Doxygen does not understand Rust syntax and therefore cannot colorize the code.)
pub fn adapter_exercise() -> Result<(), String> {
println!("");
println!("Adapter Exercise");
let mut reader_writer = DataReaderWriter::new(MemoryBlockNumber::MemoryBlock0);
reader_writer.open()?;
let memory_block_size = reader_writer.memory_block_byte_size;
let mut read_data = reader_writer.read(0, memory_block_size)?;
let mut hex_dump = adapter_buffer_to_string(&read_data, 2)?;
println!(" Initial memory block contents:");
println!("{hex_dump}");
let data_size = 16;
let buffer_offset = 41;
let mut write_data: Vec<u8> = vec![0;data_size];
for index in 0..write_data.len() {
write_data[index] = (index + 1) as u8;
}
hex_dump = adapter_buffer_to_string(&write_data, 2)?;
println!(" Data to be written to memory block:");
println!("{hex_dump}");
println!(" Writing data to byte offset {buffer_offset}");
let _ = reader_writer.write(buffer_offset, &write_data)?;
println!(" Reading back the memory block...");
read_data = reader_writer.read(0, memory_block_size)?;
println!();
hex_dump = adapter_buffer_to_string(&read_data, 2)?;
println!(" Current memory block contents:");
println!("{hex_dump}");
reader_writer.close()?;
println!(" Done.");
Ok(())
}
See Also