翻译TIPatterns--连接不同类型(Connecting different types)

博客介绍了适配器和桥接两种设计模式。适配器可解决手头类与所需类不匹配的问题。桥接模式能结构化代码,避免类组合带来的类数目爆炸式增长,可让前端和后端互不干扰修改,还给出了相关练习示例。

连接不同类型(Connecting different types)


适配器(Adapter)

    适配器(Adaper)接受一种类型,并为其它类型产生一个接口。当你手头有某个类,而你需要的却是另外一个类(When you’ve got this, and you need that),你可以通过Adapter来解决问题。唯一需要做的就是产生出你需要的那个类(that),有许多种方法可以完成这种配接。


//: adapter:SimpleAdapter.java

// "Object Adapter" from GoF diagram

package adapter;

import junit.framework.*;

 

class Target {

  public void request() {}

}

 

class Adaptee {

  public void specificRequest() {

    System.out.println("Adaptee: SpecificRequest");

  }

}

 

class Adapter extends Target {

  private Adaptee adaptee;

  public Adapter(Adaptee a) {

    adaptee = a;

  }

  public void request() {

    adaptee.specificRequest();

  }

}

 

public class SimpleAdapter extends TestCase  {

  Adaptee a = new Adaptee();

  Target t = new Adapter(a);

  public void test() {

    t.request();

  }

  public static void main(String args[]) {

    junit.textui.TestRunner.run(SimpleAdapter.class);

  }

} ///:~

 

//: adapter:AdapterVariations.java

// Variations on the Adapter pattern.

package adapter;

import junit.framework.*;

 

class WhatIHave {

  public void g() {}

  public void h() {}

}

 

interface WhatIWant {

  void f();

}

 

class SurrogateAdapter implements WhatIWant {

  WhatIHave whatIHave;

  public SurrogateAdapter(WhatIHave wih) {

    whatIHave = wih;

  }

  public void f() {

    // Implement behavior using

    // methods in WhatIHave:

    whatIHave.g();

    whatIHave.h();

  }

}

  

class WhatIUse {

  public void op(WhatIWant wiw) {

    wiw.f();

  }

}

 

// Approach 2: build adapter use into op():

class WhatIUse2 extends WhatIUse {

  public void op(WhatIHave wih) {

    new SurrogateAdapter(wih).f();

  }

}

 

// Approach 3: build adapter into WhatIHave:

class WhatIHave2 extends WhatIHave

implements WhatIWant {

  public void f() {

    g();

    h();

  }

}

 

// Approach 4: use an inner class:

class WhatIHave3 extends WhatIHave {

  private class InnerAdapter implements WhatIWant{

    public void f() {

      g();

      h();

    }

  }

  public WhatIWant whatIWant() {

    return new InnerAdapter();

  }

}

 

public class AdapterVariations extends TestCase  {

  WhatIUse whatIUse = new WhatIUse();

  WhatIHave whatIHave = new WhatIHave();

  WhatIWant adapt= new SurrogateAdapter(whatIHave);

  WhatIUse2 whatIUse2 = new WhatIUse2();

  WhatIHave2 whatIHave2 = new WhatIHave2();

  WhatIHave3 whatIHave3 = new WhatIHave3();

  public void test() {

    whatIUse.op(adapt);

    // Approach 2:

    whatIUse2.op(whatIHave);

    // Approach 3:

    whatIUse.op(whatIHave2);

    // Approach 4:

    whatIUse.op(whatIHave3.whatIWant());

  }

  public static void main(String args[]) {

    junit.textui.TestRunner.run(AdapterVariations.class);

  }

} ///:~

    我想冒昧的借用一下术语“proxy”,因为在《设计模式》里,他们坚持认为一个代理(proxy)必须拥有和它所代理的对象一模一样的接口。但是,如果把这两个词一起使用,叫做“代理适配器(proxy adapter)”,似乎更合理一些。


桥接(Bridge)

    研究Bridge模式的过程中, 我发现它几乎是GoF描述的最差的一个模式。我开始下这个结论是在我读到Alan Shalloway’s的《Design Patterns Explained》的Bridge模式这一章的时候――他指出GoF关于Bridge模式的描述使他觉得一头雾水。
    在一次会议上,我和两位与会者讨论过这个问题,这两位都写过关于设计模式的文章并且做过关于设计模式的演讲(包括Bridge模式)。这两次讨论使我对于Bridge模式的结构产生了完全不同的看法。
    带着这些疑惑,我重新钻研GoF的著作,并且意识到上面那两位与会者的观点都与GoF书中不符。我还发现书中对于Bridge模式的描述实在是糟糕,描述Bridge模式通用结构的那张图根本不顶用,倒是描述Bridge模式实际例子的那个图还说的过去。你只要多看看那张图就明白Bridge模式到底是怎么一回事了。
    审视Bridge模式的时候你会发现,它的一个重要作用就是经常可以作为辅助你书写代码的一种既定结构。我们可能会根据特定的情况在编译时或运行时选择使用不同的对象,Bridge模式的目的就是为了结构化你的代码,从而使得你可以很容易的添加新类型的前端(front-end)对象(这些前端对象是通过调用新类型的后端(back-end)对象的功能实现的)。这么以来,就可以对前端(front-end)和后端(back-end)互不干扰的进行修改。
    前端(front-end)类之间可以拥有完全不同的接口,而且通常就是这样的。它们的共同之处是可以通过使用任意数量的后端(back-end)对象来实现自身的某些功能。后端对象的接口通常也不一样。后端对象之间唯一必须相同的一点是它们都得实现某种类似的功能--例如,一组采用不同方法实现的图形库,或者一系列不同的数据存储解决方案。
    Bridge模式实际上就是一个组织代码的工具,它使得你可以添加任意数量的新的前端服务,而这些前端服务又可以通过把这些操作委托给任意数量的后端对象来实现。
    通过使用Bridge模式,你可以避免(类)组合所带来的类的数目的爆炸省增长。但是别忘了Bridge模式所处理的一系列变化通常都是发生在编码阶段:当为了实现某个功能而必须处理越来越多的选项(options)时,Bridge模式可以使你的代码保持条例性。



 


    下面这个例子的目的就是为了说明Bridge模式的结构(它实现了上面那张图):


//: bridge:BridgeStructure.java

// A demonstration of the structure and operation

// of the Bridge Pattern.

package bridge;

import junit.framework.*;

 

class Abstraction {

  private Implementation implementation;

  public Abstraction(Implementation imp) {

    implementation = imp;

  }

  // Abstraction used by the various front-end

  // objects in order to implement their

  // different interfaces.

  public void service1() {

    // Implement this feature using some

    // combination of back-end implementation:

    implementation.facility1();

    implementation.facility2();

  }

  public void service2() {

    // Implement this feature using some other

    // combination of back-end implementation:

    implementation.facility2();

    implementation.facility3();

  }

  public void service3() {

    // Implement this feature using some other

    // combination of back-end implementation:

    implementation.facility1();

    implementation.facility2();

    implementation.facility4();

  }

  // For use by subclasses:

  protected Implementation getImplementation() {

    return implementation;

  }

}

 

class ClientService1 extends Abstraction {

  public ClientService1(Implementation imp) { super(imp); }

  public void serviceA() {

    service1();

    service2();

  }

  public void serviceB() {

    service3();

  }

}

 

class ClientService2 extends Abstraction {

  public ClientService2(Implementation imp) { super(imp); }

  public void serviceC() {

    service2();

    service3();

  }

  public void serviceD() {

    service1();

    service3();

  }

  public void serviceE() {

    getImplementation().facility3();

  }

}

 

interface Implementation {

  // The common implementation provided by the

  // back-end objects, each in their own way.

  void facility1();

  void facility2();

  void facility3();

  void facility4();

}

 

class Library1 {

  public void method1() {

    System.out.println("Library1.method1()");

  }

  public void method2() {

    System.out.println("Library1.method2()");

  }

}

 

class Library2 {

  public void operation1() {

    System.out.println("Library2.operation1()");

  }

  public void operation2() {

    System.out.println("Library2.operation2()");

  }

  public void operation3() {

    System.out.println("Library2.operation3()");

  }

}

 

class Implementation1 implements Implementation {

  // Each facility delegates to a different library

  // in order to fulfill the obligations.

  private Library1 delegate = new Library1();

  public void facility1() {

    System.out.println("Implementation1.facility1");

    delegate.method1();

  }

  public void facility2() {

    System.out.println("Implementation1.facility2");

    delegate.method2();

  }

  public void facility3() {

    System.out.println("Implementation1.facility3");

    delegate.method2();

    delegate.method1();

  }

  public void facility4() {

    System.out.println("Implementation1.facility4");

    delegate.method1();

  }

}

  

class Implementation2 implements Implementation {

  private Library2 delegate = new Library2();

  public void facility1() {

    System.out.println("Implementation2.facility1");

    delegate.operation1();

  }

  public void facility2() {

    System.out.println("Implementation2.facility2");

    delegate.operation2();

  }

  public void facility3() {

    System.out.println("Implementation2.facility3");

    delegate.operation3();

  }

  public void facility4() {

    System.out.println("Implementation2.facility4");

    delegate.operation1();

  }

}

 

public class BridgeStructure extends TestCase  {

  public void test1() {

    // Here, the implementation is determined by

    // the client at creation time:

    ClientService1 cs1 =

      new ClientService1(new Implementation1());

    cs1.serviceA();

    cs1.serviceB();

  }

  public void test2() {

    ClientService1 cs1 =

      new ClientService1(new Implementation2());

    cs1.serviceA();

    cs1.serviceB();

  }

  public void test3() {

    ClientService2 cs2 =

      new ClientService2(new Implementation1());

    cs2.serviceC();

    cs2.serviceD();

    cs2.serviceE();

  }

  public void test4() {

    ClientService2 cs2 =

      new ClientService2(new Implementation2());

    cs2.serviceC();

    cs2.serviceD();

    cs2.serviceE();

  }

  public static void main(String[] args) {

    junit.textui.TestRunner.run(BridgeStructure.class);

  }  

} ///:~


    前端基类(front-end base class)根据后端基类(back-end base class)所(声明)的方法,提供了用以实现前端派生类(front-end derived class)所需的操作. 这样以来,任何后端派生类都可以被用来实现前端类所需要的操作。 注意到桥接(bridging)是分步完成的,每一步都提供了一个抽象层。上面的例子里,Implementation被定义为一个接口是为了强调所有实际功能都是通过后端派生类来实现的,而后端基类不参与实现。
    后端派生类通过委托给(delegating)别的对象(本例中,是Library1 和 Library2)来实现基类所定义的操作, 这些接受委托的对象通常拥有完全不同的接口,但是又提供几乎相同的功能(这也是Bridge模式的一个重要目的)。一言以蔽之,每一个后端类都可以看作是某一个库(library)或者工具的适配器(adapter),这些库或者工具用不同的方法实现预期的功能。

 

练习

1.修改BridgeStructure.java,写一个factroy用来(动态)选择(后端对象)的实现。
2.修改BridgeStructure.java,针对前端对象,使用委托(delegation)而不是继承。这么做有什么好处和坏处?
3.写一个Bridge模式的例子,要求用到关联数组(associative array)。要能够通过传入键值对象(key object)取出元素。在构造函数里从一个数组读入并初始化一系列的“键-值”对(key-value pairs)。读数据的时候,用数组(array)作为后端实现,但是写入key-value pair的时候,后端实现要切换到map.
4.用bridge模式,结合java.util.collections的ArrayList写一个栈(stack)和队列(queue)。写好之后,再写一个双端队列(double-ended queue)。再加入一个LinkedList作为后端实现。这写步骤做下来,你就会明白如何使用Bridge模式在影响最小的情况下往你的代码里添加前端和后端对象。
5.用bridge模式写一个类,连接不同种类的记帐(bookkeeping)程序(连同它们的接口和数据格式)和不同的银行(这些银行提供不同类型的服务和接口)。

 

目录

 

在使用 MqttFX 客户端进行连接时,如果出现 `ClientModel` 错误,通常与客户端配置、网络连接或 MQTT 代理(Broker)设置相关。以下是一些常见的原因及解决方案: ### 常见错误原因与解决办法 #### 1. **Broker 地址或端口配置错误** 确保 MQTT Broker 的地址端口正确无误。默认情况下,MQTT 使用端口 `1883`(非加密)或 `8883`(加密)。如果地址拼写错误或端口未开放,连接将失败。 ```java // 示例:检查 Broker 地址端口是否正确 String broker = "tcp://mqtt.broker.address:1883"; ``` #### 2. **网络连接问题** 确认客户端设备与 MQTT Broker 之间的网络连接是通畅的。可以使用 `ping` 或 `telnet` 命令测试网络可达性。 ```bash telnet mqtt.broker.address 1883 ``` 如果无法建立连接,则可能是防火墙、路由器或 Broker 服务未运行所致。 #### 3. **客户端 ID 冲突** 每个 MQTT 客户端必须具有唯一的客户端 ID。如果多个客户端使用相同的 ID 连接至 Broker,可能导致连接被拒绝。 ```java // 确保客户端 ID 唯一 String clientId = "Client_" + UUID.randomUUID().toString(); ``` #### 4. **认证失败(用户名或密码错误)** 如果 Broker 启用了身份验证,需确保输入正确的用户名密码。否则将导致连接失败。 #### 5. **SSL/TLS 配置问题** 如果使用加密连接(如端口 `8883`),请检查 SSL/TLS 设置是否与 Broker 的证书配置匹配。例如,是否信任 Broker 的证书、是否启用双向认证等。 #### 6. **MQTT 协议版本不兼容** MqttFX 支持多种 MQTT 协议版本(如 v3.1、v3.1.1、v5.0)。确保客户端使用的协议版本与 Broker 兼容。 ```java // 设置 MQTT 协议版本 MqttConnectOptions options = new MqttConnectOptions(); options.setMqttVersion(MqttConnectOptions.MQTT_VERSION_3_1_1); ``` #### 7. **Broker 服务未启动** 检查 MQTT Broker 是否正常运行。可以尝试通过其他客户端(如 `mosquitto_pub` 或 `MQTTX`)测试连接,以确认问题是否出在 Broker 本身。 --- ### 日志与调试建议 查看 MqttFX 的日志输出,通常会提供更详细的错误信息。例如: - `java.net.ConnectException`: 网络连接失败 - `ClientModel: connect failed`: 通常由上述配置错误导致 - `IOException - Connection refused`: Broker 未响应 启用 MqttFX 的调试日志或使用 Wireshark 等工具抓包分析网络通信,有助于进一步定位问题。 --- ### 示例:MqttFX 连接异常处理代码片段 ```java try { IMqttClient client = new MqttClient(broker, clientId, persistence); MqttConnectOptions options = new MqttConnectOptions(); options.setAutomaticReconnect(true); client.connect(options); } catch (MqttException e) { System.err.println("MQTT 连接异常: " + e.getMessage()); e.printStackTrace(); } ``` ---
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值