
#include <stddef.h>
//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: C has no inheritance, both bases are fields;
//the adaptee goes first, so the adapter is a StringList as well
typedef struct {
StringList base;
CText target;
} TextAdapter;
//Request(): the second base finds the whole adapter by its offset
void adapterGetText(const CText *self, char *buffer, size_t size) {
const char *start = (const char *)self - offsetof(TextAdapter, target);
const TextAdapter *adapter = (const TextAdapter *)start;
getString(&adapter->base, buffer, size);
}
//Client
TextAdapter adapter = {{}, {adapterGetText}};
//the adapter goes where a StringList is expected
add((StringList *)&adapter, "line 1");
add((StringList *)&adapter, "line 2");
//and where a CText is expected
CText *target = &adapter.target;
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. A ready library class keeps lines of text and can join them, but through a method with another name. You cannot change the library. You also do not want to change all 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 is a subclass of the adaptee and also implements the target. Its target method only calls the inherited method, sometimes with a small conversion. So the adapter is itself an adaptee and keeps all its methods. Compare the adapter by composition: it holds an adaptee instead of extending it. How to read the example. The adaptee already does the work, but under another method. The target declares the method that the client expects. The adapter extends the adaptee and adds the target method, which calls the inherited one. There is only one object: the adapter is the adaptee. In most languages the client fills it through the adaptee's own methods and reads the result through the target method. Languages without class inheritance imitate it: they embed the adaptee, or they add the target method to the adaptee's own type. When to use it. Use a class adapter when you adapt one known class and also need its other methods. Do not use it when the object to adapt already exists, or when it may belong to a subclass of the adaptee. A class adapter is always a new object of one fixed class, so it cannot adapt such objects. A common mistake. Forgetting that the adapter shows every method of the adaptee. Clients start to call these methods directly, and the target no longer hides the adaptee. |