Adapter
The adapter aims to be a converter between two specific interfaces: one belonging to “our” side of the application, the other to the library or other tool we need to interact with. This is very important, because it enables us to delay decisions, as the choice of a spcific implementation of the adapter becomes less important due to the fact that we are at that point using the adapter itself as our communication contract.
Usage
Often used in combo with other patterns, such as [Inversion of Control] and several Factories
Examples
Typical example of this pattern in a “pure” form is the Driver. Whether it is for printing or for database access, the API exposed by the common development systems just require the connection to the adapter and, with some kind of Factory or repository we get the specific implementation of what we need at the moment, based on the configuration of the system we have.
Specimens
15 implementationsThe Adapter pattern converts the interface of a class into another interface clients expect. It lets classes work together that couldn’t otherwise because of incompatible interfaces. In this Dart example, we have a LegacyPrinter with a printLegacy() method and a ModernPrinter interface expecting printModern(). The PrinterAdapter adapts the LegacyPrinter to fulfill the ModernPrinter interface, enabling a ModernPrintingService to work with it. This follows Dart’s use of interfaces (implemented by implements) and class composition to achieve adaptation.
// The target interface that clients can work with.
abstract class ModernPrinter {
void printModern(String text);
}
// The legacy class with an incompatible interface.
class LegacyPrinter {
void printLegacy(String text) {
print('Legacy Printer: Printing - $text');
}
}
// The adapter class that makes the legacy class compatible.
class PrinterAdapter implements ModernPrinter {
final LegacyPrinter legacyPrinter;
PrinterAdapter(this.legacyPrinter);
@override
void printModern(String text) {
legacyPrinter.printLegacy(text);
}
}
// Client code that uses the modern interface
class ModernPrintingService {
void printDocument(ModernPrinter printer, String document) {
print('Modern Printing Service: Preparing document...');
printer.printModern(document);
print('Modern Printing Service: Document printed.');
}
}
void main() {
final legacyPrinter = LegacyPrinter();
final adapter = PrinterAdapter(legacyPrinter);
final printingService = ModernPrintingService();
printingService.printDocument(adapter, 'Hello, Adapted World!');
}
The Adapter pattern converts the interface of a class into another interface clients expect. This enables classes with incompatible interfaces to collaborate. Here, we adapt a CelsiusTemperature class to provide temperature information in Fahrenheit, which is the expected format for a WeatherReport system. We achieve this with a CelsiusToFahrenheitAdapter that implements the FahrenheitTemperature trait but internally uses the CelsiusTemperature class. This implementation utilizes Scala’s trait-based approach for defining interfaces and leverages implicit conversions for seamless integration, common practices in Scala development.
// Interface for Fahrenheit temperature
trait FahrenheitTemperature {
def getTemperature: Double
}
// Class representing Celsius temperature
class CelsiusTemperature(private val celsius: Double) {
def getCelsius: Double = celsius
}
// Adapter to convert Celsius to Fahrenheit
class CelsiusToFahrenheitAdapter(celsiusTemp: CelsiusTemperature) extends FahrenheitTemperature {
override def getTemperature: Double = (celsiusTemp.getCelsius * 9 / 5) + 32
}
// Client code - Weather Report system expects Fahrenheit
class WeatherReport(temp: FahrenheitTemperature) {
def report(): String = s"The temperature is ${temp.getTemperature}°F"
}
// Example usage
object Main {
def main(args: Array[String]): Unit = {
val celsiusTemperature = new CelsiusTemperature(25.0)
val fahrenheitAdapter = new CelsiusToFahrenheitAdapter(celsiusTemperature)
val weatherReport = new WeatherReport(fahrenheitAdapter)
println(weatherReport.report()) // Output: The temperature is 77.0°F
}
}
The Adapter pattern converts the interface of a class into another interface clients expect. This allows classes with incompatible interfaces to collaborate. Here, we adapt a legacy LegacyRectangle class (providing area calculation in a specific, older format) to the Shape interface (a more modern, consistent interface). The RectangleAdapter implements the Shape interface but internally uses the LegacyRectangle to provide the functionality. This implementation uses PHP’s interface system for clear contracts and dependency injection, which aligns with common PHP practices for loose coupling and testability.
<?php
// The target interface - what the client expects
interface Shape
{
public function getArea(): float;
}
// The legacy class with an incompatible interface
class LegacyRectangle
{
public function __construct(private float $width, private float $height) {}
public function calculateArea(): float
{
return $this->width * $this->height;
}
}
// The adapter
class RectangleAdapter implements Shape
{
public function __construct(private LegacyRectangle $legacyRectangle) {}
public function getArea(): float
{
return $this->legacyRectangle->calculateArea();
}
}
// Client code
class AreaCalculator
{
public function __construct(Shape $shape) {
$this->shape = $shape;
}
public function calculate(): float {
return $this->shape->getArea();
}
}
// Usage
$legacyRectangle = new LegacyRectangle(5, 10);
$adapter = new RectangleAdapter($legacyRectangle);
$calculator = new AreaCalculator($adapter);
echo "Area: " . $calculator->calculate() . PHP_EOL;
The Adapter pattern converts the interface of a class into another interface clients expect. This allows classes with incompatible interfaces to collaborate. Our example adapts a LegacyRectangle class with a different area calculation method to the GeometricShape interface which requires a standardized area method. We achieve this with an RectangleAdapter class that wraps the LegacyRectangle and provides the required area method, calling the legacy class’s method internally. This implementation leans into Ruby’s duck typing, where method calls are executed if an object responds to them, rather than strict interface enforcement.
# GeometricShape interface
module GeometricShape
def area
raise NotImplementedError
end
end
# Legacy class with incompatible interface
class LegacyRectangle
attr_reader :width, :height
def initialize(width, height)
@width = width
@height = height
end
def calculate_area
@width * @height
end
end
# Adapter class
class RectangleAdapter
include GeometricShape
def initialize(legacy_rectangle)
@legacy_rectangle = legacy_rectangle
end
def area
@legacy_rectangle.calculate_area
end
end
# Client code
legacy_rect = LegacyRectangle.new(5, 10)
adapter = RectangleAdapter.new(legacy_rect)
puts "Area of legacy rectangle (via adapter): #{adapter.area}"
# Example using another object that directly implements GeometricShape
class Square
include GeometricShape
def initialize(side)
@side = side
end
def area
@side * @side
end
end
square = Square.new(4)
puts "Area of square: #{square.area}"
The Adapter pattern converts the interface of a class into another interface clients expect, letting incompatible classes work together. It’s a structural pattern acting as a wrapper. In this Swift example, we have a JSONData provider that needs to integrate with a UserData display system expecting Dictionary inputs. We create an JSONDataAdapter that transforms the JSONData into a Dictionary compatible with the UserData protocol. This uses Swift’s protocol-oriented programming, making the adapter a natural fit that avoids modifying the original JSONData class.
// Target interface
protocol UserData {
func getDisplayName() -> String
func getEmail() -> String
}
// Adaptee (Existing data source)
struct JSONData {
let jsonData: [String: Any]
func getData() -> [String: Any] {
return jsonData
}
}
// Adapter
class JSONDataAdapter: UserData {
private let jsonData: JSONData
init(jsonData: JSONData) {
self.jsonData = jsonData
}
func getDisplayName() -> String {
return jsonData.getData()["fullName"] as? String ?? "Unknown"
}
func getEmail() -> String {
return jsonData.getData()["emailAddress"] as? String ?? "No email"
}
}
// Client
func displayUserData(userData: UserData) {
print("Display Name: \(userData.getDisplayName())")
print("Email: \(userData.getEmail())")
}
// Example Usage
let json = JSONData(jsonData: ["fullName": "Alice Smith", "emailAddress": "alice@example.com"])
let adapter = JSONDataAdapter(jsonData: json)
displayUserData(userData: adapter)
The Adapter pattern converts the interface of a class into another interface clients expect. It lets classes work together that couldn’t otherwise because of incompatible interfaces. This implementation uses Kotlin’s interface delegation and object expressions to adapt a LegacyPrinter class (providing a specific printing method) to a common Printer interface. This design keeps the code concise and leverages Kotlin’s functional capabilities. The LegacyPrinterAdapter effectively “wraps” the LegacyPrinter and provides a standardized interface for modern use. Kotlin’s by keyword simplifies the adaptation process.
// Target interface: What clients expect
interface Printer {
fun print(document: String)
}
// Adaptee: The legacy class with a different interface
class LegacyPrinter {
fun oldPrint(data: String) {
println("Legacy Printing: $data")
}
}
// Adapter: Converts LegacyPrinter to Printer
class LegacyPrinterAdapter(private val legacyPrinter: LegacyPrinter) : Printer by legacyPrinter {
override fun print(document: String){
legacyPrinter.oldPrint(document)
}
}
// Client code
fun main() {
val legacyPrinter = LegacyPrinter()
val adapter = LegacyPrinterAdapter(legacyPrinter)
adapter.print("Hello, Adapted World!")
}
The Adapter pattern converts the interface of a class into another interface clients expect. This allows classes with incompatible interfaces to work together. Here, we adapt a CelsiusTemperature struct (legacy system) to be usable with functions expecting FahrenheitTemperature. We define a CelsiusToFahrenheitAdapter which implements the FahrenheitTemperature trait by internally holding a CelsiusTemperature instance and converting on demand. This adheres to Rust’s strong typing and trait-based polymorphism, providing a clean and type-safe way to integrate differing interfaces without modifying the original CelsiusTemperature struct.
// Legacy system: Celsius temperature representation
struct CelsiusTemperature {
temperature: f64,
}
impl CelsiusTemperature {
fn new(temp: f64) -> Self {
CelsiusTemperature { temperature: temp }
}
fn get_celsius(&self) -> f64 {
self.temperature
}
}
// Target interface: Fahrenheit temperature representation
trait FahrenheitTemperature {
fn get_fahrenheit(&self) -> f64;
}
// Adapter: Converts Celsius to Fahrenheit
struct CelsiusToFahrenheitAdapter {
celsius: CelsiusTemperature,
}
impl CelsiusToFahrenheitAdapter {
fn new(celsius: CelsiusTemperature) -> Self {
CelsiusToFahrenheitAdapter { celsius }
}
}
impl FahrenheitTemperature for CelsiusToFahrenheitAdapter {
fn get_fahrenheit(&self) -> f64 {
(self.celsius.get_celsius() * 9.0 / 5.0) + 32.0
}
}
// Client code expecting Fahrenheit
fn print_temperature_fahrenheit(temp: &dyn FahrenheitTemperature) {
println!("Temperature in Fahrenheit: {}", temp.get_fahrenheit());
}
fn main() {
let celsius_temp = CelsiusTemperature::new(25.0);
// Use the adapter to work with the Celsius temperature
let fahrenheit_adapter = CelsiusToFahrenheitAdapter::new(celsius_temp);
print_temperature_fahrenheit(&fahrenheit_adapter);
}
The Adapter pattern converts the interface of a class into another interface clients expect. It lets classes work together that couldn’t otherwise because of incompatible interfaces. This is achieved by creating a wrapper class (the Adapter) that translates calls to the adaptee’s interface into calls the client understands.
The Go example demonstrates adapting a JSONData struct (the adaptee) to a DataAdapter interface. The JSONDataAdapter struct implements the DataAdapter interface, taking a JSONData instance and translating its methods to fit the expected interface. This approach leverages Go’s interface implementation for loose coupling and aligns with its compositional style, avoiding inheritance-based solutions. The use of methods directly bound to the struct are also idiomatic.
// DataAdapter defines the interface clients expect.
type DataAdapter interface {
GetData(int) string
SetData(int, string)
}
// JSONData is the existing data structure (adaptee).
type JSONData struct {
data map[int]string
}
// NewJSONData creates a new JSONData instance.
func NewJSONData() *JSONData {
return &JSONData{data: make(map[int]string)}
}
// GetJSONData retrieves data from the JSONData struct.
func (j *JSONData) GetJSONData(key int) string {
return j.data[key]
}
// SetJSONData sets data within the JSONData struct.
func (j *JSONData) SetJSONData(key int, value string) {
j.data[key] = value
}
// JSONDataAdapter adapts JSONData to the DataAdapter interface.
type JSONDataAdapter struct {
jsonData *JSONData
}
// NewJSONDataAdapter creates a new JSONDataAdapter instance.
func NewJSONDataAdapter(jsonData *JSONData) *JSONDataAdapter {
return &JSONDataAdapter{jsonData: jsonData}
}
// GetData implements the DataAdapter interface, translating the call to GetJSONData.
func (j *JSONDataAdapter) GetData(key int) string {
return j.jsonData.GetJSONData(key)
}
// SetData implements the DataAdapter interface, translating the call to SetJSONData.
func (j *JSONDataAdapter) SetData(key int, value string) {
j.jsonData.SetJSONData(key, value)
}
// Example Usage
func main() {
jsonData := NewJSONData()
jsonData.SetJSONData(1, "example data")
adapter := NewJSONDataAdapter(jsonData)
retrievedData := adapter.GetData(1)
println(retrievedData) // Output: example data
}
The Adapter pattern converts the interface of a class into another interface clients expect. It lets classes work together that couldn’t otherwise because of incompatible interfaces. This is achieved by creating a wrapper class (the adapter) that translates calls to the adaptee’s interface into calls that the client understands. In C, this is often done using function pointers, effectively creating a new interface that calls the old one. The example adapts a specific temperature reporting system (Celsius) to a more general one (Kelvin), allowing clients expecting Kelvin to work with the Celsius system without modification. This is idiomatic C because it’s type-safe utilizing function pointers for interface mapping.
#include <stdio.h>
// Existing system (Adaptee) - reports temperature in Celsius
typedef struct {
float (*get_celsius)(void);
} CelsiusReporter;
float get_celsius_impl(void) {
// Simulate reading from a Celsius sensor
return 25.0;
}
// Target interface - reports temperature in Kelvin
typedef struct {
float (*get_kelvin)(void);
} KelvinReporter;
// Adapter - adapts CelsiusReporter to KelvinReporter
typedef struct {
CelsiusReporter* celsius_reporter;
} KelvinAdapter;
KelvinAdapter* create_kelvin_adapter(CelsiusReporter* celsius_reporter) {
KelvinAdapter* adapter = (KelvinAdapter*)malloc(sizeof(KelvinAdapter));
adapter->celsius_reporter = celsius_reporter;
return adapter;
}
float get_kelvin_from_celsius(KelvinAdapter* adapter) {
float celsius = adapter->celsius_reporter->get_celsius();
return celsius + 273.15;
}
//Implement KelvinReporter interface with the adapter
float get_kelvin(void *opaque_adapter){
return get_kelvin_from_celsius((KelvinAdapter*)opaque_adapter);
}
// Client code
int main() {
CelsiusReporter celsius_system;
celsius_system.get_celsius = get_celsius_impl;
KelvinAdapter* adapter = create_kelvin_adapter(&celsius_system);
// Client expects Kelvin
KelvinReporter kelvin_system;
kelvin_system.get_kelvin = get_kelvin;
float kelvin_temperature = kelvin_system.get_kelvin((void*)adapter);
printf("Temperature in Kelvin: %.2f\n", kelvin_temperature);
free(adapter);
return 0;
}
The Adapter pattern converts the interface of a class into another interface clients expect. It lets classes work together that couldn’t otherwise because of incompatible interfaces. This is achieved by creating a wrapper class (the Adapter) that translates calls from the client to the adaptee.
The C++ example demonstrates adapting a LegacyRectangle class (with a different interface for calculating area) to the Shape interface. The RectangleAdapter class implements Shape and internally uses a LegacyRectangle object. The calculate_area() method of the adapter translates the client’s request into a call to the LegacyRectangle’s area() method. This implementation uses inheritance, a common approach in C++ for adapting interfaces, and follows standard C++ naming and class structure conventions.
#include <iostream>
// The Target interface (Shape)
class Shape {
public:
virtual double calculate_area() = 0;
};
// The Adaptee class (LegacyRectangle)
class LegacyRectangle {
public:
LegacyRectangle(double w, double h) : width_(w), height_(h) {}
double area() {
return width_ * height_;
}
private:
double width_;
double height_;
};
// The Adapter class (RectangleAdapter)
class RectangleAdapter : public Shape {
public:
RectangleAdapter(LegacyRectangle* legacyRect) : legacy_rect_(legacyRect) {}
double calculate_area() override {
return legacy_rect_->area();
}
private:
LegacyRectangle* legacy_rect_;
};
int main() {
LegacyRectangle* legacyRect = new LegacyRectangle(5.0, 4.0);
RectangleAdapter adapter(legacyRect);
std::cout << "Area of the rectangle: " << adapter.calculate_area() << std::endl;
delete legacyRect;
return 0;
}
The Adapter pattern converts the interface of a class into another interface clients expect. This enables classes with incompatible interfaces to work together. Here, we adapt a LegacyRectangle class (with a different way of calculating area) to the IShape interface, allowing it to be used interchangeably with other shapes that implement this interface. This implementation utilizes C#’s interface-based approach for loose coupling and is a common approach for integrating older systems with modern ones. The adapter class RectangleAdapter holds an instance of LegacyRectangle and exposes the desired IShape interface.
// IShape interface - the expected interface
public interface IShape
{
double Area();
}
// Concrete Shape class
public class Circle : IShape
{
private double radius;
public Circle(double radius)
{
this.radius = radius;
}
public double Area()
{
return Math.PI * radius * radius;
}
}
// Legacy class with incompatible interface
public class LegacyRectangle
{
private double width;
private double height;
public LegacyRectangle(double width, double height)
{
this.width = width;
this.height = height;
}
public double GetWidth() { return width; }
public double GetHeight() { return height; }
}
// Adapter class
public class RectangleAdapter : IShape
{
private LegacyRectangle legacyRectangle;
public RectangleAdapter(LegacyRectangle legacyRectangle)
{
this.legacyRectangle = legacyRectangle;
}
public double Area()
{
return legacyRectangle.GetWidth() * legacyRectangle.GetHeight();
}
}
// Example Usage
public class Program
{
public static void Main(string[] args)
{
IShape[] shapes = { new Circle(5), new RectangleAdapter(new LegacyRectangle(4, 6)) };
foreach (var shape in shapes)
{
Console.WriteLine($"Area: {shape.Area()}");
}
}
}
The Adapter pattern converts the interface of a class into another interface clients expect. It lets classes work together that couldn’t otherwise because of incompatible interfaces. This is achieved by creating a wrapper class (the Adapter) that translates calls from the client to the adaptee.
The following TypeScript example adapts a legacy LegacyRectangle class (providing area calculation in old units) to a modern Rectangle interface (expecting area in square meters). The LegacyRectangleAdapter implements the Rectangle interface, taking a LegacyRectangle instance in its constructor and translating the area() call. TypeScript’s strong typing and interface implementation features naturally fit the Adapter pattern’s contract-based approach benefiting from compile-time safety.
// The target interface
interface Rectangle {
area(): number;
}
// The adaptee
class LegacyRectangle {
width: number;
height: number;
constructor(width: number, height: number) {
this.width = width;
this.height = height;
}
getAreaInOldUnits(): number {
return this.width * this.height; // Assume old units are just width * height
}
}
// The adapter
class LegacyRectangleAdapter implements Rectangle {
private legacyRectangle: LegacyRectangle;
constructor(legacyRectangle: LegacyRectangle) {
this.legacyRectangle = legacyRectangle;
}
area(): number {
const areaInOldUnits = this.legacyRectangle.getAreaInOldUnits();
// Conversion factor from old units to square meters (example)
const conversionFactor = 0.092903;
return areaInOldUnits * conversionFactor;
}
}
// Client code
function processRectangle(rectangle: Rectangle): void {
console.log(`Rectangle area: ${rectangle.area()} square meters`);
}
// Usage
const legacyRect = new LegacyRectangle(10, 5);
const adapter = new LegacyRectangleAdapter(legacyRect);
processRectangle(adapter);
The Adapter pattern converts the interface of a class into another interface clients expect. It lets classes work together that couldn’t otherwise because of incompatible interfaces. This example demonstrates adapting a legacy logging service (with a log() method accepting a single string) to a modern logging interface (an AdapterLogger requiring objects with level and message properties). The LegacyLoggerAdapter handles this conversion, allowing modern clients to use the old service without modification. This fits JavaScript’s flexible object nature, leveraging objects as configuration to adapt behavior.
// Legacy Logging Service: Incompatible interface
class LegacyLogger {
log(message) {
console.log(`LEGACY: ${message}`);
}
}
// Modern Logging Interface: What clients expect
class AdapterLogger {
log(logObject) {
if (!logObject || !logObject.level || !logObject.message) {
console.error("Invalid log object. Expected { level: '...', message: '...' }");
return;
}
console.log(`ADAPTER: [${logObject.level}] ${logObject.message}`);
}
}
// Adapter: Converts LegacyLogger's interface to AdapterLogger
class LegacyLoggerAdapter {
constructor(legacyLogger) {
this.legacyLogger = legacyLogger;
}
log(logObject) {
if (!logObject || !logObject.message) {
console.error("Invalid log object. Expected { message: '...' }");
return;
}
this.legacyLogger.log(logObject.message);
}
}
// Client Code: Uses the AdapterLogger interface
function clientCode(logger) {
logger.log({ level: 'INFO', message: 'This is an informational message.' });
logger.log({ level: 'WARNING', message: 'Something needs attention.' });
logger.log({ message: 'Error from client code' }); //Will trigger error handling.
}
// Usage:
const legacyLogger = new LegacyLogger();
const adapter = new LegacyLoggerAdapter(legacyLogger);
const modernLogger = new AdapterLogger();
clientCode(adapter);
clientCode(modernLogger);
The Adapter pattern converts the interface of a class into another interface clients expect. It allows classes with incompatible interfaces to collaborate. Here, we adapt a LegacyRectangle class (with a draw method taking width and height) to the Shape interface, which uses a calculate_area method. The RectangleAdapter class wraps the LegacyRectangle and provides a calculate_area method, effectively making the legacy code usable within a newer system designed around the Shape interface. This implementation is Pythonic due to its use of duck typing – the adapter doesn’t enforce a specific interface, but provides the necessary method to satisfy the client’s expectations.
# Define the desired interface (Shape)
class Shape:
def calculate_area(self):
raise NotImplementedError
# The legacy class with an incompatible interface
class LegacyRectangle:
def draw(self, width, height):
print(f"Drawing rectangle with width {width} and height {height}")
# The Adapter class
class RectangleAdapter(Shape):
def __init__(self, legacy_rectangle):
self.legacy_rectangle = legacy_rectangle
def calculate_area(self):
# Adapt the LegacyRectangle's draw functionality to calculate area
width = 10 # Assuming some default width
height = 5 # Assuming some default height
self.legacy_rectangle.draw(width, height) #Side effect of traditional use
return width * height
# Client code
def process_shape(shape):
area = shape.calculate_area()
print(f"Calculated area: {area}")
# Usage
legacy_rectangle = LegacyRectangle()
rectangle_adapter = RectangleAdapter(legacy_rectangle)
process_shape(rectangle_adapter)
The Adapter pattern converts the interface of a class into another interface clients expect. It lets classes work together that couldn’t otherwise due to incompatible interfaces. This is achieved by creating a wrapper class (the adapter) that translates calls to the adaptee’s interface.
This Java implementation adapts a LegacyRectangularPeg (which has a square peg interface) to fit into a RoundHole (requiring a round peg). The SquarePegAdapter implements the RoundPeg interface but internally uses a LegacyRectangularPeg. The adapt() method demonstrates how to use the adapter to make the rectangular peg fit into the round hole. This implementation is idiomatic Java by utilizing interfaces to define contracts and employing composition (the adapter has-a LegacyRectangularPeg) rather than inheritance when possible.
// Target interface: Round peg
interface RoundPeg {
void fitIntoHole(RoundHole hole);
}
// Client: Round hole
class RoundHole {
private final double radius;
public RoundHole(double radius) {
this.radius = radius;
}
public double getRadius() {
return radius;
}
}
// Adaptee: Legacy rectangular peg
class LegacyRectangularPeg {
private final double width;
private final double height;
public LegacyRectangularPeg(double width, double height) {
this.width = width;
this.height = height;
}
public double getWidth() {
return width;
}
public double getHeight() {
return height;
}
}
// Adapter: Converts square peg to round peg
class SquarePegAdapter implements RoundPeg {
private final LegacyRectangularPeg peg;
public SquarePegAdapter(LegacyRectangularPeg peg) {
this.peg = peg;
}
@Override
public void fitIntoHole(RoundHole hole) {
double reqRadius = hole.getRadius();
double pegSize = peg.getWidth(); // Assuming square peg, width == height. Reduced size for fitting.
if(pegSize > 2 * reqRadius){
System.out.println("Square peg is too large to fit into the round hole!");
} else {
System.out.println("Adapting square peg to fit into round hole.");
}
}
}
public class AdapterExample {
public static void main(String[] args) {
RoundHole hole = new RoundHole(5);
LegacyRectangularPeg peg = new LegacyRectangularPeg(8, 8);
SquarePegAdapter adapter = new SquarePegAdapter(peg);
adapter.fitIntoHole(hole);
LegacyRectangularPeg smallPeg = new LegacyRectangularPeg(3, 3);
SquarePegAdapter smallAdapter = new SquarePegAdapter(smallPeg);
smallAdapter.fitIntoHole(hole);
}
}