In a well-designed system, each layer provides a different abstraction from the layers above and below it; if you follow a single operation as it moves up and down through layers by invoking methods, the abstractions changes with each method call.


最上层实现了文件抽象概念 - 由可变长度的字节流数组(可读写)
下一层实现了缓存在固定大小的内存中 - 调用者能快速访问


If a system contains adjacent layers with similar abstractions, this is a red flag that suggests a problem with the class decomposition.


- situations where this happens
	- 包装了一层(pass-through methods 或 decorators)
	- 上下层使用一个概念
	- pass-through arguments
- the problems that result
	- pass-through methods 和 decorators 方式很可能没有提供足够的好处来弥补额外的复杂度(任职成本等)。
	- 如果上层和下层使用了一个概念,可能会增加系统的复杂度
		- 编辑器面向行的接口与面向字符的接口面对多行删除等场景时
	- pass-through arguments 必须得让中间的方法都意识到它的存在
- how to refactor to eliminate the problems
	- 见下文

Pass-through methods

A pass-through method is one that does little except invoke another method, whose signature is similar or identical to that of the calling method.
A pass-through method is one that does nothing except pass its arguments to another method, usually with the same API as the pass-through method.
This typically indicates that there is not a clean division of responsibility between the classes.

Pass-through methods 指那些类似的方法签名类似甚至一致的方法,主要作用是调用其他方法。


Pass-through methods make classes shallower: they increase the interface complexity of the class, which adds complexity, but they don't increase the total functionality of the system.
Pass-through methods also create dependencies between classes
Pass-through methods indicate that there is confusion over the division of responsibility between classes.

pass-through method 带来的问题:


Pass-through methods are bad because they contribute no new functionality.

You will probably notice that there is an overlap in responsibility between the classes.

The solution is to refator the classes so that each class has a distinct and coherent set of responsibilities.

(a) 出现了Pass-through methods
{b} 将底层的类直接暴露给高层类的调用方,
(c) 将能力在类之间重新分配
(d) 如果类不能分割,最好地方式是合并在一起

When is interface dulication OK?

The important thing is that each new method should contribute significant functionality
One example where it's useful for a method to call another method with the same signature is a dispatcher.


A dispatcher is a method that uses its arguments to select one of several other methods to invoke;
then it passes most or all of its arguments to the chosen method.
The signature for the dispatcher is offen the same as the signature for the methods that it calls.
the dispatcher provides useful functionality: it chooses which of several other methods should carry out each task.


For example, when a Web server receives an incomming HTTP request from a web browser, it invokes a dispatcher that examins the URL in the incoming request and selects a specific method to handle the request.
Another method is interface with multiple implementations, such as disk drivers in an operating system.
When several methods provide different implementations of the same interface, it reduces congnitive load...Methods like this are usually in the same layer and they don't invoke each other.





The decorator design pattern (also known as a "wrapper") is one that encourages API duplication across layers.
A decorator object takes an existing object and extends its functionality; it provides an API similar or identical to the underlying object, and its methods invoke the methods of the underlying object.

Java I/O: BufferedInputStream class is a decorator: given a InputStream object, it provides the sampe API but introduces buffering.
sindowing systems: a Window class implements a simple form of window that is not scrollable, an a ScrollableWindow class decorates the Window class by adding horizontal and vertical scrollbars.

The motivation for decorators is to separate special-purpose extensions of a class from a more generic core.


decorator classes tend to be shallow: they introduce a large amount of boilerplate for a small amount of new functionality.
It's easy to overuse the decorator pattern, creating a new class for every small new feature.


Before creating a decorator class, consider alternatives such as the following:

  • Could you add the new functionality directly to the underlying class, rather than creating a decorator class?
    This makes sense if the new functionality is relatively general-purpose, or if it is logically related to the underlying class, or if moset uses of the underlying class will alse use the new functionality.
  • If the new functionality is specialized for a particular use case, would it make sense to merge it with the use case, rather than creating a separate class?
  • Could you merge the new functionality with an existing decorator, rathar than creating a new decorator?
    This would result in a single deeper decorator class rathar than multiple shallow ones.
  • whether the new functionality really needs to wrap the existing functionality: could your implement it as a stand-alone class that is independent of the base class?

Interface versus implementation

the interface of a class should normally be different from its implementation: the representations used internally should be different from the abstractions that appear in the interface.
If the two have similar abstractions, then the class probably isn't very deep




Pass-through variables

Another form of API duplication accross layers is a pass-through variable, which is a variable that is passed down through a long chain of methods.


Pass-through variables add complexity becaouse they force all of the intermidiate methods to be aware of their existence, even though the methods have no use for the variables.
Furthermore, if a new variable comes into existence, you may have to modify a large number of interfaces and methods to pass the variable through all of the relevant paths.

消除Pass-through variables
shared object

One approach is to see if there is already an object shared between the topmost and bottommost methods.

global variable

Another approach is to store the information in a global variable.
global variables make it impossible to create two independent instances of the same system in the same process, since accesses to the global variables will confilct.

context object

introduce a context object.
stores all of the application's global state.
The context allows multiple instances of the system to coexist in a single process, each with its own context.
th class containing m3 stores a reference to the context as an instance variable in its objects. When a new object is created, the creating method retrieves the context reference from its object and passes it to the constructor for the new object.
the context is available every where, but it only appears as an explicit argument in constructors.

The context object unifies the handling of all system-global information and eliminates the need for pass-through variables.
If a new variable needs to be added, it can be added to the context object; no existing code is affected except for the constructor and destructor for the context.
The context is also convenient for testing: test code can change the global configuration of the application by modifying fields in the context.

Contexts are far from an idea solution.The variables stored in a context have most of the disadvantages of global variables;

  • it may not be obvious why a particular variable is present, or where it is used.
  • without discipline, a context can turn intu a huge grab-bag of data that creates nonobvious dependencies throughout the system
  • may also create thread-safety issues; the best way to avoid problems is for variables in a context tob immutable.
列举了三种解决Pass-through variables的方法,



Each piece of design infrastructure added to a system, such as an interface, argument, function, class, or definition, adds complexity, since developers must learn about this element.
In order for an element to provide a net gain against complexity, it must eliminate some complexity that would be present in the absence of the disign elemnt. Otherwise, you are better off implementing the system without that particular element.

任何元素的增加都会增加系统的复杂度 —— 增加认知成本

The "different layer, different abstraction" rule is just an application of this idea: if different layers have the same abstraction ,such as pass-through methods or decorators, then there's a good chance that they haven't provided enough benefit to compensate for the additional infrastructure they represent.


pass-through arguments必须得让那些方法意识到它们的存在,但是没有贡献额外的功能。