Прочитав книгу Элесиаса Уайта "Making Embedded Systems" (от O'Reilly), я запутался из-за этих двух терминов: фасады и шаблоны адаптеров. Объяснение, которое она дает обоим, совершенно непонятно.
Шаблон адаптера (стр. 19): "(... Иногда называется оболочкой) Преобразует интерфейс объекта в более простой для клиента ... Часто адаптеры написаны поверх программных API, чтобы скрыть уродливые интерфейсы ... ".
Узор фасада (стр. 86): "... Он обеспечивает упрощенный интерфейс для фрагмента кода ...". Затем он говорит ".. . Шаблон адаптера - это более общая версия шаблона фасада ".
К сожалению, оба термина кажутся мне похожими.
Исходя из других определений на этом (и других) сайтах, большинство людей говорят: "Шаблон адаптера обеспечивает совместимость двух несовместимых интерфейсов". Что в этом контексте означает слово "несовместимо"?
Большинство сайтов и книг дают свои определения шаблонов с более высокого уровня, отличного от точки зрения встроенной системы (простой C, а не ООП), поэтому приведенные примеры действительно не ясны.
Стоит упомянуть, что, хотя книга является отличным источником знаний как для новичков, так и для профессионалов, в нее не входило так много кода, поэтому следует выяснить такого рода определения.
Я попытался понять их с помощью пары примеров, которые я написал для себя, не могли бы вы указать мне, правильное мое понимание или нет?
Пример 1, узор фасада:
/* This is a fancy API that I want to 'facade' */
fancy_gui_DrawWidget(parent, id, x0, y0, x1, y1, text, txt_color, back_color, brdr_color, draw_callback(), ... and more parameters)
{
/* draw the widget */
}
/* Here I'm using the 'facade pattern' */
mygui_DrawButton(parent, id, x, y, width, height, text)
{
...
x1=x+width;
y1=y+height;
...
fancy_gui_DrawWidget(parent, id, x, y, x1, y1, text, BLACK, WHITE, ORANGE, button_draw_fn, ... and some more parameters needed);
}
Пример 2, шаблон адаптера:
/* Ugly interface that I want to 'adapt' (from LPC17xx NXP's CMSIS library) */
uint32_t UART_Send(
LPC_UART_TypeDef *UARTx,
uint8_t *txbuf,
uint32_t buflen,
TRANSFER_BLOCK_Type flag)
{
/* transmits the txbuf */
}
/* Here I'm using the 'adapter pattern' (I think so) for a good looking interface */
int uart0_Send(buffer, len_buffer)
{
/* Do some stuff */
len=UART_Send(uart0_handler,buffer,len_buffer, BLOCKING);
if(len!=len_buffer)
return 0;
return 1;
}
Надеюсь, я сам достаточно хорошо объяснил. Заранее благодарю!!
Ответ 1 · рейтинг 2 · Brian Driscoll
Паттерн Фасад используется для абстрагирования сложной функциональности, чтобы упростить использование API.
Например, предположим, что у вас есть фрагмент кода, который обновляет сразу несколько объектов:
ObjectA.update();
ObjectB.update();
ObjectC.update();
...и т.д...
Вы можете создать класс, который объединяет эти три вызова update () в один вызов:
SuperObject.update();
Это один из примеров фасада.
Вы бы использовали шаблон адаптера, когда у вас есть конкретный интерфейс, который вам необходимо использовать, и объект, который реализует желаемое поведение, но не имеет необходимого интерфейса.
Допустим, у вашего необходимого интерфейса есть метод со следующей подписью:
void Save();
У вас уже есть класс, который реализует необходимое поведение, но не имеет необходимого интерфейса, возможно, примерно так:
bool Update();
Вы не хотите изменять свой существующий класс и рисковать кодом нарушения, который его использует, и вы не хотите воссоздавать колесо, поэтому вместо этого вы создаете класс-оболочку, который реализует метод Save, но использует экземпляр вашего существующего класса:
void Save()
{
bool notUsingThisReturnValue = existingClassInstance.Update();
}
Я написал статьи об использовании как паттерна фасада и шаблон адаптера
Ответ 2 · dbf
Франциско, ваш пример для адаптера верен. Я могу привести еще один, но он в основном объектно-ориентированный: представьте, что у вас есть интерфейс для источников данных, у всех есть метод int readValue(), и у вас есть интерфейс с этим методом для полиморфных вызовов. И у вас есть еще один устаревший старый, который вы не можете переписать (например, он управляется другой командой или он недоступен в исходном коде) с помощью метода int readInteger(). Вы не можете использовать ваш исходный интерфейс с readValue() методом для этого класса, поэтому вы создаете промежуточный класс, который имеет int readValue() метод, который делегирует void readInteger().
class Adapter implements Reader {
private LegacyReader legacyReader;
public int readValue() {
return legacyReader.readInteger();
}
}
Теперь вы можете использовать устаревшую программу чтения вместе с новыми классами и интерфейсом Reader.
В простом мире C вы можете использовать его, если ваша функция ожидает иметь указатель на функцию с определенной сигнатурой в качестве аргумента, а функция реализации имеет другую сигнатуру. Просто оберните свою функцию другой с правильной подписью.
Фасад обычно используется, когда у вас есть подробный API (например, drawCircle (), drawRect (), drawLine ()), но часто вам нужно использовать комбинацию этих вызовов, и вы хотите избежать копирования-вставки или не хотите для предоставления низкоуровневой абстракции клиентскому коду. В этом случае вы просто используете этот код:
class DrawerFacade {
private LowLevelDrawer drawer;
public void drawHouse(int i, int j) {
drawer.drawCircle(...);
drawer.drawRect(...);
}
}
Вы можете использовать ту же концепцию с API функций (не-ООП) на простом C, если вы говорите о встроенных.