桥接模式和适配器模式有什么区别?
桥接模式和适配器模式有什么区别?
"适配器模式是在设计完成之后使事情能够运转;桥接模式则是在设计时就使它们工作。[GoF, p219]"
适配器模式(Adapter pattern)通常在你拥有现有代码但无法对其进行更改以满足所需接口的情况下非常有用,这些代码可能来自第三方或内部,但不在您的控制范围内。例如,我们有一个超级武器阵列(SuperWeaponsArray),可以控制一组毁灭性设备。
public class SuperWeaponsArray {
/*...*/
public void destroyWorld() {
for (Weapon w : armedWeapons) {
w.fire();
}
}
}
很好。除了我们意识到我们的武器库中有一枚核弹头比转换成Weapon接口早得多。但是我们真的想在这里让它工作...所以我们该怎么办... 把它塞进去!
NukeWeaponsAdaptor - 基于我们的Nuke类,但导出Weapon接口。太棒了,现在我们肯定可以摧毁世界了。这似乎有点笨拙,但确实使事情变得可行。
桥接模式是一种您提前实施的东西-如果您知道有两个正交层次结构,则它提供了一种将接口和实现解耦的方式,以便您不会得到疯狂数量的类。假设您有:
MemoryMappedFile和DirectReadFile类型的文件对象。假设您想能够从各种来源读取文件(可能是Linux vs. Windows实现等)。桥接帮助您避免最终得到的结果是:
MemoryMappedWindowsFile MemoryMappedLinuxFile DirectReadWindowsFile DirectReadLinuxFile
适配器:
例子:
正方形和矩形是两种不同的形状,获取它们各自的面积()需要不同的方法。但是,仍然可以通过转换一些属性,使正方形适用于矩形接口。
public class AdapterDemo{
public static void main(String args[]){
SquareArea s = new SquareArea(4);
System.out.println("Square area :"+s.getArea());
}
}
class RectangleArea {
public int getArea(int length, int width){
return length * width;
}
}
class SquareArea extends RectangleArea {
int length;
public SquareArea(int length){
this.length = length;
}
public int getArea(){
return getArea(length,length);
}
}
桥接模式:
如 Gang of Four 设计模式书籍(第174页) 所解释的,此模式有四个组件:
Abstraction
:定义了抽象的接口
RefinedAbstraction
:扩展了 Abstraction 定义的接口
Implementor
:为实现类定义了接口
ConcreteImplementor
:实现 Implementor 接口并定义其具体实现。
请参考下面的 Stack Exchange 帖子中的代码:
class SquareArea { ... }
- Evgeny适配器模式更多的是让现有代码与新系统或接口一起使用。
如果您有一组公司标准的Web服务API,希望提供给另一个应用程序的现有可扩展性接口,则可以考虑编写一组适配器来实现。需要注意的是,适配器模式和外观模式等其他模式相似,技术上如何定义模式存在一定灰色地带。
桥接模式可以让您拥有算法或系统的替代实现。
尽管不是经典的桥接模式示例,但想象一下,如果您有一些数据存储的实现:其中一个在空间效率方面高效,另一个在原始性能方面高效……并且您有业务需求在应用程序或框架中都提供这两个实现。
至于您的问题“在哪里使用哪种模式”,答案是:无论何时在您的项目中合理使用即可!也许可以考虑提供一次澄清编辑,以指导讨论关于何时使用适配器模式或桥接模式。
抽象化必须具有多个实现,两者可以独立演进。适配器模式使得设计完成后事情能够工作;桥接模式使它们在被设计前就能够工作。这并不意味着适配器比桥接模式更差;每种模式只是解决不同的问题。适配器模式与桥接模式
适配器模式和桥接模式具有一些共同点。它们都通过提供对另一个对象的间接性来促进灵活性。它们都涉及从不同于其自身的接口转发请求。
这些模式之间的关键区别在于它们的意图。适配器专注于解决两个现有接口之间的不兼容性。它不关注这些接口的实现方式,也不考虑它们可能会独立地发展。它是使两个独立设计的类协同工作的一种方法,而无需重新实现其中一个。另一方面,桥接模式桥接了一个抽象和它的(潜在众多)实现。它为客户端提供一个稳定的接口,同时允许你变化实现该接口的类。随着系统的发展,它还能容纳新的实现。
由于这些差异,适配器模式和桥接模式通常在软件生命周期的不同阶段使用。当你发现两个不兼容的类应该协同工作时,通常需要适配器模式,以避免复制代码。这种耦合是意想不到的。相反,桥接模式的用户预先了解抽象和其实现之间的关系。
根据另一个stackoverflow的回答,对我来说这是更短更清晰的答案这里:
Adapter 是在你有一个抽象接口并且你想将该接口映射到另一个具有类似功能但不同接口的对象时使用。
Bridge 和Adapter非常相似,但当你定义抽象接口和底层实现时,我们称其为Bridge。也就是说,你不是适应一些旧代码或第三方代码,而是所有代码的设计师,但需要能够交换不同的实现。
桥接器是改进后的适配器。桥接器包括适配器,并为其添加了额外的灵活性。 以下是Ravindra答案中元素在模式之间的映射:
Adapter | Bridge
-----------|---------------
Target | Abstraction
-----------|---------------
| RefinedAbstraction
|
| This element is Bridge specific. If there is a group of
| implementations that share the same logic, the logic can be placed here.
| For example, all cars split into two large groups: manual and auto.
| So, there will be two RefinedAbstraction classes.
-----------|---------------
Adapter | Implementor
-----------|---------------
Adaptee | ConcreteImplementor
我认为这很简单。
适配器的设计是为了让第三方应用程序能够与您的应用程序一起工作。反之亦然,以便您的应用程序可以与第三方应用程序一起工作。
使用桥接模式,它应该连接两个或多个应用程序而不实现适配器。
事实上,桥接是两个应用程序将相互交互的接口。作为桥接的示例,这些是 PHP 中的 PSR 接口。
例如:
OtherApp
<?php
interface IRequestDataOtherApp {};
interface IResponseDataOtherApp {};
class ResponseDataOtherApp implements IResponseDataOtherApp {
};
class OtherApp {
public static function request(IRequestDataOtherApp $requestData):IResponseOtherApp
{
// code
return new ResponseDataOtherApp ();
}
}
我的应用
<?php
interface IResponseDataMyApp {};
interface IReqestDataMyApp {};
class ReqestDataMyApp implements IReqestDataMyApp {};
class Adapter {
public static function convertResponseData(IResponseDataOtherApp $response):IResponseDataMyApp
{
}
public static function convertRequestData(IReqestDataMyApp $request):IRequestOtherApp
{
}
};
$unformattedResponse=OtherApp::request(Adapter::convertRequestData(new ReqestDataMyApp ()));
$myResponse=ResponseAdapter::convertResponseData($unformattedResponse);
//...
<?php
interface IBridgeResponse {};
其他应用
<?php
interface IRequestDataOtherApp {};
interface IResponseDataOtherApp {};
class ResponseDataOtherApp implements IBridgeResponse, IResponseDataOtherApp {
};
class OtherApp {
public static function request(IRequestDataOtherApp $requestData):IResponseOtherApp
{
// code
return new ResponseDataOtherApp ();
}
}
我的应用
<?php
interface IResponseDataMyApp {};
interface IReqestDataMyApp {};
class ReqestDataMyApp implements IReqestDataMyApp {};
class Adapter {
public static function convertResponseData(IResponseDataOtherApp $response):IResponseDataMyApp
{
}
public static function convertRequestData(IReqestDataMyApp $request):IRequestOtherApp
{
}
};
$response=OtherApp::request(Adapter::convertRequestData(new ReqestDataMyApp ()));
if($response instanceof IBridgeResponse){
/**
The component has implemented IBridgeResponse interface,
thanks to which our application can interact with it.
This is the bridge.
Our application does not have to create an additional adapter
(if our application can work with IBridgeResponse).
*/
}
//...