Design Pattern Examples
Overview of object-oriented design patterns
Adapter Pattern

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

Links to the DataReaderWriter class (or functions)
C++ C# Python C
DataReaderWriter class DataReaderWriter class DataReaderWriter class Adapter_OpenMemory()
Adapter_CloseMemory()
Adapter_GetMemorySize()
Adapter_ReadMemory()
Adapter_WriteMemory()
Adapter_BufferToString()
Adapter_GetLastErrorMessage()

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;
// Create the data to be written
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);
}
// Display the data to be written
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;
// Write the data to the external component
dataReaderWriter.Write(byteOffset, writeData, dataSize);
std::cout << " Reading back the memory block..." << std::endl;
// Read the data from the external component
readData = dataReaderWriter.Read(0, memoryBlockSize);
std::cout << std::endl;
// Display the data read back.
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
{
// Will call Dispose() automatically when exiting the using block
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);
// Create the data to be written
uint dataSize = 16;
int byteOffset = 41;
byte[] writeData = new byte[dataSize];
for (int index = 0; index < dataSize; ++index)
{
writeData[index] = (byte)(index + 1);
}
// Display the data to be written
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);
// Write the data to the external component
dataReaderWriter.Write(byteOffset, writeData, dataSize);
Console.WriteLine(" Reading back the memory block...");
// Read the data from the external component
readData = dataReaderWriter.Read(0, memoryBlockSize);
Console.WriteLine();
// Display the data read back. Should be the same as was written.
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
# Create the data to be written
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)
# Display the data to be written
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))
# Write the data to the external component
dataReaderWriter.Write(byteOffset, writeData, len(writeData))
print(" Reading back the memory block...")
# Read the data from the external component
readData = dataReaderWriter.Read(0, memoryBlockSize)
print()
# Display the data read back. Should be the same as was written.
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 (Adapter_GetMemorySize(dataHandle, &memoryBlockSize))
{
if (Adapter_ReadMemory(dataHandle, 0, readData, memoryBlockSize, &bytesRead))
{
hexdump = Adapter_BufferToString(readData, bytesRead, 2);
if (hexdump != NULL)
{
printf(" Initial memory block contents:\n");
printf("%s\n", hexdump);
// Create the data to be written
for (uint32_t index = 0; index < dataSize; index++)
{
writeData[index] = (uint8_t)(index + 1);
}
// Display the data to be written
hexdump = Adapter_BufferToString(writeData, dataSize, 2);
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);
if (Adapter_WriteMemory(dataHandle, bufferOffset, writeData, dataSize, &bytesWritten))
{
printf(" Reading back the memory block...\n");
if (Adapter_ReadMemory(dataHandle, 0, readData, memoryBlockSize, &bytesRead))
{
hexdump = Adapter_BufferToString(readData, bytesRead, 2);
if (hexdump != NULL)
{
printf(" Current memory block contents:\n");
printf("%s\n", hexdump);
}
else
{
printf(" %s\n", Adapter_GetLastErrorMessage());
}
}
else
{
printf(" %s\n", Adapter_GetLastErrorMessage());
}
}
else
{
printf(" %s\n", Adapter_GetLastErrorMessage());
}
}
else
{
printf(" %s\n", Adapter_GetLastErrorMessage());
}
}
else
{
printf(" %s\n", Adapter_GetLastErrorMessage());
}
}
else
{
printf(" %s\n", Adapter_GetLastErrorMessage());
}
}
else
{
printf(" %s\n", Adapter_GetLastErrorMessage());
}
if (!Adapter_CloseMemory(dataHandle))
{
printf(" %s\n", Adapter_GetLastErrorMessage());
}
}
else
{
printf(" %s\n", Adapter_GetLastErrorMessage());
}
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.
#define _countof(w)

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