Bruce Eckel
读完需要
10
分钟速读仅需 1 分钟
布鲁斯 • 埃克尔(Bruce Eckel),C++ 标准委员会的创始成员之一,知名技术顾问,专注于编程语言和软件系统设计方面的研究,常活跃于世界各大顶级技术研讨会。
他自 1986 年以来,累计出版 Thinking in C++、Thinking in Java、On Java 等十余部经典计算机著作,曾多次荣获 Jolt 最佳图书奖(被誉为“软件业界的奥斯卡”),其代表作 Thinking in Java 被译为中文、日文、俄文、意大利文、波兰文、韩文等十几种语言,在世界范围内产生了广泛影响。
8
改变接口
有时你要解决的问题可能就像“我没有想要的接口”这么简单。有两种设计模式可以解决这个问题:适配器模式(Adapter)接收一种类型为参数,并生成面向另一种类型的接口;外观模式(Facade)可以创建一个面向一组类的接口,这只是提供了一种更为简便的方式,来处理一个库或一批资源。
8.1
适配器模式
当你有了 A,而且想要 B 的时候,适配器模式便可以解决你的问题。这里唯一的需求就是生成 B,有多种方法可以完成该适配工作:
// patterns/adapt/Adapter.java
// 多种适配器模式
// {java patterns.adapt.Adapter}
package patterns.adapt;
class WhatIHave {
public void g() {}
public void h() {}
}
interface WhatIWant {
void f();
}
class ProxyAdapter implements WhatIWant {
WhatIHave whatIHave;
ProxyAdapter(WhatIHave wih) {
whatIHave = wih;
}
@Override public void f() {
// 用WhatIHave中的方法实现行为:
whatIHave.g();
whatIHave.h();
}
}
class WhatIUse {
public void op(WhatIWant wiw) {
wiw.f();
}
}
// 方法2:构造传入op()中使用的适配器
class WhatIUse2 extends WhatIUse {
public void op(WhatIHave wih) {
new ProxyAdapter(wih).f();
}
}
// 方法3:在WhatIHave内部构造适配器
class WhatIHave2 extends WhatIHave
implements WhatIWant {
@Override public void f() {
g();
h();
}
}
// 方法4:使用内部类
class WhatIHave3 extends WhatIHave {
private class InnerAdapter implements WhatIWant{
@Override public void f() {
g();
h();
}
}
public WhatIWant whatIWant() {
return new InnerAdapter();
}
}
public class Adapter {
public static void main(String[] args) {
WhatIUse whatIUse = new WhatIUse();
WhatIHave whatIHave = new WhatIHave();
WhatIWant adapt= new ProxyAdapter(whatIHave);
whatIUse.op(adapt);
// 方法2:
WhatIUse2 whatIUse2 = new WhatIUse2();
whatIUse2.op(whatIHave);
// 方法3:
WhatIHave2 whatIHave2 = new WhatIHave2();
whatIUse.op(whatIHave2);
// 方法4:
WhatIHave3 whatIHave3 = new WhatIHave3();
whatIUse.op(whatIHave3.whatIWant());
}
}
在 ProxyAdapter 的应用上,我对术语“代理”的使用没有那么严格。《设计模式》一书的作者主张代理必须具有和其代理的对象完全相同的接口。将代理和适配器这两个词合并为代理适配器(ProxyAdapter)来使用也许更合理。
8.2
外观模式
这是我在将需求转化为对象设计的开始阶段所遵循的原则:
如果有一段逻辑很丑陋,那么就把它藏到对象里。
这基本上就是外观模式所完成的任务。如果你有一堆相当杂乱的类和交互逻辑,并且不需要让调用方程序员看到,就可以将这些藏到一个接口后面,以一种更易懂的形式(只)暴露出外部需要知道的信息。外观模式通常被实现为单例抽象工厂。可以使用包含静态工厂方法的类来实现这个效果:
// patterns/Facade.java
class A { A(int x) {} }
class B { B(long x) {} }
class C { C(double x) {} }
// 其他不需要由外观暴露出去的类都放到这里
public class Facade {
static A makeA(int x) { return new A(x); }
static B makeB(long x) { return new B(x); }
static C makeC(double x) { return new C(x); }
public static void main(String[] args) {
// 调用方程序员通过调用静态方法得到对象:
A a = Facade.makeA(1);
B b = Facade.makeB(1);
C c = Facade.makeC(1.0);
}
}
《设计模式》中给出的示例并不是真正的外观模式,只是一个用到了其他类的类而已。
8.3
可作为外观模式变体的 Java 包
对我来说,外观模式会给人一种相当“面向过程”(而非面向对象)的感觉:我们只是在调用一些生成对象的函数而已。而这实际上和抽象工厂又有多大区别呢?外观模式的重点在于将一组类(以及它们之间的交互)形成的库部分隐藏起来,不让调用方程序员看到,使得这一组类对外暴露出的接口更好用、更易懂。而这正是 Java 的包特性所实现的功能。在库以外只能创建和使用 public 的类,而所有非 public 的类都只能在包范围内访问。这就好像外观模式是 Java 的内建特性。公平地讲,《设计模式》面向的主要是使用 C++的群体。虽然 C++有命名空间来防止全局冲突和类名冲突,但这并不能提供由 Java 中的非 public 类带来的类隐藏机制。大部分时候,Java 的包似乎就可以解决外观模式所要解决的问题了。
9
解释器模式:运行时的灵活性
在一些应用程序中,用户需要在运行时拥有更高的灵活性,例如创建用来描述系统所需行为的脚本。解释器设计模式可以用来创建语言解释器,并将其嵌入这样的程序中。
在构建应用程序的过程中,开发自定义语言以及为其构建解释器是一件费时费力的事。最好的方案是复用代码:嵌入一个已构建好并且调试过的解释器。Python 语言可以免费地嵌入到营利性质的应用程序中,而无须任何许可协议、版税或其他产权负担。另外,Python 还有一个称为 Jython 的版本(已经过时了,但仍然能用),它是彻底的 Java 字节码,因此将它加入 Java 程序是相对比较简单的。Python 是一种脚本语言,易于学习,编写和阅读的逻辑性很强,支持函数和对象,拥有大量可用的库,并且几乎可以在所有的平台上运行。你可以通过 Python 网站进一步学习。
本书特色
查漏宝典:涵盖Java关键特性的设计原理和应用方法
避坑指南:以产业实践的得失为鉴,指明Java开发者不可不知的设计陷阱
经典普适:值得不同层次的Java开发者反复研读
专家领读:4位一线业务专家、知名作译者帮你拆解书中难点,总结Java开发精要
值得一提的是,为了帮助新手加深理解,出版方邀请了4位从业10年以上知名作译者(DDD 专家张逸、服务端专家梁桂钊、软件系统架构专家王前明、译者陈德伟)为本书录制【精讲视频】和【导读指南】,该视频已在B站和图灵社区发布,感兴趣的朋友可以去看看。
往期推荐
如何在 SpringBoot 项目中控制 RocketMQ消费线程数量