Шаблоны фасадов и адаптеров для встраиваемых систем

Прочитав книгу Элесиаса Уайта "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;
}

Надеюсь, я сам достаточно хорошо объяснил. Заранее благодарю!!


person Francisco Rodríguez    schedule 02.07.2012    source источник
comment
Добавьте шаблон Proxy, чтобы немного запутать себя :)   -  person haylem    schedule 03.07.2012


Ответы (2)


Паттерн Фасад используется для абстрагирования сложной функциональности, чтобы упростить использование API.

Например, предположим, что у вас есть фрагмент кода, который обновляет сразу несколько объектов:

ObjectA.update();
ObjectB.update();
ObjectC.update();

...и т.д...

Вы можете создать класс, который объединяет эти три вызова update () в один вызов:

SuperObject.update();

Это один из примеров фасада.

Вы бы использовали шаблон адаптера, когда у вас есть конкретный интерфейс, который вам необходимо использовать, и объект, который реализует желаемое поведение, но не имеет необходимого интерфейса.

Допустим, у вашего необходимого интерфейса есть метод со следующей подписью:

void Save();

У вас уже есть класс, который реализует необходимое поведение, но не имеет необходимого интерфейса, возможно, примерно так:

bool Update();

Вы не хотите изменять свой существующий класс и рисковать кодом нарушения, который его использует, и вы не хотите воссоздавать колесо, поэтому вместо этого вы создаете класс-оболочку, который реализует метод Save, но использует экземпляр вашего существующего класса:

void Save()
{
   bool notUsingThisReturnValue = existingClassInstance.Update();
}

Я написал статьи об использовании как паттерна фасада и шаблон адаптера

person Brian Driscoll    schedule 02.07.2012
comment
После ваших комментариев и примеров имеет смысл сказать, что шаблон фасада является синонимом оболочки, в отличие от определения книги, потому что на самом деле, как в моем примере №1, пользователю не нужно заполнять все поля по порядку чтобы нарисовать кнопку с помощью fancy_gui_DrawWidget (), поэтому он использует вместо этого более простой mygui_DrawButton (). mygui_DrawButton () оборачивает fancy_gui_DrawWidget (). Я проверю ваши статьи, но думаю, я в правильном направлении =) - person Francisco Rodríguez; 03.07.2012

Франциско, ваш пример для адаптера верен. Я могу привести еще один, но он в основном объектно-ориентированный: представьте, что у вас есть интерфейс для источников данных, у всех есть метод 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, если вы говорите о встроенных.

person dbf    schedule 02.07.2012
comment
Поэтому я могу понимать шаблон адаптера как своего рода преобразование от одной уже существующей подписи к более новой или к другой, более полезной при определенных обстоятельствах. Я уверен, что не могу использовать преобразование в этом контексте, но это заставляет меня думать более ясно. - person Francisco Rodríguez; 03.07.2012