1. Overview
1.概述
Structural design patterns are those that simplify the design of large object structures by identifying relationships between them. They describe common ways of composing classes and objects so that they become repeatable as solutions.
结构性设计模式是那些通过确定大型对象结构之间的关系来简化设计的模式。它们描述了组成类和对象的常见方式,从而使它们成为可重复的解决方案。
The Gang of Four has described seven such structural ways or patterns. In this quick tutorial, we’ll see examples of how some core libraries in Java have adopted each one of them.
Gang of Four已经描述了七种这样的结构方式或模式。在这个快速教程中,我们将看到Java中的一些核心库如何采用其中每一种的例子。
2. Adapter
2. adapter
An adapter, as the name suggests, acts as an intermediary to convert an otherwise incompatible interface to one that a client expects.
适配器,顾名思义,作为一个中间人,将原本不兼容的接口转换为客户期望的接口。
This is useful in cases where we want to take an existing class whose source code cannot be modified and make it work with another class.
这在以下情况下很有用:我们想把一个现有的、源代码不能修改的类,让它与另一个类一起工作。
JDK’s collection framework offers many examples of the adapter pattern:
JDK的集合框架提供了许多适配器模式的例子。
List<String> musketeers = Arrays.asList("Athos", "Aramis", "Porthos");
Here, Arrays#asList is helping us adapt an Array to a List.
在这里,Arrays#asList正在帮助我们将一个Array调整为一个List。
The I/O framework also makes extensive use of this pattern. As an example, let’s consider this snippet, which is mapping an InputStream to a Reader object:
I/O框架也广泛使用了这种模式。作为一个例子,让我们考虑这个片段,它是将一个InputStream映射到一个Reader对象。
InputStreamReader input = new InputStreamReader(new FileInputStream("input.txt"));
3. Bridge
3.桥梁
A bridge pattern allows separation between abstractions and implementations so that they can be developed independently from each other but still have a way, or bridge, to coexist and interact.
桥梁模式允许抽象和实现之间的分离,以便它们可以独立开发,但仍有一种方法或桥梁可以共存和互动。
An example of this in Java would be the JDBC API. It acts as a link between the database such as Oracle, MySQL, and PostgreSQL, and their particular implementations.
Java中的一个例子是JDBC API。它在数据库(如Oracle、MySQL和PostgreSQL)和它们的特定实现之间充当了一个链接。
The JDBC API is a set of standard interfaces such as Driver, Connection, and ResultSet, to name a few. This enables different database vendors to have their separate implementations.
JDBC API是一组标准接口,如Driver、Connection和ResultSet,仅举几例。这使得不同的数据库供应商能够拥有他们独立的实现。
For example, to create a connection to a database, we’d say:
例如,要创建一个与数据库的连接,我们会说。
Connection connection = DriverManager.getConnection(url);
Here, url is a String that can represent any database vendor.
这里,url是一个字符串,可以代表任何数据库供应商。
As an example, for PostgreSQL, we might have:
作为一个例子,对于PostgreSQL,我们可能有。
String url = "jdbc:postgresql://localhost/demo";
And for MySQL:
而对于MySQL。
String url = "jdbc:mysql://localhost/demo";
4. Composite
4.Composite
This pattern deals with a tree-like structure of objects. In this tree, the individual object, or even the entire hierarchy, is treated the same way. In simpler words, this pattern arranges objects in a hierarchical fashion so that a client can work seamlessly with either the part of the whole.
这种模式处理的是对象的树状结构。在这个树状结构中,单个对象,甚至整个层次结构,都以同样的方式处理。用更简单的话来说,这种模式以分层的方式安排对象,这样客户就可以与整体的任何一部分无缝协作。
Nested containers in AWT/Swing are great examples of usages of the composite pattern in core Java. The java.awt.Container object is basically a root component that can contain other components, forming a tree structure of nested components.
AWT/Swing中的嵌套容器是核心Java中使用复合模式的绝佳例子。java.awt.Container对象基本上是一个根组件,可以包含其他组件,形成一个嵌套组件的树状结构。
Consider this code snippet:
考虑一下这个代码片断。
JTabbedPane pane = new JTabbedPane();
pane.addTab("1", new Container());
pane.addTab("2", new JButton());
pane.addTab("3", new JCheckBox());
All the classes used here – namely, JTabbedPane, JButton, JCheckBox, and JFrame – are descendants of Container. As we can see, this code snippet handles the root of the tree or Container, in the second line, in the same way as it handles its children.
这里使用的所有类–即JTabbedPane、JButton、JCheckBox和JFrame–是Container的后代。正如我们所看到的,该代码段在第二行处理树的根或Container,与处理其子代的方式相同。
5. Decorator
5.Decorator
This pattern comes into play when we want to enhance the behavior of an object without modifying the original object itself. This is achieved by adding a wrapper of the same type to the object to attach additional responsibility to it.
当我们想在不修改原始对象本身的情况下增强对象的行为时,这种模式就发挥作用了。这可以通过向对象添加一个相同类型的包装器来实现,以给它附加额外的责任。
One of the most ubiquitous usages of this pattern can be found in the java.io package:
在java.io包中可以找到这种模式最普遍的用法。
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File("test.txt")));
while (bis.available() > 0) {
char c = (char) bis.read();
System.out.println("Char: " + c);
}
Here, BufferedInputStream is decorating the FileInputStream to add the capability to buffer the input. Notably, both these classes have InputStream as a common ancestor. This implies that both the object that decorates and the object that’s being decorated are of the same type. This is an unmistakable indicator of decorator pattern.
这里,BufferedInputStream是对FileInputStream的装饰,以增加缓冲输入的能力。值得注意的是,这两个类都有InputStream作为一个共同的祖先。这意味着,装饰的对象和被装饰的对象都是同一类型。这是一个明确无误的装饰器模式的指标。
6. Facade
6.Facade
By definition, the word facade means an artificial or false appearance of an object. Applied to programming, it similarly means providing another face – or rather, interface – to a complex set of objects.
根据定义,门面这个词的意思是一个物体的人造或虚假的外观。应用于编程,它同样意味着为一组复杂的对象提供另一种面孔–或者说,界面。
This pattern comes into play when we want to simplify or hide the complexity of a subsystem or framework.
当我们想简化或隐藏一个子系统或框架的复杂性时,这种模式就会发挥作用。
Faces API’s ExternalContext is an excellent example of the facade pattern. It uses classes such as HttpServletRequest, HttpServletResponse, and HttpSession internally. Basically, it’s a class that allows the Faces API to be blissfully unaware of its underlying application environment.
Faces API的ExternalContext是facade模式的一个优秀范例。它在内部使用了诸如HttpServletRequest、HttpServletResponse和HttpSession等类。基本上,它是一个允许Faces API对其底层应用环境毫不知情的类。
Let’s look at how Primefaces uses it to write an HttpResponse, without actually knowing about it:
让我们看看Primefaces如何使用它来编写HttpResponse,而实际上并不知道。
protected void writePDFToResponse(ExternalContext externalContext, ByteArrayOutputStream baos, String fileName)
throws IOException, DocumentException {
externalContext.setResponseContentType("application/pdf");
externalContext.setResponseHeader("Expires", "0");
// set more relevant headers
externalContext.setResponseContentLength(baos.size());
externalContext.addResponseCookie(
Constants.DOWNLOAD_COOKIE, "true", Collections.<String, Object>emptyMap());
OutputStream out = externalContext.getResponseOutputStream();
baos.writeTo(out);
// do cleanup
}
As we can see here, we’re setting the response headers, the actual response, and the cookie directly using ExternalContext as a facade. HTTPResponse is not in the picture.
正如我们在这里看到的,我们直接使用ExternalContext作为门面来设置响应头、实际响应和cookie。HTTPResponse不在画面中。
7. Flyweight
7.Flyweight
The flyweight pattern takes the weight, or memory footprint, off of our objects by recycling them. In other words, if we have immutable objects that can share state, as per this pattern, we can cache them to improve system performance.
“轻量级 “模式通过回收对象来减轻其重量或内存占用。换句话说,如果我们有可以共享状态的不可变的对象,按照这种模式,我们可以对它们进行缓存以提高系统性能。
Flyweight can be spotted all over the Number classes in Java.
在Java中的Number类中,到处可以看到Flyweight。
The valueOf methods used to create an object of any data type’s wrapper class are designed to cache values and return them when required.
用于创建任何数据类型的包装类的对象的valueOf方法被设计为缓存值并在需要时返回。
For example, Integer has a static class, IntegerCache, which helps its valueOf method to always cache values in the range -128 to 127:
例如,Integer有一个静态类,IntegerCache,帮助其valueOf方法始终缓存-128到127范围内的值。
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high) {
return IntegerCache.cache[i + (-IntegerCache.low)];
}
return new Integer(i);
}
8. Proxy
8. Proxy
This pattern offers a proxy, or a substitute, to another complex object. While it sounds similar to a facade, it’s actually different in the sense that a facade offers a different interface to the client to interact with. In the case of a proxy, the interface is the same as that of the object it hides.
这种模式为另一个复杂的对象提供了一个代理,或者说一个替代品。虽然它听起来与facade相似,但它实际上是不同的,因为facade为客户提供了一个不同的接口来进行交互。在代理的情况下,该接口与它所隐藏的对象的接口相同。
Using this pattern, it becomes easy to perform any operation on the original object before or after its creation.
使用这种模式,在原始对象创建之前或之后对其进行任何操作都变得容易。
JDK provides a java.lang.reflect.Proxy class out-of-the-box for proxy implementations:
JDK为代理实现提供了一个java.lang.reflect.Proxy类,即开即用。
Foo proxyFoo = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
new Class<?>[] { Foo.class }, handler);
The above code snippet creates a proxy, proxyFoo, for an interface Foo.
上面的代码段为一个接口Foo创建了一个代理,proxyFoo。
9. Conclusion
9.结语
In this short tutorial, we saw practical usages of structural design patterns implemented in core Java.
在这个简短的教程中,我们看到了在核心Java中实现的结构设计模式的实际应用。
To summarize, we briefly defined what each of the seven patterns stands for and then understood them one by one with code snippets.
总结一下,我们简要地定义了七种模式各自代表的含义,然后用代码片段逐一理解。