【问题标题】:Facade vs Adapter patterns for embedded systems嵌入式系统的外观与适配器模式
【发布时间】:2012-07-03 06:52:15
【问题描述】:

阅读 Elecias White 的书“制作嵌入式系统”(来自 O'Reilly)让我感到困惑,因为这两个术语:外观和适配器模式。她对两者的解释根本不清楚。

Adapter Pattern (Pag, 19): "(...有时称为包装器) 它将对象的接口转换为对客户来说更容易的接口。...通常,适配器是在软件 API 之上编写的,以隐藏丑陋的接口……”.

外观模式(第 86 页):“...它为一段代码提供了简化的界面...”。 然后它说 “...适配器模式是外观模式的更通用版本”

遗憾的是,这两个术语对我来说似乎很相似。

根据本网站(和其他)中的其他定义,大多数人说“适配器模式使两个不兼容的接口兼容”。在这种情况下,“不兼容”这个词是什么意思?

大多数网站和书籍都从嵌入式系统的角度(纯 C,而不是 OOP)以外的更高级别给出了关于模式的定义,因此给出的示例确实不清楚。

值得一提的是,虽然这本书是一本极好的知识来源,但对于新手和专业人士来说,它并没有包含这么多代码,所以人们应该弄清楚这种定义。

我试图通过我为自己写的几个例子来理解它们,你能指出我的理解是否正确吗?

示例 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;
}

希望我自己解释得足够好。提前谢谢你!

【问题讨论】:

  • 加入代理模式,让自己更加困惑:)

标签: design-patterns embedded adapter facade


【解决方案1】:

外观模式用于抽象出复杂的功能,以使 API 更易于使用。

例如,假设您有一段代码同时更新多个对象:

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

...等等...

您可以创建一个类,将这三个 update() 调用包装到一个调用中:

SuperObject.update();

这是外观的一个例子。

当您有一个需要使用的特定接口和一个实现您想要的行为但没有所需接口的对象时,您将使用适配器模式。

假设您所需的接口有一个具有以下签名的方法:

void Save();

您已经有一个实现必要行为的类,但没有您需要的接口,可能是这样的:

bool Update();

您不想更改现有类并冒着破坏使用它的代码的风险,也不想重新创建轮子,因此您改为创建一个实现 Save 方法但使用现有类的实例的包装类:

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

我写过文章概述了Facade PatternAdapter Pattern 的使用

【讨论】:

  • 在您的 cmets 和示例之后,说“外观模式”是“包装器”的同义词更有意义,这与书中的定义不同,因为事实上,就像我的示例 #1 一样,用户无需填写所有字段即可通过fancy_gui_DrawWidget() 绘制按钮,因此他使用了更简单的mygui_DrawButton()mygui_DrawButton() 是“包装”fancy_gui_DrawWidget()。我会检查你的文章,但我想我的方向是正确的 =)
【解决方案2】:

Francisco,您的适配器示例是正确的。我可以给出另一个,但它主要是面向对象的:想象一下你有一个数据源的接口都有方法 int readValue() 并且您可以使用此方法进行多态调用。 而且您还有一个无法用方法重写的旧旧版本(例如,它由其他团队管理或在源代码中不可用) int readInteger()。您不能将原始接口与此类的 readValue() 方法一起使用,因此您创建了具有 int readValue() 方法的中间类,该类委托给 void readInteger()

class Adapter implements Reader {
  private LegacyReader legacyReader;
  public int readValue() {
       return legacyReader.readInteger();
  }
}

现在您可以将旧版阅读器与新的类和接口阅读器一起使用。

在纯 C 世界中,如果您的函数希望有一个指向具有特定签名的函数的指针作为参数,并且实现函数具有另一个签名,则可以使用它。只需用另一个具有正确签名的函数包装您的函数即可。

Facade 通常在您拥有详细的 API(例如 drawCircle()、drawRect()、drawLine())时使用,但通常您需要使用这些调用的组合并且您希望避免复制粘贴或不使用不想为客户端代码提供低级抽象。 在这种情况下,您只需使用以下代码:

class DrawerFacade {
   private LowLevelDrawer drawer;
   public void drawHouse(int i, int j) {
       drawer.drawCircle(...);
       drawer.drawRect(...);
   }
}

如果您谈论的是嵌入式,您可以在纯 C 中使用与函数(非 OOP)API 相同的概念。

【讨论】:

  • 所以我可能将“适配器模式”理解为某种转换,从一个已经存在的签名到一个较新的签名,或者在某些情况下到另一个更有用的签名。我很确定我不能在这种情况下使用“转换”,但这让我想得更清楚。
猜你喜欢
  • 1970-01-01
  • 2010-10-03
  • 2015-05-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-12
相关资源
最近更新 更多