
//Target: a pointer to a function plays Request()
typedef struct CText {
void (*getText)(const struct CText *self, char *buffer, size_t size);
} CText;
//Adaptee
typedef struct {
const char *rows[10];
size_t count;
} StringList;
void add(StringList *list, const char *value) {
if (list->count < 10) {
list->rows[list->count++] = value;
}
}
//SpecificRequest()
void getString(const StringList *list, char *buffer, size_t size) {
buffer[0] = '\0';
for (size_t i = 0; i < list->count; i++) {
if (i != 0) {
strncat(buffer, "\n", size - strlen(buffer) - 1);
}
strncat(buffer, list->rows[i], size - strlen(buffer) - 1);
}
}
//Adapter: the target is the first field, the adaptee is
//an object it points to
typedef struct {
CText base;
const StringList *rowList;
} TextAdapter;
//Request() passes the work to SpecificRequest()
void adapterGetText(const CText *self, char *buffer, size_t size) {
const TextAdapter *adapter = (const TextAdapter *)self;
getString(adapter->rowList, buffer, size);
}
//Client
StringList rowList = {};
add(&rowList, "line 1");
add(&rowList, "line 2");
//the adapter wraps a list that already exists
TextAdapter adapter = {{adapterGetText}, &rowList};
CText *target = &adapter.base;
char text[32];
target->getText(target, text, sizeof text);
//text: line 1
// line 2
printf("text:\n%s\n", text);
Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.Read moreThe problem. Your program reads text from any source through one method. Another part of the system gives you ready lists of lines. A list can join its lines, but through a method with another name. The lists arrive already filled, and you cannot change their class. You also do not want to change the code that calls the method it expects. The idea. An adapter makes an existing class, the adaptee, fit the interface that the client expects, the target. In this version the adapter implements the target and holds a reference to an adaptee object. Its target method forwards the call to that object, sometimes with a small conversion. The adapter does not have the other methods of the adaptee. Compare the adapter by inheritance: it extends the adaptee instead of holding it. How to read the example. There are two objects: the adaptee and the adapter that holds it. In most languages the adaptee is created separately and given to the adapter. In a few, the adapter creates its own adaptee, and the client fills it through the adapter's field. The client reads the result only through the target method, and the adapter forwards this call to the adaptee. Some adapters return an empty result when they hold no adaptee. In some languages the adapter is simply a function that captures the adaptee. When to use it. Prefer an object adapter in most cases. It can adapt any object of the adaptee type, including subclasses and objects that already exist. Do not use an adapter at all when you can simply change the adaptee: renaming one method is cheaper. A common mistake. Putting new logic into the adapter. An adapter only translates one call into another. Checks and business rules belong elsewhere. |