Full Code of glen9527/Clean-Code-zh for AI

master 28a2ca4069d9 cached
25 files
704.8 KB
184.9k tokens
1 requests
Download .txt
Showing preview only (822K chars total). Download the full file or copy to clipboard to get everything.
Repository: glen9527/Clean-Code-zh
Branch: master
Commit: 28a2ca4069d9
Files: 25
Total size: 704.8 KB

Directory structure:
gitextract_0vuhkduc/

├── .gitignore
├── LICENSE
├── README.md
├── docs/
│   ├── .vuepress/
│   │   └── config.js
│   ├── README.md
│   ├── apA.md
│   ├── ch1.md
│   ├── ch10.md
│   ├── ch11.md
│   ├── ch12.md
│   ├── ch13.md
│   ├── ch14.md
│   ├── ch15.md
│   ├── ch16.md
│   ├── ch17.md
│   ├── ch2.md
│   ├── ch3.md
│   ├── ch4.md
│   ├── ch5.md
│   ├── ch6.md
│   ├── ch7.md
│   ├── ch8.md
│   └── ch9.md
├── gitee-deploy.sh
└── package.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
node_modules/
docs/.vuepress/dist/

================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2018-present, Yuxi (Evan) You

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

================================================
FILE: README.md
================================================
# Clean-Code-zh

《代码整洁之道》中文翻译

在线阅读:[http://gdut_yy.gitee.io/doc-cleancode/](http://gdut_yy.gitee.io/doc-cleancode/)

<img src="./docs/cover.jpg" width=24% />

## 前言

## Index

- [第 1 章 整洁代码](docs/ch1.md)
- [第 2 章 有意义的命名](docs/ch2.md)
- [第 3 章 函数](docs/ch3.md)
- [第 4 章 注释](docs/ch4.md)
- [第 5 章 格式](docs/ch5.md)
- [第 6 章 对象和数据结构](docs/ch6.md)
- [第 7 章 错误处理](docs/ch7.md)
- [第 8 章 边界](docs/ch8.md)
- [第 9 章 单元测试](docs/ch9.md)
- [第 10 章 类](docs/ch10.md)
- [第 11 章 系统](docs/ch11.md)
- [第 12 章 迭进](docs/ch12.md)
- [第 13 章 并发编程](docs/ch13.md)
- [第 14 章 逐步改进](docs/ch14.md)
- [第 15 章 JUnit 内幕](docs/ch15.md)
- [第 16 章 重构 SerialDate](docs/ch16.md)
- [第 17 章 味道与启发](docs/ch17.md)
- [附录 A 并发编程 II](docs/apA.md)

## 本地开发 & 阅读

本项目基于 vuepress 进行开发,以提供比 github mardown 更佳的阅读体验

依赖于 `node.js`、`yarn`、`vuepress` 等环境

```sh
# vuepress
yarn global add vuepress

# 本地开发
git clone https://github.com/gdut-yy/Clean-Code-zh.git
cd Clean-Code-zh/
yarn docs:dev

# 本地阅读
http://localhost:8080/doc-cleancode/
```

## License

[MIT](https://github.com/gdut-yy/Clean-Code-zh/blob/master/LICENSE)


================================================
FILE: docs/.vuepress/config.js
================================================
// .vuepress/config.js
module.exports = {
  // 网站的标题
  title: "Clean Code 中文",
  // 上下文根
  base: "/doc-cleancode/",
  themeConfig: {
    // 假定是 GitHub. 同时也可以是一个完整的 GitLab URL
    repo: "gdut-yy/Clean-Code-zh",
    // 自定义仓库链接文字。默认从 `themeConfig.repo` 中自动推断为
    // "GitHub"/"GitLab"/"Bitbucket" 其中之一,或是 "Source"。
    repoLabel: "Github",
    // 以下为可选的编辑链接选项
    // 假如你的文档仓库和项目本身不在一个仓库:
    docsRepo: "gdut-yy/Clean-Code-zh",
    // 假如文档放在一个特定的分支下:
    docsBranch: "master/docs",
    // 默认是 false, 设置为 true 来启用
    editLinks: true,
    // 默认为 "Edit this page"
    editLinkText: "帮助我们改善此页面!",
    // 最后更新时间
    lastUpdated: "Last Updated",
    // 最大深度
    sidebarDepth: 2,
    // 导航栏
    nav: [],
    // 侧边栏
    sidebar: {
      "/": [
        "",
        "ch1.md",
        "ch2.md",
        "ch3.md",
        "ch4.md",
        "ch5.md",
        "ch6.md",
        "ch7.md",
        "ch8.md",
        "ch9.md",
        "ch10.md",
        "ch11.md",
        "ch12.md",
        "ch13.md",
        "ch14.md",
        "ch15.md",
        "ch16.md",
        "ch17.md",
        "apA.md"
      ]
    }
  }
};


================================================
FILE: docs/README.md
================================================
# Clean Code 中文

<div style="margin: 0 auto; width: 40%;">
  <img src='./cover.jpg'/>
</div>

## 序

## 目录

- [第 1 章 整洁代码](ch1.md)
- [第 2 章 有意义的命名](ch2.md)
- [第 3 章 函数](ch3.md)
- [第 4 章 注释](ch4.md)
- [第 5 章 格式](ch5.md)
- [第 6 章 对象和数据结构](ch6.md)
- [第 7 章 错误处理](ch7.md)
- [第 8 章 边界](ch8.md)
- [第 9 章 单元测试](ch9.md)
- [第 10 章 类](ch10.md)
- [第 11 章 系统](ch11.md)
- [第 12 章 迭进](ch12.md)
- [第 13 章 并发编程](ch13.md)
- [第 14 章 逐步改进](ch14.md)
- [第 15 章 JUnit 内幕](ch15.md)
- [第 16 章 重构 SerialDate](ch16.md)
- [第 17 章 味道与启发](ch17.md)
- [附录 A 并发编程 II](apA.md)


================================================
FILE: docs/apA.md
================================================
# Appendix A
Concurrency II
by Brett L. Schuchert

This appendix supports and amplifies the Concurrency chapter on page 177. It is written as a series of independent topics and you can generally read them in any order. There is some duplication between sections to allow for such reading.

CLIENT/SERVER EXAMPLE
Imagine a simple client/server application. A server sits and waits listening on a socket for a client to connect. A client connects and sends a request.

The Server
Here is a simplified version of a server application. Full source for this example is available starting on page 343, Client/Server Nonthreaded.
```java
   ServerSocket serverSocket = new ServerSocket(8009);
 
   while (keepProcessing) {
       try {
           Socket socket = serverSocket.accept();
           process(socket);
       } catch (Exception e) {
           handle(e);
       }
   }
```
This simple application waits for a connection, processes an incoming message, and then again waits for the next client request to come in. Here’s client code that connects to this server:
```java
   private void connectSendReceive(int i) {
       try {
           Socket socket = new Socket(“localhost”, PORT);
           MessageUtils.sendMessage(socket, Integer.toString(i));
           MessageUtils.getMessage(socket);
           socket.close();
       } catch (Exception e) {
           e.printStackTrace();
       }
   }
```
How well does this client/server pair perform? How can we formally describe that performance? Here’s a test that asserts that the performance is “acceptable”:
```java
   @Test(timeout = 10000)
   public void shouldRunInUnder10Seconds() throws Exception {
        Thread[] threads = createThreads();
        startAllThreadsw(threads);
        waitForAllThreadsToFinish(threads);
   }
```
The setup is left out to keep the example simple (see “ClientTest.java” on page 344). This test asserts that it should complete within 10,000 milliseconds.

This is a classic example of validating the throughput of a system. This system should complete a series of client requests in ten seconds. So long as the server can process each individual client request in time, the test will pass.

What happens if the test fails? Short of developing some kind of event polling loop, there is not much to do within a single thread that will make this code any faster. Will using multiple threads solve the problem? It might, but we need to know where the time is being spent. There are two possibilities:

- I/O—using a socket, connecting to a database, waiting for virtual memory swapping, and so on.
- Processor—numerical calculations, regular expression processing, garbage collection, and so on.

Systems typically have some of each, but for a given operation one tends to dominate. If the code is processor bound, more processing hardware can improve throughput, making our test pass. But there are only so many CPU cycles available, so adding threads to a processor-bound problem will not make it go faster.

On the other hand, if the process is I/O bound, then concurrency can increase efficiency. When one part of the system is waiting for I/O, another part can use that wait time to process something else, making more effective use of the available CPU.

Adding Threading
Assume for the moment that the performance test fails. How can we improve the throughput so that the performance test passes? If the process method of the server is I/O bound, then here is one way to make the server use threads (just change the processMessage):
```java
   void process(final Socket socket) {
       if (socket == null)
           return;
 
       Runnable clientHandler = new Runnable() {
           public void run() {
               try {
                   String message = MessageUtils.getMessage(socket);
                   MessageUtils.sendMessage(socket, “Processed: ” + message);
                   closeIgnoringException(socket);
               } catch (Exception e) {
                   e.printStackTrace();
               }
           }
       };
 
       Thread clientConnection = new Thread(clientHandler);
       clientConnection.start();
   }
```
Assume that this change causes the test to pass;1 the code is complete, correct?

1. You can verify that for yourself by trying out the before and after code. Review the nonthreaded code starting on page 343. Review the threaded code starting on page 346.

Server Observations
The updated server completes the test successfully in just over one second. Unfortunately, this solution is a bit naive and introduces some new problems.

How many threads might our server create? The code sets no limit, so the we could feasibly hit the limit imposed by the Java Virtual Machine (JVM). For many simple systems this may suffice. But what if the system is meant to support many users on the public net? If too many users connect at the same time, the system might grind to a halt.

But set the behavioral problem aside for the moment. The solution shown has problems of cleanliness and structure. How many responsibilities does the server code have?

- Socket connection management
- Client processing
- Threading policy
- Server shutdown policy

Unfortunately, all these responsibilities live in the process function. In addition, the code crosses many different levels of abstraction. So, small as the process function is, it needs to be repartitioned.

The server has several reasons to change; therefore it violates the Single Responsibility Principle. To keep concurrent systems clean, thread management should be kept to a few, well-controlled places. What’s more, any code that manages threads should do nothing other than thread management. Why? If for no other reason than that tracking down concurrency issues is hard enough without having to unwind other nonconcurrency issues at the same time.

If we create a separate class for each of the responsibilities listed above, including the thread management responsibility, then when we change the thread management strategy, the change will impact less overall code and will not pollute the other responsibilities. This also makes it much easier to test all the other responsibilities without having to worry about threading. Here is an updated version that does just that:
```java
   public void run() {
     while (keepProcessing) {
      try {
       ClientConnection clientConnection = connectionManager.awaitClient();
       ClientRequestProcessor requestProcessor
         = new ClientRequestProcessor(clientConnection);
       clientScheduler.schedule(requestProcessor);
       } catch (Exception e) {
         e.printStackTrace();
       }
     }
     connectionManager.shutdown();
   }
```
This now focuses all things thread-related into one place, clientScheduler. If there are concurrency problems, there is just one place to look:
```java
   public interface ClientScheduler {
       void schedule(ClientRequestProcessor requestProcessor);
   }
```
The current policy is easy to implement:
```java
   public class ThreadPerRequestScheduler implements ClientScheduler {
       public void schedule(final ClientRequestProcessor requestProcessor) {
           Runnable runnable = new Runnable() {
               public void run() {
                   requestProcessor.process();
               }
           };
 
          Thread thread = new Thread(runnable);
          thread.start();
       }
   }
```
Having isolated all the thread management into a single place, it is much easier to change the way we control threads. For example, moving to the Java 5 Executor framework involves writing a new class and plugging it in (Listing A-1).


Listing A-1 ExecutorClientScheduler.java
```java
   import java.util.concurrent.Executor;
   import java.util.concurrent.Executors;
 
   public class ExecutorClientScheduler implements ClientScheduler {
       Executor executor;
 
       public ExecutorClientScheduler(int availableThreads) {
           executor = Executors.newFixedThreadPool(availableThreads);
        }
 
       public void schedule(final ClientRequestProcessor requestProcessor) {
           Runnable runnable = new Runnable() {
               public void run() {
                   requestProcessor.process();
               }
           };
           executor.execute(runnable);
        }
   }
```
Conclusion
Introducing concurrency in this particular example demonstrates a way to improve the throughput of a system and one way of validating that throughput through a testing framework. Focusing all concurrency code into a small number of classes is an example of applying the Single Responsibility Principle. In the case of concurrent programming, this becomes especially important because of its complexity.

POSSIBLE PATHS OF EXECUTION
Review the method incrementValue, a one-line Java method with no looping or branching:
```java
   public class IdGenerator {
     int lastIdUsed;
 
     public int incrementValue() {
       return ++lastIdUsed;
     }
   }
```
Ignore integer overflow and assume that only one thread has access to a single instance of IdGenerator. In this case there is a single path of execution and a single guaranteed result:

- The value returned is equal to the value of lastIdUsed, both of which are one greater than just before calling the method.

What happens if we use two threads and leave the method unchanged? What are the possible outcomes if each thread calls incrementValue once? How many possible paths of execution are there? First, the outcomes (assume lastIdUsed starts with a value of 93):

- Thread 1 gets the value of 94, thread 2 gets the value of 95, and lastIdUsed is now 95.
- Thread 1 gets the value of 95, thread 2 gets the value of 94, and lastIdUsed is now 95.
- Thread 1 gets the value of 94, thread 2 gets the value of 94, and lastIdUsed is now 94.

The final result, while surprising, is possible. To see how these different results are possible, we need to understand the number of possible paths of execution and how the Java Virtual Machine executes them.

Number of Paths
To calculate the number of possible execution paths, we’ll start with the generated byte-code. The one line of java (return ++lastIdUsed;) becomes eight byte-code instructions. It is possible for the two threads to interleave the execution of these eight instructions the way a card dealer interleaves cards as he shuffles a deck.2 Even with only eight cards in each hand, there are a remarkable number of shuffled outcomes.

2. This is a bit of a simplification. However, for the purpose of this discussion, we can use this simplifying model.

For this simple case of N instructions in a sequence, no looping or conditionals, and T threads, the total number of possible execution paths is equal to

![](figures/apA/322equ01.jpg)

Calculating the Possible Orderings

This comes from an email from Uncle Bob to Brett:

With N steps and T threads there are T* N total steps. Prior to each step there is a context switch that chooses between the T threads. Each path can thus be represented as a string of digits denoting the context switches. Given steps A and B and threads 1 and 2, the six possible paths are 1122, 1212, 1221, 2112, 2121, and 2211. Or, in terms of steps it is A1B1A2B2, A1A2B1B2, A1A2B2B1, A2A1B1B2, A2A1B2B1, and A2B2A1B1. For three threads the sequence is 112233, 112323, 113223, 113232, 112233, 121233, 121323, 121332, 123132, 123123, ….

One characteristic of these strings is that there must always be N instances of each T. So the string 111111 is invalid because it has six instances of 1 and zero instances of 2 and 3.

So we want the permutations of N 1’s, N 2’s, … and N T’s. This is really just the permutations of N* T things taken N* T at a time, which is (N* T)!, but with all the duplicates removed. So the trick is to count the duplicates and subtract that from (N* T)!.

Given two steps and two threads, how many duplicates are there? Each four-digit string has two 1s and two 2s. Each of those pairs could be swapped without changing the sense of the string. You could swap the 1s or the 2s both, or neither. So there are four isomorphs for each string, which means that there are three duplicates. So three out of four of the options are duplicates; alternatively one of four of the permutations are NOT duplicates. 4! * .25 = 6. So this reasoning seems to work.

How many duplicates are there? In the case where N = 2 and T = 2, I could swap the 1s, the 2s, or both. In the case where N = 2 and T = 3, I could swap the 1s, the 2s, the 3s, 1s and 2s, 1s and 3s, or 2s and 3s. Swapping is just the permutations of N. Let’s say there are P permutations of N. The number of different ways to arrange those permutations are P**T.

So the number of possible isomorphs is N!**T. And so the number of paths is (T*N)!/(N!**T). Again, in our T = 2, N = 2 case we get 6 (24/4).

For N = 2 and T = 3 we get 720/8 = 90.

For N = 3 and T = 3 we get 9!/6^3 = 1680.

For our simple case of one line of Java code, which equates to eight lines of byte-code and two threads, the total number of possible paths of execution is 12,870. If the type of lastIdUsed is a long, then every read/write becomes two operations instead of one, and the number of possible orderings becomes 2,704,156.

What happens if we make one change to this method?
```java
   public synchronized void incrementValue() {
       ++lastIdUsed;
   }

The number of possible execution pathways becomes two for two threads and N! in the general case.

Digging Deeper
What about the surprising result that two threads could both call the method once (before we added synchronized) and get the same numeric result? How is that possible? First things first.

What is an atomic operation? We can define an atomic operation as any operation that is uninterruptable. For example, in the following code, line 5, where 0 is assigned to lastid, is atomic because according to the Java Memory model, assignment to a 32-bit value is uninterruptable.
```java
   01: public class Example {
   02:    int lastId;
   03:
   04:    public void resetId() {
   05:        value = 0;
   06:    }
   07:
   08:    public int getNextId() {
   09:        ++value;
   10:    }
   11:}
```
What happens if we change type of lastId from int to long? Is line 5 still atomic? Not according to the JVM specification. It could be atomic on a particular processor, but according to the JVM specification, assignment to any 64-bit value requires two 32-bit assignments. This means that between the first 32-bit assignment and the second 32-bit assignment, some other thread could sneak in and change one of the values.

What about the pre-increment operator, ++, on line 9? The pre-increment operator can be interrupted, so it is not atomic. To understand, let’s review the byte-code of both of these methods in detail.

Before we go any further, here are three definitions that will be important:

- Frame—Every method invocation requires a frame. The frame includes the return address, any parameters passed into the method and the local variables defined in the method. This is a standard technique used to define a call stack, which is used by modern languages to allow for basic function/method invocation and to allow for recursive invocation.

- Local variable—Any variables defined in the scope of the method. All nonstatic methods have at least one variable, this, which represents the current object, the object that received the most recent message (in the current thread), which caused the method invocation.

- Operand stack—Many of the instructions in the Java Virtual Machine take parameters. The operand stack is where those parameters are put. The stack is a standard last-in, first-out (LIFO) data structure.

Here is the byte-code generated for resetId():

![](figures/apA/0324tab01.jpg)

![](figures/apA/0325tab01.jpg)

These three instructions are guaranteed to be atomic because, although the thread executing them could be interrupted after any one of them, the information for the PUTFIELD instruction (the constant value 0 on the top of the stack and the reference to this one below the top, along with the field value) cannot be touched by another thread. So when the assignment occurs, we are guaranteed that the value 0 will be stored in the field value. The operation is atomic. The operands all deal with information local to the method, so there is no interference between multiple threads.

So if these three instructions are executed by ten threads, there are 4.38679733629e+24 possible orderings. However, there is only one possible outcome, so the different orderings are irrelevant. It just so happens that the same outcome is guaranteed for longs in this case as well. Why? All ten threads are assigning a constant value. Even if they interleave with each other, the end result is the same.

With the ++ operation in the getNextId method, there are going to be problems. Assume that lastId holds 42 at the beginning of this method. Here is the byte-code for this new method:

![](figures/apA/0325tab02.jpg)

Imagine the case where the first thread completes the first three instructions, up to and including GETFIELD, and then it is interrupted. A second thread takes over and performs the entire method, incrementing lastId by one; it gets 43 back. Then the first thread picks up where it left off; 42 is still on the operand stack because that was the value of lastId when it executed GETFIELD. It adds one to get 43 again and stores the result. The value 43 is returned to the first thread as well. The result is that one of the increments is lost because the first thread stepped on the second thread after the second thread interrupted the first thread.

Making the getNexId() method synchronized fixes this problem.

Conclusion
An intimate understanding of byte-code is not necessary to understand how threads can step on each other. If you can understand this one example, it should demonstrate the possibility of multiple threads stepping on each other, which is enough knowledge.

That being said, what this trivial example demonstrates is a need to understand the memory model enough to know what is and is not safe. It is a common misconception that the ++ (pre- or post-increment) operator is atomic, and it clearly is not. This means you need to know:

- Where there are shared objects/values
- The code that can cause concurrent read/update issues
- How to guard such concurrent issues from happening

KNOWING YOUR LIBRARY
Executor Framework
As demonstrated in the ExecutorClientScheduler.java on page 321, the Executor framework introduced in Java 5 allows for sophisticated execution using thread pools. This is a class in the java.util.concurrent package.

If you are creating threads and are not using a thread pool or are using a hand-written one, you should consider using the Executor. It will make your code cleaner, easier to follow, and smaller.

The Executor framework will pool threads, resize automatically, and recreate threads if necessary. It also supports futures, a common concurrent programming construct. The Executor framework works with classes that implement Runnable and also works with classes that implement the Callable interface. A Callable looks like a Runnable, but it can return a result, which is a common need in multithreaded solutions.

A future is handy when code needs to execute multiple, independent operations and wait for both to finish:
```java
   public String processRequest(String message) throws Exception {
       Callable<String> makeExternalCall = new Callable<String>() {

           public String call() throws Exception {
               String result = “”;
               // make external request
               return result;
           }
       };
 
       Future<String> result = executorService.submit(makeExternalCall);
       String partialResult = doSomeLocalProcessing();
       return result.get() + partialResult;
   }
```
In this example, the method starts executing the makeExternalCall object. The method continues other processing. The final line calls result.get(), which blocks until the future completes.

Nonblocking Solutions
The Java 5 VM takes advantage of modern processor design, which supports reliable, nonblocking updates. Consider, for example, a class that uses synchronization (and therefore blocking) to provide a thread-safe update of a value:
```java
   public class ObjectWithValue {
       private int value;
       public void synchronized incrementValue() { ++value; }
       public int getValue() { return value; }
   }
```
Java 5 has a series of new classes for situations like this: AtomicBoolean, AtomicInteger, and AtomicReference are three examples; there are several more. We can rewrite the above code to use a nonblocking approach as follows:
```java
   public class ObjectWithValue {
       private AtomicInteger value = new AtomicInteger(0);
 
       public void incrementValue() {
       value.incrementAndGet();
       }
       public int getValue() {
           return value.get();
       }
   }
```
Even though this uses an object instead of a primitive and sends messages like incrementAndGet() instead of ++, the performance of this class will nearly always beat the previous version. In some cases it will only be slightly faster, but the cases where it will be slower are virtually nonexistent.

How is this possible? Modern processors have an operation typically called Compare and Swap (CAS). This operation is analogous to optimistic locking in databases, whereas the synchronized version is analogous to pessimistic locking.

The synchronized keyword always acquires a lock, even when a second thread is not trying to update the same value. Even though the performance of intrinsic locks has improved from version to version, they are still costly.

The nonblocking version starts with the assumption that multiple threads generally do not modify the same value often enough that a problem will arise. Instead, it efficiently detects whether such a situation has occurred and retries until the update happens successfully. This detection is almost always less costly than acquiring a lock, even in moderate to high contention situations.

How does the Virtual Machine accomplish this? The CAS operation is atomic. Logically, the CAS operation looks something like the following:
```java
   int variableBeingSet;
 
   void simulateNonBlockingSet(int newValue) {
       int currentValue;
       do {
          currentValue = variableBeingSet
       } while(currentValue != compareAndSwap(currentValue, newValue));
   }
 
   int synchronized compareAndSwap(int currentValue, int newValue) {
       if(variableBeingSet == currentValue) {
           variableBeingSet = newValue;
           return currentValue;
       }
       return variableBeingSet; 
   }
```
When a method attempts to update a shared variable, the CAS operation verifies that the variable getting set still has the last known value. If so, then the variable is changed. If not, then the variable is not set because another thread managed to get in the way. The method making the attempt (using the CAS operation) sees that the change was not made and retries.

Nonthread-Safe Classes
There are some classes that are inherently not thread safe. Here are a few examples:

- SimpleDateFormat
- Database Connections
- Containers in java.util
- Servlets

Note that some collection classes have individual methods that are thread-safe. However, any operation that involves calling more than one method is not. For example, if you do not want to replace something in a HashTable because it is already there, you might write the following code:
```java
   if(!hashTable.containsKey(someKey)) {
       hashTable.put(someKey, new SomeValue());
   }
```
Each individual method is thread-safe. However, another thread might add a value in between the containsKey and put calls. There are several options to fix this problem.

- Lock the HashTable first, and make sure all other users of the HashTable do the same—client-based locking:
```java
   synchronized(map) {
   if(!map.conainsKey(key))
       map.put(key, value);
   }
```
- Wrap the HashTable in its own object and use a different API—server-based locking using an ADAPTER:
```java
   public class WrappedHashtable<K, V> {
       private Map<K, V> map = new Hashtable<K, V>();
 
       public synchronized void putIfAbsent(K key, V value) {
           if (map.containsKey(key))
               map.put(key, value);
       }
   }
```
- Use the thread-safe collections:
```java
   ConcurrentHashMap<Integer, String> map = new ConcurrentHashMap<Integer,
   String>();
   map.putIfAbsent(key, value);
```
The collections in java.util.concurrent have operations like putIfAbsent() to accommodate such operations.

DEPENDENCIES BETWEEN METHODS CAN BREAK CONCURRENT CODE
Here is a trivial example of a way to introduce dependencies between methods:
```java
   public class IntegerIterator implements Iterator<Integer>
       private Integer nextValue = 0;
 
       public synchronized boolean hasNext() {
           return nextValue < 100000;
       }
       public synchronized Integer next() {
           if (nextValue == 100000)
               throw new IteratorPastEndException();
           return nextValue++;
       }
       public synchronized Integer getNextValue() {
           return nextValue;
       }
   }
```
Here is some code to use this IntegerIterator:
```java
   IntegerIterator iterator = new IntegerIterator();
   while(iterator.hasNext()) {
      int nextValue = iterator.next();
      // do something with nextValue
   }
```
If one thread executes this code, there will be no problem. But what happens if two threads attempt to share a single instance of IngeterIterator with the intent that each thread will process the values it gets, but that each element of the list is processed only once? Most of the time, nothing bad happens; the threads happily share the list, processing the elements they are given by the iterator and stopping when the iterator is complete. However, there is a small chance that, at the end of the iteration, the two threads will interfere with each other and cause one thread to go beyond the end of the iterator and throw an exception.

Here’s the problem: Thread 1 asks the question hasNext(), which returns true. Thread 1 gets preempted and then Thread 2 asks the same question, which is still true. Thread 2 then calls next(), which returns a value as expected but has a side effect of making hasNext() return false. Thread 1 starts up again, thinking hasNext() is still true, and then calls next(). Even though the individual methods are synchronized, the client uses two methods.

This is a real problem and an example of the kinds of problems that crop up in concurrent code. In this particular situation this problem is especially subtle because the only time where this causes a fault is when it happens during the final iteration of the iterator. If the threads happen to break just right, then one of the threads could go beyond the end of the iterator. This is the kind of bug that happens long after a system has been in production, and it is hard to track down.

You have three options:

- Tolerate the failure.
- Solve the problem by changing the client: client-based locking
- Solve the problem by changing the server, which additionally changes the client: server-based locking

Tolerate the Failure
Sometimes you can set things up such that the failure causes no harm. For example, the above client could catch the exception and clean up. Frankly, this is a bit sloppy. It’s rather like cleaning up memory leaks by rebooting at midnight.

Client-Based Locking
To make IntegerIterator work correctly with multiple threads, change this client (and every other client) as follows:
```java
   IntegerIterator iterator = new IntegerIterator();
 
       while (true) {
         int nextValue;
     synchronized (iterator) {
       if (!iterator.hasNext())
         break;
       nextValue = iterator.next();
     }
     doSometingWith(nextValue);
   }
```
Each client introduces a lock via the synchronized keyword. This duplication violates the DRY principle, but it might be necessary if the code uses non-thread-safe third-party tools.

This strategy is risky because all programmers who use the server must remember to lock it before using it and unlock it when done. Many (many!) years ago I worked on a system that employed client-based locking on a shared resource. The resource was used in hundreds of different places throughout the code. One poor programmer forgot to lock the resource in one of those places.

The system was a multi-terminal time-sharing system running accounting software for Local 705 of the trucker’s union. The computer was in a raised-floor, environment-controlled room 50 miles north of the Local 705 headquarters. At the headquarters they had dozens of data entry clerks typing union dues postings into the terminals. The terminals were connected to the computer using dedicated phone lines and 600bps half-duplex modems. (This was a very, very long time ago.)

About once per day, one of the terminals would “lock up.” There was no rhyme or reason to it. The lock up showed no preference for particular terminals or particular times. It was as though there were someone rolling dice choosing the time and terminal to lock up. Sometimes more than one terminal would lock up. Sometimes days would go by without any lock-ups.

At first the only solution was a reboot. But reboots were tough to coordinate. We had to call the headquarters and get everyone to finish what they were doing on all the terminals. Then we could shut down and restart. If someone was doing something important that took an hour or two, the locked up terminal simply had to stay locked up.

After a few weeks of debugging we found that the cause was a ring-buffer counter that had gotten out of sync with its pointer. This buffer controlled output to the terminal. The pointer value indicated that the buffer was empty, but the counter said it was full. Because it was empty, there was nothing to display; but because it was also full, nothing could be added to the buffer to be displayed on the screen.

So we knew why the terminals were locking, but we didn’t know why the ring buffer was getting out of sync. So we added a hack to work around the problem. It was possible to read the front panel switches on the computer. (This was a very, very, very long time ago.) We wrote a little trap function that detected when one of these switches was thrown and then looked for a ring buffer that was both empty and full. If one was found, it reset that buffer to empty. Voila! The locked-up terminal(s) started displaying again.

So now we didn’t have to reboot the system when a terminal locked up. The Local would simply call us and tell us we had a lock-up, and then we just walked into the computer room and flicked a switch.

Of course sometimes they worked on the weekends, and we didn’t. So we added a function to the scheduler that checked all the ring buffers once per minute and reset any that were both empty and full. This caused the displays to unclog before the Local could even get on the phone.

It was several more weeks of poring over page after page of monolithic assembly language code before we found the culprit. We had done the math and calculated that the frequency of the lock-ups was consistent with a single unprotected use of the ring buffer. So all we had to do was find that one faulty usage. Unfortunately, this was so very long ago that we didn’t have search tools or cross references or any other kind of automated help. We simply had to pore over listings.

I learned an important lesson that cold Chicago winter of 1971. Client-based locking really blows.

Server-Based Locking
The duplication can be removed by making the following changes to IntegerIterator:
```java
   public class IntegerIteratorServerLocked {
       private Integer nextValue = 0;
       public synchronized Integer getNextOrNull() {
           if (nextValue < 100000)
               return nextValue++;
           else
              return null;
       }
   }
```
And the client code changes as well:
```java
   while (true) {
       Integer nextValue = iterator.getNextOrNull();
       if (next == null)
           break;
       // do something with nextValue
   }
```
In this case we actually change the API of our class to be multithread aware.3 The client needs to perform a null check instead of checking hasNext().

3. In fact, the Iterator interface is inherently not thread-safe. It was never designed to be used by multiple threads, so this should come as no surprise.

In general you should prefer server-based locking for these reasons:

- It reduces repeated code—Client-based locking forces each client to lock the server properly. By putting the locking code into the server, clients are free to use the object and not worry about writing additional locking code.
- It allows for better performance—You can swap out a thread-safe server for a non-thread safe one in the case of single-threaded deployment, thereby avoiding all overhead.
- It reduces the possibility of error—All it takes is for one programmer to forget to lock properly.
- It enforces a single policy—The policy is in one place, the server, rather than many places, each client.
- It reduces the scope of the shared variables—The client is not aware of them or how they are locked. All of that is hidden in the server. When things break, the number of places to look is smaller.

What if you do not own the server code?

- Use an ADAPTER to change the API and add locking
```java
   public class ThreadSafeIntegerIterator {
       private IntegerIterator iterator = new IntegerIterator();
 
       public synchronized Integer getNextOrNull() {
           if(iterator.hasNext())
               return iterator.next();
           return null;
       }
   }
```
- OR better yet, use the thread-safe collections with extended interfaces

INCREASING THROUGHPUT
Let’s assume that we want to go out on the net and read the contents of a set of pages from a list of URLs. As each page is read, we will parse it to accumulate some statistics. Once all the pages are read, we will print a summary report.

The following class returns the contents of one page, given a URL.
```java
   public class PageReader {
     //…
     public String getPageFor(String url) {
       HttpMethod method = new GetMethod(url);
 
       try {
         httpClient.executeMethod(method);
         String response = method.getResponseBodyAsString();
         return response;
       } catch (Exception e) {
         handle(e);
       } finally {
         method.releaseConnection();
       }
     }
   }
```
The next class is the iterator that provides the contents of the pages based on an iterator of URLs:
```java
   public class PageIterator {
     private PageReader reader;
     private URLIterator urls;
 
     public PageIterator(PageReader reader, URLIterator urls) {
       this.urls = urls;
       this.reader = reader;
     }
 
     public synchronized String getNextPageOrNull() {
       if (urls.hasNext())
         getPageFor(urls.next());
       else
         return null;
     }
 
     public String getPageFor(String url) {
       return reader.getPageFor(url);
     }
   }
```
An instance of the PageIterator can be shared between many different threads, each one using it’s own instance of the PageReader to read and parse the pages it gets from the iterator.

Notice that we’ve kept the synchronized block very small. It contains just the critical section deep inside the PageIterator. It is always better to synchronize as little as possible as opposed to synchronizing as much as possible.

Single-Thread Calculation of Throughput
Now lets do some simple calculations. For the purpose of argument, assume the following:

- I/O time to retrieve a page (average): 1 second
- Processing time to parse page (average): .5 seconds
- I/O requires 0 percent of the CPU while processing requires 100 percent.

For N pages being processed by a single thread, the total execution time is 1.5 seconds * N. Figure A-1 shows a snapshot of 13 pages or about 19.5 seconds.


Figure A-1 Single thread

![](figures/apA/x01-1single_thread.jpg)

Multithread Calculation of Throughput
If it is possible to retrieve pages in any order and process the pages independently, then it is possible to use multiple threads to increase throughput. What happens if we use three threads? How many pages can we acquire in the same time?

As you can see in Figure A-2, the multithreaded solution allows the process-bound parsing of the pages to overlap with the I/O-bound reading of the pages. In an idealized world this means that the processor is fully utilized. Each one-second page read is overlapped with two parses. Thus, we can process two pages per second, which is three times the throughput of the single-threaded solution.


Figure A-2 Three concurrent threads

![](figures/apA/x01-2multi_thread.jpg)

DEADLOCK
Imagine a Web application with two shared resource pools of some finite size:

- A pool of database connections for local work in process storage
- A pool of MQ connections to a master repository

Assume there are two operations in this application, create and update:

- Create—Acquire connection to master repository and database. Talk to service master repository and then store work in local work in process database.
- Update—Acquire connection to database and then master repository. Read from work in process database and then send to the master repository

What happens when there are more users than the pool sizes? Consider each pool has a size of ten.

- Ten users attempt to use create, so all ten database connections are acquired, and each thread is interrupted after acquiring a database connection but before acquiring a connection to the master repository.
- Ten users attempt to use update, so all ten master repository connections are acquired, and each thread is interrupted after acquiring the master repository but before acquiring a database connection.
- Now the ten “create” threads must wait to acquire a master repository connection, but the ten “update” threads must wait to acquire a database connection.
- Deadlock. The system never recovers.

This might sound like an unlikely situation, but who wants a system that freezes solid every other week? Who wants to debug a system with symptoms that are so difficult to reproduce? This is the kind of problem that happens in the field, then takes weeks to solve.

A typical “solution” is to introduce debugging statements to find out what is happening. Of course, the debug statements change the code enough so that the deadlock happens in a different situation and takes months to again occur.4

4. For example, someone adds some debugging output and the problem “disappears.” The debugging code “fixes” the problem so it remains in the system.

To really solve the problem of deadlock, we need to understand what causes it. There are four conditions required for deadlock to occur:

- Mutual exclusion
- Lock & wait
- No preemption
- Circular wait

Mutual Exclusion
Mutual exclusion occurs when multiple threads need to use the same resources and those resources

- Cannot be used by multiple threads at the same time.
- Are limited in number.

A common example of such a resource is a database connection, a file open for write, a record lock, or a semaphore.

Lock & Wait
Once a thread acquires a resource, it will not release the resource until it has acquired all of the other resources it requires and has completed its work.

No Preemption
One thread cannot take resources away from another thread. Once a thread holds a resource, the only way for another thread to get it is for the holding thread to release it.

Circular Wait
This is also referred to as the deadly embrace. Imagine two threads, T1 and T2, and two resources, R1 and R2. T1 has R1, T2 has R2. T1 also requires R2, and T2 also requires R1. This gives something like Figure A-3:


Figure A-3

![](figures/apA/x01-3breaking_cycle.jpg)

All four of these conditions must hold for deadlock to be possible. Break any one of these conditions and deadlock is not possible.

Breaking Mutual Exclusion
One strategy for avoiding deadlock is to sidestep the mutual exclusion condition. You might be able to do this by

- Using resources that allow simultaneous use, for example, AtomicInteger.
- Increasing the number of resources such that it equals or exceeds the number of competing threads.
- Checking that all your resources are free before seizing any.

Unfortunately, most resources are limited in number and don’t allow simultaneous use. And it’s not uncommon for the identity of the second resource to be predicated on the results of operating on the first. But don’t be discouraged; there are three conditions left.

Breaking Lock & Wait
You can also eliminate deadlock if you refuse to wait. Check each resource before you seize it, and release all resources and start over if you run into one that’s busy.

This approach introduces several potential problems:

- Starvation—One thread keeps being unable to acquire the resources it needs (maybe it has a unique combination of resources that seldom all become available).
- Livelock—Several threads might get into lockstep and all acquire one resource and then release one resource, over and over again. This is especially likely with simplistic CPU scheduling algorithms (think embedded devices or simplistic hand-written thread balancing algorithms).

Both of these can cause poor throughput. The first results in low CPU utilization, whereas the second results in high and useless CPU utilization.

As inefficient as this strategy sounds, it’s better than nothing. It has the benefit that it can almost always be implemented if all else fails.

Breaking Preemption
Another strategy for avoiding deadlock is to allow threads to take resources away from other threads. This is usually done through a simple request mechanism. When a thread discovers that a resource is busy, it asks the owner to release it. If the owner is also waiting for some other resource, it releases them all and starts over.

This is similar to the previous approach but has the benefit that a thread is allowed to wait for a resource. This decreases the number of startovers. Be warned, however, that managing all those requests can be tricky.

Breaking Circular Wait
This is the most common approach to preventing deadlock. For most systems it requires no more than a simple convention agreed to by all parties.

In the example above with Thread 1 wanting both Resource 1 and Resource 2 and Thread 2 wanting both Resource 2 and then Resource 1, simply forcing both Thread 1 and Thread 2 to allocate resources in the same order makes circular wait impossible.

More generally, if all threads can agree on a global ordering of resources and if they all allocate resources in that order, then deadlock is impossible. Like all the other strategies, this can cause problems:

- The order of acquisition might not correspond to the order of use; thus a resource acquired at the start might not be used until the end. This can cause resources to be locked longer than strictly necessary.
- Sometimes you cannot impose an order on the acquisition of resources. If the ID of the second resource comes from an operation performed on the first, then ordering is not feasible.

So there are many ways to avoid deadlock. Some lead to starvation, whereas others make heavy use of the CPU and reduce responsiveness. TANSTAAFL!5

5. There ain’t no such thing as a free lunch.

Isolating the thread-related part of your solution to allow for tuning and experimentation is a powerful way to gain the insights needed to determine the best strategies.

TESTING MULTITHREADED CODE
How can we write a test to demonstrate the following code is broken?
```java
   01: public class ClassWithThreadingProblem {
   02:    int nextId;
   03:
   04:    public int takeNextId() {
   05:        return nextId++;
   06:    }
   07:}
```
Here’s a description of a test that will prove the code is broken:

- Remember the current value of nextId.
- Create two threads, both of which call takeNextId() once.
- Verify that nextId is two more than what we started with.
- Run this until we demonstrate that nextId was only incremented by one instead of two.

Listing A-2 shows such a test:


Listing A-2 ClassWithThreadingProblemTest.java
```java
   01: package example;
   02:
   03: import static org.junit.Assert.fail;
   04:
   05: import org.junit.Test;
   06:
   07: public class ClassWithThreadingProblemTest {
   08:     @Test
   09:     public void twoThreadsShouldFailEventually() throws Exception {
   10:         final ClassWithThreadingProblem classWithThreadingProblem
                   = new ClassWithThreadingProblem();
   11:
   12:         Runnable runnable = new Runnable() {
   13:             public void run() {
   14:                 classWithThreadingProblem.takeNextId();
   15:             }
   16:         };
   17:
   18:         for (int i = 0; i < 50000; ++i) {
   19:             int startingId = classWithThreadingProblem.lastId;
   20:             int expectedResult = 2 + startingId;
   21:
   22:             Thread t1 = new Thread(runnable);
   23:             Thread t2 = new Thread(runnable);
   24:             t1.start();
   25:             t2.start();
   26:             t1.join();
   27:             t2.join();
   28:
   29:             int endingId = classWithThreadingProblem.lastId;
   30:
   31:             if (endingId != expectedResult)
   32:                 return;
   33:         }
   34:
   35:         fail(“Should have exposed a threading issue but it did not.”);
   36:     }
   37: }
```
![](figures/apA/0340tab01.jpg)

![](figures/apA/0341tab01.jpg)

This test certainly sets up the conditions for a concurrent update problem. However, the problem occurs so infrequently that the vast majority of times this test won’t detect it.

Indeed, to truly detect the problem we need to set the number of iterations to over one million. Even then, in ten executions with a loop count of 1,000,000, the problem occurred only once. That means we probably ought to set the iteration count to well over one hundred million to get reliable failures. How long are we prepared to wait?

Even if we tuned the test to get reliable failures on one machine, we’ll probably have to retune the test with different values to demonstrate the failure on another machine, operating system, or version of the JVM.

And this is a simple problem. If we cannot demonstrate broken code easily with this problem, how will we ever detect truly complex problems?

So what approaches can we take to demonstrate this simple failure? And, more importantly, how can we write tests that will demonstrate failures in more complex code? How will we be able to discover if our code has failures when we do not know where to look?

Here are a few ideas:

- Monte Carlo Testing. Make tests flexible, so they can be tuned. Then run the test over and over—say on a test server—randomly changing the tuning values. If the tests ever fail, the code is broken. Make sure to start writing those tests early so a continuous integration server starts running them soon. By the way, make sure you carefully log the conditions under which the test failed.

- Run the test on every one of the target deployment platforms. Repeatedly. Continuously. The longer the tests run without failure, the more likely that

– The production code is correct or

– The tests aren’t adequate to expose problems.

- Run the tests on a machine with varying loads. If you can simulate loads close to a production environment, do so.

Yet, even if you do all of these things, you still don’t stand a very good chance of finding threading problems with your code. The most insidious problems are the ones that have such a small cross section that they only occur once in a billion opportunities. Such problems are the terror of complex systems.

TOOL SUPPORT FOR TESTING THREAD-BASED CODE
IBM has created a tool called ConTest.6 It instruments classes to make it more likely that non-thread-safe code fails.

6. http://www.haifa.ibm.com/projects/verification/contest/index.html

We do not have any direct relationship with IBM or the team that developed ConTest. A colleague of ours pointed us to it. We noticed vast improvement in our ability to find threading issues after a few minutes of using it.

Here’s an outline of how to use ConTest:

- Write tests and production code, making sure there are tests specifically designed to simulate multiple users under varying loads, as mentioned above.
- Instrument test and production code with ConTest.
- Run the tests.

When we instrumented code with ConTest, our success rate went from roughly one failure in ten million iterations to roughly one failure in thirty iterations. Here are the loop values for several runs of the test after instrumentation: 13, 23, 0, 54, 16, 14, 6, 69, 107, 49, 2. So clearly the instrumented classes failed much earlier and with much greater reliability.

CONCLUSION
This chapter has been a very brief sojourn through the large and treacherous territory of concurrent programming. We barely scratched the surface. Our emphasis here was on disciplines to help keep concurrent code clean, but there is much more you should learn if you are going to be writing concurrent systems. We recommend you start with Doug Lea’s wonderful book Concurrent Programming in Java: Design Principles and Patterns.7

7. See [Lea99] p. 191.

In this chapter we talked about concurrent update, and the disciplines of clean synchronization and locking that can prevent it. We talked about how threads can enhance the throughput of an I/O-bound system and showed the clean techniques for achieving such improvements. We talked about deadlock and the disciplines for preventing it in a clean way. Finally, we talked about strategies for exposing concurrent problems by instrumenting your code.

TUTORIAL: FULL CODE EXAMPLES
Client/Server Nonthreaded

Listing A-3 Server.java
```java
   package com.objectmentor.clientserver.nonthreaded;
 
   import java.io.IOException;
   import java.net.ServerSocket;
   import java.net.Socket;
   import java.net.SocketException;
 
   import common.MessageUtils;
 
   public class Server implements Runnable {
       ServerSocket serverSocket;
       volatile boolean keepProcessing = true;
 
       public Server(int port, int millisecondsTimeout) throws IOException {
           serverSocket = new ServerSocket(port);
           serverSocket.setSoTimeout(millisecondsTimeout);
       }
 
       public void run() {
           System.out.printf(“Server Starting\n”);
 
           while (keepProcessing) {
               try {
                   System.out.printf(“accepting client\n”);
                   Socket socket = serverSocket.accept();
                   System.out.printf(“got client\n”);
                   process(socket);
               } catch (Exception e) {
                   handle(e);
               }
           }
       }
 
       private void handle(Exception e) {
           if (!(e instanceof SocketException)) {
               e.printStackTrace();
           }
       }
 
       public void stopProcessing() {
           keepProcessing = false;
           closeIgnoringException(serverSocket);
       }
       void process(Socket socket) {
           if (socket == null)
               return;
 
           try {
               System.out.printf(“Server: getting message\n”);
               String message = MessageUtils.getMessage(socket);
               System.out.printf(“Server: got message: %s\n”, message);
               Thread.sleep(1000);
               System.out.printf(“Server: sending reply: %s\n”, message);
               MessageUtils.sendMessage(socket, “Processed: ” + message);
               System.out.printf(“Server: sent\n”);
               closeIgnoringException(socket);
           } catch (Exception e) {
               e.printStackTrace();
           }
 
       }
 
       private void closeIgnoringException(Socket socket) {
           if (socket != null)
               try {
                   socket.close();
               } catch (IOException ignore) {
               }
       }
 
       private void closeIgnoringException(ServerSocket serverSocket) {
           if (serverSocket != null)
               try {
                   serverSocket.close();
               } catch (IOException ignore) {
               }
       }
   }
```

Listing A-4 ClientTest.java
```java
package com.objectmentor.clientserver.nonthreaded;

import java.io.IOException;
import java.net.Socket;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import common.MessageUtils;


public class ClientTest {
    private static final int PORT = 8009;
    private static final int TIMEOUT = 2000;

    Server server;
    Thread serverThread;

    @Before
    public void createServer() throws Exception {
        try {
            server = new Server(PORT, TIMEOUT);
            serverThread = new Thread(server);
            serverThread.start();
        } catch (Exception e) {
            e.printStackTrace(System.err);
            throw e;
        }
    }

    @After
    public void shutdownServer() throws InterruptedException {
        if (server != null) {
            server.stopProcessing();
            serverThread.join();
        }
    }

    class TrivialClient implements Runnable {
        int clientNumber;

        TrivialClient(int clientNumber) {
            this.clientNumber = clientNumber;
        }

        public void run() {
            try {
                connectSendReceive(clientNumber);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Test(timeout = 10000)
    public void shouldRunInUnder10Seconds() throws Exception {
        Thread[] threads = new Thread[10];


        for (int i = 0; i < threads.length; ++i) {
            threads[i] = new Thread(new TrivialClient(i));
            threads[i].start();
        }

        for (int i = 0; i < threads.length; ++i) {
            threads[i].join();
        }
    }

    private void connectSendReceive(int i) throws IOException {
        System.out.printf("Client %2d: connecting\n", i);
        Socket socket = new Socket("localhost", PORT);
        System.out.printf("Client %2d: sending message\n", i);
        MessageUtils.sendMessage(socket, Integer.toString(i));
        System.out.printf("Client %2d: getting reply\n", i);
        MessageUtils.getMessage(socket);
        System.out.printf("Client %2d: finished\n", i);
        socket.close();
    }
}
```

Listing A-5 MessageUtils.java
```java
   package common;
 
   import java.io.IOException;
   import java.io.InputStream;
   import java.io.ObjectInputStream;
   import java.io.ObjectOutputStream;
   import java.io.OutputStream;
   import java.net.Socket;
 
   public class MessageUtils {
       public static void sendMessage(Socket socket, String message)
               throws IOException {
          OutputStream stream = socket.getOutputStream();
          ObjectOutputStream oos = new ObjectOutputStream(stream);
          oos.writeUTF(message);
          oos.flush();
       }
 
       public static String getMessage(Socket socket) throws IOException {
           InputStream stream = socket.getInputStream();
           ObjectInputStream ois = new ObjectInputStream(stream);
           return ois.readUTF();
       }
   }
```
Client/Server Using Threads
Changing the server to use threads simply requires a change to the process message (new lines are emphasized to stand out):
```java
   void process(final Socket socket) {
       if (socket == null)
           return;
 
       Runnable clientHandler = new Runnable() {
           public void run() {
 
               try {
                   System.out.printf("Server: getting message\n");
                   String message = MessageUtils.getMessage(socket);
                   System.out.printf("Server: got message: %s\n", message);
                    Thread.sleep(1000);
                    System.out.printf("Server: sending reply: %s\n", message);
                    MessageUtils.sendMessage(socket, "Processed: " + message);
                   System.out.printf("Server: sent\n");
                   closeIgnoringException(socket);
                } catch (Exception e) {
                   e.printStackTrace();
               }
           }
       };
 
       Thread clientConnection = new Thread(clientHandler);
       clientConnection.start();
   }
```

================================================
FILE: docs/ch1.md
================================================
# 第 1 章 Clean Code 整洁代码

![](figures/ch1/1-1fig_martin.jpg)

You are reading this book for two reasons. First, you are a programmer. Second, you want to be a better programmer. Good. We need better programmers.

> 阅读本书有两种原因:第一,你是个程序员;第二,你想成为更好的程序员。很好。我们需要更好的程序员。

This is a book about good programming. It is filled with code. We are going to look at code from every different direction. We’ll look down at it from the top, up at it from the bottom, and through it from the inside out. By the time we are done, we’re going to know a lot about code. What’s more, we’ll be able to tell the difference between good code and bad code. We’ll know how to write good code. And we’ll know how to transform bad code into good code.

> 这是本有关编写好程序的书。它充斥着代码。我们要从各个方向来考察这些代码。从顶向下,从底往上,从里而外。读完后,就能知道许多关于代码的事了。而且,我们还能说出好代码和糟糕的代码之间的差异。我们将了解到如何写出好代码。我们也会知道,如何将糟糕的代码改成好代码。

## 1.1 THERE WILL BE CODE 要有代码

One might argue that a book about code is somehow behind the times—that code is no longer the issue; that we should be concerned about models and requirements instead. Indeed some have suggested that we are close to the end of code. That soon all code will be generated instead of written. That programmers simply won’t be needed because business people will generate programs from specifications.

> 有人也许会以为,关于代码的书有点儿落后于时代——代码不再是问题;我们应当关注模型和需求。确实,有人说过我们正在临近代码的终结点。很快,代码就会自动产生出来,不需要再人工编写。程序员完全没用了,因为商务人士可以从规约直接生成程序。

Nonsense! We will never be rid of code, because code represents the details of the requirements. At some level those details cannot be ignored or abstracted; they have to be specified. And specifying requirements in such detail that a machine can execute them is programming. Such a specification is code.

> 扯淡!我们永远抛不掉代码,因为代码呈现了需求的细节。在某些层面上,这些细节无法被忽略或抽象,必须明确之。将需求明确到机器可以执行的细节程度,就是编程要做的事。而这种规约正是代码。

I expect that the level of abstraction of our languages will continue to increase. I also expect that the number of domain-specific languages will continue to grow. This will be a good thing. But it will not eliminate code. Indeed, all the specifications written in these higher level and domain-specific language will be code! It will still need to be rigorous, accurate, and so formal and detailed that a machine can understand and execute it.

> 我期望语言的抽象程度继续提升。我也期望领域特定语言的数量继续增加。那会是好事一桩。但那终结不了代码。实际上,在较高层次上用领域特定语言撰写的规约也将是代码!它也得严谨、精确、规范和详细,好让机器理解和执行。

The folks who think that code will one day disappear are like mathematicians who hope one day to discover a mathematics that does not have to be formal. They are hoping that one day we will discover a way to create machines that can do what we want rather than what we say. These machines will have to be able to understand us so well that they can translate vaguely specified needs into perfectly executing programs that precisely meet those needs.

> 那帮以为代码终将消失的伙计,就像是巴望着发现一种无规范数学的数学家们一般。他们巴望着,总有一天能创造出某种机器,我们只要想想、嘴都不用张就能叫它依计行事。那机器要能透彻理解我们,只有这样,它才能把含糊不清的需求翻译为可完美执行的程序,精确满足需求。

This will never happen. Not even humans, with all their intuition and creativity, have been able to create successful systems from the vague feelings of their customers. Indeed, if the discipline of requirements specification has taught us anything, it is that well-specified requirements are as formal as code and can act as executable tests of that code!

> 这种事永远不会发生。即便是人类,倾其全部的直觉和创造力,也造不出满足客户模糊感觉的成功系统来。如果说需求规约原则教给了我们什么,那就是归置良好的需求就像代码一样正式,也能作为代码的可执行测试来使用。

Remember that code is really the language in which we ultimately express the requirements. We may create languages that are closer to the requirements. We may create tools that help us parse and assemble those requirements into formal structures. But we will never eliminate necessary precision—so there will always be code.

> 记住,代码确然是我们最终用来表达需求的那种语言。我们可以创造各种与需求接近的语言。我们可以创造帮助把需求解析和汇整为正式结构的各种工具。然而,我们永远无法抛弃必要的精确性——所以代码永存。

## 1.2 BAD CODE 糟糕的代码

I was recently reading the preface to Kent Beck’s book Implementation Patterns.1 He says, “… this book is based on a rather fragile premise: that good code matters….” A fragile premise? I disagree! I think that premise is one of the most robust, supported, and overloaded of all the premises in our craft (and I think Kent knows it). We know good code matters because we’ve had to deal for so long with its lack.

> 最近我在读 Kent Beck 著 Implementation Patterns(中译版《实现模式》)[1]一书的序言。他这样写道:“……本书基于一种不太牢靠的前提:好代码的确重要……”这前提不牢靠?我反对!我认为这是该领域最强固、最受支持、最被强调的前提了(我想 Kent 也知道)。我们知道好代码重要,是因为其短缺实在困扰了我们太久。

1. [Beck07].

I know of one company that, in the late 80s, wrote a killer app. It was very popular, and lots of professionals bought and used it. But then the release cycles began to stretch. Bugs were not repaired from one release to the next. Load times grew and crashes increased. I remember the day I shut the product down in frustration and never used it again. The company went out of business a short time after that.

> 20 世纪 80 年代末,有家公司写了个很流行的杀手应用,许多专业人士都买来用。然后,发布周期开始拉长。缺陷总是不能修复。装载时间越来越久,崩溃的几率也越来越大。至今我还记得自己在某天沮丧地关掉那个程序,从此再不用它。在那之后不久,该公司就关门大吉了。

![](figures/ch1/1-2fig_martin.jpg)

Two decades later I met one of the early employees of that company and asked him what had happened. The answer confirmed my fears. They had rushed the product to market and had made a huge mess in the code. As they added more and more features, the code got worse and worse until they simply could not manage it any longer. It was the bad code that brought the company down.

> 20 年后,我见到那家公司的一位早期雇员,问他当年发生了什么事。他的回答叫我愈发恐惧起来。原来,当时他们赶着推出产品,代码写得乱七八糟。特性越加越多,代码也越来越烂,最后再也没法管理这些代码了。是糟糕的代码毁了这家公司。

Have you ever been significantly impeded by bad code? If you are a programmer of any experience then you’ve felt this impediment many times. Indeed, we have a name for it. We call it wading. We wade through bad code. We slog through a morass of tangled brambles and hidden pitfalls. We struggle to find our way, hoping for some hint, some clue, of what is going on; but all we see is more and more senseless code.

> 你是否曾为糟糕的代码所深深困扰?如果你是位有点儿经验的程序员,定然多次遇到过这类困境。我们有专用来形容这事的词:沼泽(wading)。我们趟过代码的水域。我们穿过灌木密布、瀑布暗藏的沼泽地。我们拼命想找到出路,期望有点什么线索能启发我们到底发生了什么事;但目光所及,只是越来越多死气沉沉的代码。

Of course you have been impeded by bad code. So then—why did you write it?

> 你当然曾为糟糕的代码所困扰过。那么——为什么要写糟糕的代码呢?

Were you trying to go fast? Were you in a rush? Probably so. Perhaps you felt that you didn’t have time to do a good job; that your boss would be angry with you if you took the time to clean up your code. Perhaps you were just tired of working on this program and wanted it to be over. Or maybe you looked at the backlog of other stuff that you had promised to get done and realized that you needed to slam this module together so you could move on to the next. We’ve all done it.

> 是想快点完成吗?是要赶时间吗?有可能。或许你觉得自己要干好所需的时间不够;假使花时间清理代码,老板就会大发雷霆。或许你只是不耐烦再搞这套程序,期望早点结束。或许你看了看自己承诺要做的其他事,意识到得赶紧弄完手上的东西,好接着做下一件工作。这种事我们都干过。

We’ve all looked at the mess we’ve just made and then have chosen to leave it for another day. We’ve all felt the relief of seeing our messy program work and deciding that a working mess is better than nothing. We’ve all said we’d go back and clean it up later. Of course, in those days we didn’t know LeBlanc’s law: Later equals never.

> 我们都曾经瞟一眼自己亲手造成的混乱,决定弃之而不顾,走向新一天。我们都曾经看到自己的烂程序居然能运行,然后断言能运行的烂程序总比什么都没有强。我们都曾经说过有朝一日再回头清理。当然,在那些日子里,我们都没听过勒布朗(LeBlanc)法则:稍后等于永不(Later equals never)。

## 1.3 THE TOTAL COST OF OWNING A MESS 混乱的代价

If you have been a programmer for more than two or three years, you have probably been significantly slowed down by someone else’s messy code. If you have been a programmer for longer than two or three years, you have probably been slowed down by messy code. The degree of the slowdown can be significant. Over the span of a year or two, teams that were moving very fast at the beginning of a project can find themselves moving at a snail’s pace. Every change they make to the code breaks two or three other parts of the code. No change is trivial. Every addition or modification to the system requires that the tangles, twists, and knots be “understood” so that more tangles, twists, and knots can be added. Over time the mess becomes so big and so deep and so tall, they can not clean it up. There is no way at all.

> 只要你干过两三年编程,就有可能曾被某人的糟糕的代码绊倒过。如果你编程不止两三年,也有可能被这种代码拖过后腿。进度延缓的程度会很严重。有些团队在项目初期进展迅速,但有那么一两年的时间却慢如蜗行。对代码的每次修改都影响到其他两三处代码。修改无小事。每次添加或修改代码,都得对那堆扭纹柴了然于心,这样才能往上扔更多的扭纹柴。这团乱麻越来越大,再也无法理清,最后束手无策。

As the mess builds, the productivity of the team continues to decrease, asymptotically approaching zero. As productivity decreases, management does the only thing they can; they add more staff to the project in hopes of increasing productivity. But that new staff is not versed in the design of the system. They don’t know the difference between a change that matches the design intent and a change that thwarts the design intent. Furthermore, they, and everyone else on the team, are under horrific pressure to increase productivity. So they all make more and more messes, driving the productivity ever further toward zero. (See Figure 1-1.)

> 随着混乱的增加,团队生产力也持续下降,趋向于零。当生产力下降时,管理层就只有一件事可做了:增加更多人手到项目中,期望提升生产力。可是新人并不熟悉系统的设计。他们搞不清楚什么样的修改符合设计意图,什么样的修改违背设计意图。而且,他们以及团队中的其他人都背负着提升生产力的可怕压力。于是,他们制造更多的混乱,驱动生产力向零那端不断下降。如图 1-1 所示

Figure 1-1 Productivity vs. time

![](figures/ch1/1-4fig_martin.jpg)

### 1.3.1 The Grand Redesign in the Sky 华丽新设计

Eventually the team rebels. They inform management that they cannot continue to develop in this odious code base. They demand a redesign. Management does not want to expend the resources on a whole new redesign of the project, but they cannot deny that productivity is terrible. Eventually they bend to the demands of the developers and authorize the grand redesign in the sky.

> 最后,开发团队造反了,他们告诉管理层,再也无法在这令人生厌的代码基础上做开发。他们要求做全新的设计。管理层不愿意投入资源完全重启炉灶,但他们也不能否认生产力低得可怕。他们只好同意开发者的要求,授权去做一套看上去很美的华丽新设计。

A new tiger team is selected. Everyone wants to be on this team because it’s a green-field project. They get to start over and create something truly beautiful. But only the best and brightest are chosen for the tiger team. Everyone else must continue to maintain the current system.

> 于是就组建了一支新军。谁都想加入这个团队,因为它是张白纸。他们可以重新来过,搞出点真正漂亮的东西来。但只有最优秀、最聪明的家伙被选中。其余人等则继续维护现有系统。

Now the two teams are in a race. The tiger team must build a new system that does everything that the old system does. Not only that, they have to keep up with the changes that are continuously being made to the old system. Management will not replace the old system until the new system can do everything that the old system does.

> 现在有两支队伍在竞赛了。新团队必须搭建一套新系统,要能实现旧系统的所有功能。另外,还得跟上对旧系统的持续改动。在新系统功能足以抗衡旧系统之前,管理层不会替换掉旧系统。

This race can go on for a very long time. I’ve seen it take 10 years. And by the time it’s done, the original members of the tiger team are long gone, and the current members are demanding that the new system be redesigned because it’s such a mess.

> 竞赛可能会持续极长时间。我就见过延续了十年之久的。到了完成的时候,新团队的老成员早已不知去向,而现有成员则要求重新设计一套新系统,因为这套系统太烂了。

If you have experienced even one small part of the story I just told, then you already know that spending time keeping your code clean is not just cost effective; it’s a matter of professional survival.

> 假使你经历过哪怕是一小段我谈到的这种事,那么你一定知道,花时间保持代码整洁不但有关效率,还有关生存。

### 1.3.2 Attitude 态度

Have you ever waded through a mess so grave that it took weeks to do what should have taken hours? Have you seen what should have been a one-line change, made instead in hundreds of different modules? These symptoms are all too common.

> 你是否遇到过某种严重到要花数个星期来做本来只需数小时即可完成的事的混乱状况?你是否见过本来只需做一行修改,结果却涉及上百个模块的情况?这种事太常见了。

Why does this happen to code? Why does good code rot so quickly into bad code? We have lots of explanations for it. We complain that the requirements changed in ways that thwart the original design. We bemoan the schedules that were too tight to do things right. We blather about stupid managers and intolerant customers and useless marketing types and telephone sanitizers. But the fault, dear Dilbert, is not in our stars, but in ourselves. We are unprofessional.

> 怎么会发生这种事?为什么好代码会这么快就变质成糟糕的代码?理由多得很。我们抱怨需求变化背离了初期设计。我们哀叹进度太紧张,没法干好活。我们把问题归咎于那些愚蠢的经理、苛求的用户、没用的营销方式和那些电话消毒剂。不过,亲爱的呆伯特(Dilbert)[2],我们是自作自受[3]。我们太不专业了。

This may be a bitter pill to swallow. How could this mess be our fault? What about the requirements? What about the schedule? What about the stupid managers and the useless marketing types? Don’t they bear some of the blame?

> 这话可不太中听。怎么会是自作自受呢?难道不关需求的事?难道不关进度的事?难道不关那些蠢经理和没用的营销手段的事?难道他们就不该负点责吗?

No. The managers and marketers look to us for the information they need to make promises and commitments; and even when they don’t look to us, we should not be shy about telling them what we think. The users look to us to validate the way the requirements will fit into the system. The project managers look to us to help work out the schedule. We are deeply complicit in the planning of the project and share a great deal of the responsibility for any failures; especially if those failures have to do with bad code!

> 不。经理和营销人员指望从我们这里得到必须的信息,然后才能做出承诺和保证;即便他们没开口问,我们也不该羞于告知自己的想法。用户指望我们验证需求是否都在系统中实现了。项目经理指望我们遵守进度。我们与项目的规划脱不了干系,对失败负有极大的责任;特别是当失败与糟糕的代码有关时尤为如此!

“But wait!” you say. “If I don’t do what my manager says, I’ll be fired.” Probably not. Most managers want the truth, even when they don’t act like it. Most managers want good code, even when they are obsessing about the schedule. They may defend the schedule and requirements with passion; but that’s their job. It’s your job to defend the code with equal passion.

> “且慢!”你说。“不听经理的,我就会被炒鱿鱼。”多半不会。多数经理想要知道实情,即便他们看起来不喜欢实情。多数经理想要好代码,即便他们总是痴缠于进度。他们会奋力卫护进度和需求;那是他们该干的。你则当以同等的热情卫护代码。

To drive this point home, what if you were a doctor and had a patient who demanded that you stop all the silly hand-washing in preparation for surgery because it was taking too much time?2 Clearly the patient is the boss; and yet the doctor should absolutely refuse to comply. Why? Because the doctor knows more than the patient about the risks of disease and infection. It would be unprofessional (never mind criminal) for the doctor to comply with the patient.

> 再说明白些,假使你是位医生,病人请求你在给他做手术前别洗手,因为那会花太多时间,你会照办吗[4]?本该是病人说了算;但医生却绝对应该拒绝遵从。为什么?因为医生比病人更了解疾病和感染的风险。医生如果按病人说的办,就是一种不专业的态度(更别说是犯罪了)。

2. When hand-washing was first recommended to physicians by Ignaz Semmelweis in 1847, it was rejected on the basis that doctors were too busy and wouldn’t have time to wash their hands between patient visits.

So too it is unprofessional for programmers to bend to the will of managers who don’t understand the risks of making messes.

> 同理,程序员遵从不了解混乱风险的经理的意愿,也是不专业的做法。

### 1.3.3 The Primal Conundrum 迷题

Programmers face a conundrum of basic values. All developers with more than a few years experience know that previous messes slow them down. And yet all developers feel the pressure to make messes in order to meet deadlines. In short, they don’t take the time to go fast!

> 程序员面临着一种基础价值谜题。有那么几年经验的开发者都知道,之前的混乱拖了自己的后腿。但开发者们背负期限的压力,只好制造混乱。简言之,他们没花时间让自己做得更快!

True professionals know that the second part of the conundrum is wrong. You will not make the deadline by making the mess. Indeed, the mess will slow you down instantly, and will force you to miss the deadline. The only way to make the deadline—the only way to go fast—is to keep the code as clean as possible at all times.

> 真正的专业人士明白,这道谜题的第二部分说错了。制造混乱无助于赶上期限。混乱只会立刻拖慢你,叫你错过期限。赶上期限的唯一方法——做得快的唯一方法 ——就是始终尽可能保持代码整洁。

### 1.3.4 The Art of Clean Code? 整洁代码的艺术

Let’s say you believe that messy code is a significant impediment. Let’s say that you accept that the only way to go fast is to keep your code clean. Then you must ask yourself: “How do I write clean code?” It’s no good trying to write clean code if you don’t know what it means for code to be clean!

> 假设你相信混乱的代码是祸首,假设你接受做得快的唯一方法是保持代码整洁的说法,你一定会自问:“我怎么才能写出整洁的代码?”不过,如果你不明白整洁对代码有何意义,尝试去写整洁代码就毫无所益!

The bad news is that writing clean code is a lot like painting a picture. Most of us know when a picture is painted well or badly. But being able to recognize good art from bad does not mean that we know how to paint. So too being able to recognize clean code from dirty code does not mean that we know how to write clean code!

> 坏消息是写整洁代码很像是绘画。多数人都知道一幅画是好还是坏。但能分辨优劣并不表示懂得绘画。能分辨整洁代码和肮脏代码,也不意味着会写整洁代码!

Writing clean code requires the disciplined use of a myriad little techniques applied through a painstakingly acquired sense of “cleanliness.” This “code-sense” is the key. Some of us are born with it. Some of us have to fight to acquire it. Not only does it let us see whether code is good or bad, but it also shows us the strategy for applying our discipline to transform bad code into clean code.

> 写整洁代码,需要遵循大量的小技巧,贯彻刻苦习得的“整洁感”。这种“代码感”就是关键所在。有些人生而有之。有些人费点劲才能得到。它不仅让我们看到代码的优劣,还予我们以借戒规之力化劣为优的攻略。

A programmer without “code-sense” can look at a messy module and recognize the mess but will have no idea what to do about it. A programmer with “code-sense” will look at a messy module and see options and variations. The “code-sense” will help that programmer choose the best variation and guide him or her to plot a sequence of behavior preserving transformations to get from here to there.

> 缺乏“代码感”的程序员,看混乱是混乱,无处着手。有“代码感”的程序员能从混乱中看出其他的可能与变化。“代码感”帮助程序员选出最好的方案,并指导程序员制订修改行动计划,按图索骥。

In short, a programmer who writes clean code is an artist who can take a blank screen through a series of transformations until it is an elegantly coded system.

> 简言之,编写整洁代码的程序员就像是艺术家,他能用一系列变换把一块白板变作由优雅代码构成的系统。

### 1.3.5 What Is Clean Code? 什么是整洁代码

There are probably as many definitions as there are programmers. So I asked some very well-known and deeply experienced programmers what they thought.

> 有多少程序员,就有多少定义。所以我只询问了一些非常知名且经验丰富的程序员。

![](figures/ch1/1-5fig_martin.jpg)

Bjarne Stroustrup, inventor of C++ and author of The C++ Programming Language

> Bjarne Stroustrup,C++语言发明者,C++Programming Language(中译版《C++程序设计语言》)一书作者。

I like my code to be elegant and efficient. The logic should be straightforward to make it hard for bugs to hide, the dependencies minimal to ease maintenance, error handling complete according to an articulated strategy, and performance close to optimal so as not to tempt people to make the code messy with unprincipled optimizations. Clean code does one thing well.

> 我喜欢优雅和高效的代码。代码逻辑应当直截了当,叫缺陷难以隐藏;尽量减少依赖关系,使之便于维护;依据某种分层战略完善错误处理代码;性能调至最优,省得引诱别人做没规矩的优化,搞出一堆混乱来。整洁的代码只做好一件事。

Bjarne uses the word “elegant.” That’s quite a word! The dictionary in my MacBook® provides the following definitions: pleasingly graceful and stylish in appearance or manner; pleasingly ingenious and simple. Notice the emphasis on the word “pleasing.” Apparently Bjarne thinks that clean code is pleasing to read. Reading it should make you smile the way a well-crafted music box or well-designed car would.

> Bjarne 用了“优雅”一词。说得好!我 MacBook 上的词典提供了如下定义:外表或举止上令人愉悦的优美和雅观;令人愉悦的精致和简单。注意对“愉悦”一词的强调。Bjarne 显然认为整洁的代码读起来令人愉悦。读这种代码,就像见到手工精美的音乐盒或者设计精良的汽车一般,让你会心一笑。

Bjarne also mentions efficiency—twice. Perhaps this should not surprise us coming from the inventor of C++; but I think there’s more to it than the sheer desire for speed. Wasted cycles are inelegant, they are not pleasing. And now note the word that Bjarne uses to describe the consequence of that inelegance. He uses the word “tempt.” There is a deep truth here. Bad code tempts the mess to grow! When others change bad code, they tend to make it worse.

> Bjarne 也提到效率——而且两次提及。这话出自 C++发明者之口,或许并不出奇;不过我认为并非是在单纯追求速度。被浪费掉的运算周期并不雅观,并不令人愉悦。留意 Bjarne 怎么描述那种不雅观的结果。他用了“引诱”这个词。诚哉斯言。糟糕的代码引发混乱!别人修改糟糕的代码时,往往会越改越烂。

Pragmatic Dave Thomas and Andy Hunt said this a different way. They used the metaphor of broken windows.3 A building with broken windows looks like nobody cares about it. So other people stop caring. They allow more windows to become broken. Eventually they actively break them. They despoil the facade with graffiti and allow garbage to collect. One broken window starts the process toward decay.

> 务实的 Dave Thomas 和 Andy Hunt 从另一角度阐述了这种情况。他们提到破窗理论[5]。窗户破损了的建筑让人觉得似乎无人照管。于是别人也再不关心。他们放任窗户继续破损。最终自己也参加破坏活动,在外墙上涂鸦,任垃圾堆积。一扇破损的窗户开辟了大厦走向倾颓的道路。

3. http://www.pragmaticprogrammer.com/booksellers/2004-12.html

Bjarne also mentions that error handing should be complete. This goes to the discipline of paying attention to details. Abbreviated error handling is just one way that programmers gloss over details. Memory leaks are another, race conditions still another. Inconsistent naming yet another. The upshot is that clean code exhibits close attention to detail.

> Bjarne 也提到完善错误处理代码。往深处说就是在细节上花心思。敷衍了事的错误处理代码只是程序员忽视细节的一种表现。此外还有内存泄漏,还有竞态条件代码。还有前后不一致的命名方式。结果就是凸现出整洁代码对细节的重视。

Bjarne closes with the assertion that clean code does one thing well. It is no accident that there are so many principles of software design that can be boiled down to this simple admonition. Writer after writer has tried to communicate this thought. Bad code tries to do too much, it has muddled intent and ambiguity of purpose. Clean code is focused. Each function, each class, each module exposes a single-minded attitude that remains entirely undistracted, and unpolluted, by the surrounding details.

> Bjarne 以“整洁的代码只做好一件事”结束论断。毋庸置疑,软件设计的许多原则最终都会归结为这句警语。有那么多人发表过类似的言论。糟糕的代码想做太多事,它意图混乱、目的含混。整洁的代码力求集中。每个函数、每个类和每个模块都全神贯注于一事,完全不受四周细节的干扰和污染。

Grady Booch, author of Object Oriented Analysis and Design with Applications

> Grady Booch,Object Oriented Analysis and Design with Applications(中译版《面向对象分析与设计》)一书作者。

![](figures/ch1/1-6fig_martin.jpg)

Clean code is simple and direct. Clean code reads like well-written prose. Clean code never obscures the designer’s intent but rather is full of crisp abstractions and straightforward lines of control.

> 整洁的代码简单直接。整洁的代码如同优美的散文。整洁的代码从不隐藏设计者的意图,充满了干净利落的抽象和直截了当的控制语句。

Grady makes some of the same points as Bjarne, but he takes a readability perspective. I especially like his view that clean code should read like well-written prose. Think back on a really good book that you’ve read. Remember how the words disappeared to be replaced by images! It was like watching a movie, wasn’t it? Better! You saw the characters, you heard the sounds, you experienced the pathos and the humor.

> Grady 的观点与 Bjarne 的观点有类似之处,但他从可读性的角度来定义。我特别喜欢“整洁的代码如同优美的散文”这种看法。想想你读过的某本好书。回忆一下,那些文字是如何在脑中形成影像!就像是看了场电影,对吧?还不止!你还看到那些人物,听到那些声音,体验到那些喜怒哀乐。

Reading clean code will never be quite like reading Lord of the Rings. Still, the literary metaphor is not a bad one. Like a good novel, clean code should clearly expose the tensions in the problem to be solved. It should build those tensions to a climax and then give the reader that “Aha! Of course!” as the issues and tensions are resolved in the revelation of an obvious solution.

> 阅读整洁的代码和阅读 Lord of the Rings(中译版《指环王》)自然不同。不过,仍有可类比之处。如同一本好的小说般,整洁的代码应当明确地展现出要解决问题的张力。它应当将这种张力推至高潮,以某种显而易见的方案解决问题和张力,使读者发出“啊哈!本当如此!”的感叹。

I find Grady’s use of the phrase “crisp abstraction” to be a fascinating oxymoron! After all the word “crisp” is nearly a synonym for “concrete.” My MacBook’s dictionary holds the following definition of “crisp”: briskly decisive and matter-of-fact, without hesitation or unnecessary detail. Despite this seeming juxtaposition of meaning, the words carry a powerful message. Our code should be matter-of-fact as opposed to speculative. It should contain only what is necessary. Our readers should perceive us to have been decisive.

> 窃以为 Grady 所谓“干净利落的抽象”(crisp abstraction),乃是绝妙的矛盾修辞法。毕竟 crisp 几乎就是“具体”(concrete)的同义词。我 MacBook 上的词典这样定义 crisp 一词:果断决绝,就事论事,没有犹豫或不必要的细节。尽管有两种不同的定义,该词还是承载了有力的信息。代码应当讲述事实,不引人猜测。它只该包含必需之物。读者应当感受到我们的果断决绝。

“Big” Dave Thomas, founder of OTI, godfather of the Eclipse strategy

> “老大”Dave Thomas,OTI 公司创始人,Eclipse 战略教父。

![](figures/ch1/1-7fig_martin.jpg)

Clean code can be read, and enhanced by a developer other than its original author. It has unit and acceptance tests. It has meaningful names. It provides one way rather than many ways for doing one thing. It has minimal dependencies, which are explicitly defined, and provides a clear and minimal API. Code should be literate since depending on the language, not all necessary information can be expressed clearly in code alone.

> 整洁的代码应可由作者之外的开发者阅读和增补。它应当有单元测试和验收测试。它使用有意义的命名。它只提供一种而非多种做一件事的途径。它只有尽量少的依赖关系,而且要明确地定义和提供清晰、尽量少的 API。代码应通过其字面表达含义,因为不同的语言导致并非所有必需信息均可通过代码自身清晰表达。

Big Dave shares Grady’s desire for readability, but with an important twist. Dave asserts that clean code makes it easy for other people to enhance it. This may seem obvious, but it cannot be overemphasized. There is, after all, a difference between code that is easy to read and code that is easy to change.

> Dave 老大在可读性上和 Grady 持相同观点,但有一个重要的不同之处。Dave 断言,整洁的代码便于其他人加以增补。这看似显而易见,但亦不可过分强调。毕竟易读的代码和易修改的代码之间还是有区别的。

Dave ties cleanliness to tests! Ten years ago this would have raised a lot of eyebrows. But the discipline of Test Driven Development has made a profound impact upon our industry and has become one of our most fundamental disciplines. Dave is right. Code, without tests, is not clean. No matter how elegant it is, no matter how readable and accessible, if it hath not tests, it be unclean.

> Dave 将整洁系于测试之上!要在十年之前,这会让人大跌眼镜。但测试驱动开发(Test Driven Development)已在行业中造成了深远影响,成为基础规程之一。Dave 说得对。没有测试的代码不干净。不管它有多优雅,不管有多可读、多易理解,微乎测试,其不洁亦可知也。

Dave uses the word minimal twice. Apparently he values code that is small, rather than code that is large. Indeed, this has been a common refrain throughout software literature since its inception. Smaller is better.

> Dave 两次提及“尽量少”。显然,他推崇小块的代码。实际上,从有软件起人们就在反复强调这一点。越小越好。

Dave also says that code should be literate. This is a soft reference to Knuth’s literate programming.4 The upshot is that the code should be composed in such a form as to make it readable by humans.

> Dave 也提到,代码应在字面上表达其含义。这一观点源自 Knuth 的“字面编程”(literate programming)[6]。结论就是应当用人类可读的方式来写代码。

4. [Knuth92].

Michael Feathers, author of Working Effectively with Legacy Code

> Michael Feathers,Working Effectively with Legacy Code(中译版《修改代码的艺术》)一书作者。

![](figures/ch1/1-8fig_martin.jpg)

I could list all of the qualities that I notice in clean code, but there is one overarching quality that leads to all of them. Clean code always looks like it was written by someone who cares. There is nothing obvious that you can do to make it better. All of those things were thought about by the code’s author, and if you try to imagine improvements, you’re led back to where you are, sitting in appreciation of the code someone left for you—code left by someone who cares deeply about the craft.

> 我可以列出我留意到的整洁代码的所有特点,但其中有一条是根本性的。整洁的代码总是看起来像是某位特别在意它的人写的。几乎没有改进的余地。代码作者什么都想到了,如果你企图改进它,总会回到原点,赞叹某人留给你的代码——全心投入的某人留下的代码。

One word: care. That’s really the topic of this book. Perhaps an appropriate subtitle would be How to Care for Code.

> 一言以蔽之:在意。这就是本书的题旨所在。或许该加个副标题,如何在意代码。

Michael hit it on the head. Clean code is code that has been taken care of. Someone has taken the time to keep it simple and orderly. They have paid appropriate attention to details. They have cared.

> Michael 一针见血。整洁代码就是作者着力照料的代码。有人曾花时间让它保持简单有序。他们适当地关注到了细节。他们在意过。

Ron Jeffries, author of Extreme Programming Installed and Extreme Programming Adventures in C#

> Ron Jeffries,Extreme Programming Installed(中译版《极限编程实施》)以及 Extreme Programming Adventures in C#(中译版《C#极限编程探险》)作者。

Ron began his career programming in Fortran at the Strategic Air Command and has written code in almost every language and on almost every machine. It pays to consider his words carefully.

> Ron 初入行就在战略空军司令部(Strategic Air Command)编写 Fortran 程序,此后几乎在每种机器上编写过每种语言的代码。他的言论值得咀嚼。

![](figures/ch1/1-9fig_martin.jpg)

In recent years I begin, and nearly end, with Beck’s rules of simple code. In priority order, simple code:

> 近年来,我开始研究贝克的简单代码规则,差不多也都琢磨透了。简单代码,依其重要顺序:

- Runs all the tests;
- Contains no duplication;
- Expresses all the design ideas that are in the system;
- Minimizes the number of entities such as classes, methods, functions, and the like.

---

> - 能通过所有测试;
> - 没有重复代码;
> - 体现系统中的全部设计理念;
> - 包括尽量少的实体,比如类、方法、函数等。

Of these, I focus mostly on duplication. When the same thing is done over and over, it’s a sign that there is an idea in our mind that is not well represented in the code. I try to figure out what it is. Then I try to express that idea more clearly.

> 在以上诸项中,我最在意代码重复。如果同一段代码反复出现,就表示某种想法未在代码中得到良好的体现。我尽力去找出到底那是什么,然后再尽力更清晰地表达出来。

Expressiveness to me includes meaningful names, and I am likely to change the names of things several times before I settle in. With modern coding tools such as Eclipse, renaming is quite inexpensive, so it doesn’t trouble me to change. Expressiveness goes beyond names, however. I also look at whether an object or method is doing more than one thing. If it’s an object, it probably needs to be broken into two or more objects. If it’s a method, I will always use the Extract Method refactoring on it, resulting in one method that says more clearly what it does, and some submethods saying how it is done.

> 在我看来,有意义的命名是体现表达力的一种方式,我往往会修改好几次才会定下名字来。借助 Eclipse 这样的现代编码工具,重命名代价极低,所以我无所顾忌。然而,表达力还不只体现在命名上。我也会检查对象或方法是否想做的事太多。如果对象功能太多,最好是切分为两个或多个对象。如果方法功能太多,我总是使用抽取手段(Extract Method)重构之,从而得到一个能较为清晰地说明自身功能的方法,以及另外数个说明如何实现这些功能的方法。

Duplication and expressiveness take me a very long way into what I consider clean code, and improving dirty code with just these two things in mind can make a huge difference. There is, however, one other thing that I’m aware of doing, which is a bit harder to explain.

> 消除重复和提高表达力让我在整洁代码方面获益良多,只要铭记这两点,改进脏代码时就会大有不同。不过,我时常关注的另一规则就不太好解释了。

After years of doing this work, it seems to me that all programs are made up of very similar elements. One example is “find things in a collection.” Whether we have a database of employee records, or a hash map of keys and values, or an array of items of some kind, we often find ourselves wanting a particular item from that collection. When I find that happening, I will often wrap the particular implementation in a more abstract method or class. That gives me a couple of interesting advantages.

> 这么多年下来,我发现所有程序都由极为相似的元素构成。例如“在集合中查找某物”。不管是雇员记录数据库还是名-值对哈希表,或者某类条目的数组,我们都会发现自己想要从集合中找到某一特定条目。一旦出现这种情况,我通常会把实现手段封装到更抽象的方法或类中。这样做好处多多。

I can implement the functionality now with something simple, say a hash map, but since now all the references to that search are covered by my little abstraction, I can change the implementation any time I want. I can go forward quickly while preserving my ability to change later.

> 可以先用某种简单的手段,比如哈希表来实现这一功能,由于对搜索功能的引用指向了我那个小小的抽象,就能随需应变,修改实现手段。这样就既能快速前进,又能为未来的修改预留余地。

In addition, the collection abstraction often calls my attention to what’s “really” going on, and keeps me from running down the path of implementing arbitrary collection behavior when all I really need is a few fairly simple ways of finding what I want.

> 另外,该集合抽象常常提醒我留意“真正”在发生的事,避免随意实现集合行为,因为我真正需要的不过是某种简单的查找手段。

Reduced duplication, high expressiveness, and early building of simple abstractions. That’s what makes clean code for me.

> 减少重复代码,提高表达力,提早构建简单抽象。这就是我写整洁代码的方法。

Here, in a few short paragraphs, Ron has summarized the contents of this book. No duplication, one thing, expressiveness, tiny abstractions. Everything is there.

> Ron 以寥寥数段文字概括了本书的全部内容。不要重复代码,只做一件事,表达力,小规模抽象。该有的都有了。

Ward Cunningham, inventor of Wiki, inventor of Fit, coinventor of eXtreme Programming. Motive force behind Design Patterns. Smalltalk and OO thought leader. The godfather of all those who care about code.

> Ward Cunningham,Wiki 发明者,eXtreme Programming (极限编程)的创始人之一,Smalltalk 语言和面向对象的思想领袖。所有在意代码者的教父。

![](figures/ch1/1-10fig_martin.jpg)

You know you are working on clean code when each routine you read turns out to be pretty much what you expected. You can call it beautiful code when the code also makes it look like the language was made for the problem.

> 如果每个例程都让你感到深合己意,那就是整洁代码。如果代码让编程语言看起来像是专为解决那个问题而存在,就可以称之为漂亮的代码。

Statements like this are characteristic of Ward. You read it, nod your head, and then go on to the next topic. It sounds so reasonable, so obvious, that it barely registers as something profound. You might think it was pretty much what you expected. But let’s take a closer look.

> 这种说法很 Ward。它教你听了之后就点头,然后继续听下去。如此在理,如此浅显,绝不故作高深。你大概以为此言深合己意吧。再走近点看看。

“… pretty much what you expected.” When was the last time you saw a module that was pretty much what you expected? Isn’t it more likely that the modules you look at will be puzzling, complicated, tangled? Isn’t misdirection the rule? Aren’t you used to flailing about trying to grab and hold the threads of reasoning that spew forth from the whole system and weave their way through the module you are reading? When was the last time you read through some code and nodded your head the way you might have nodded your head at Ward’s statement?

> “……深合己意”。你最近一次看到深合己意的模块是什么时候?模块多半都繁复难解吧?难道没有触犯规则吗?你不是也曾挣扎着想抓住些从整个系统中散落而出的线索,编织进你在读的那个模块吗?你最近一次读到某段代码、并且如同对 Ward 的说法点头一般对这段代码点头,是什么时候的事了?

Ward expects that when you read clean code you won’t be surprised at all. Indeed, you won’t even expend much effort. You will read it, and it will be pretty much what you expected. It will be obvious, simple, and compelling. Each module will set the stage for the next. Each tells you how the next will be written. Programs that are that clean are so profoundly well written that you don’t even notice it. The designer makes it look ridiculously simple like all exceptional designs.

> Ward 期望你不会为整洁代码所震惊。你无需花太多力气。那代码就是深合你意。它明确、简单、有力。每个模块都为下一个模块做好准备。每个模块都告诉你下一个模块会是怎样的。整洁的程序好到你根本不会注意到它。设计者把它做得像一切其他设计般简单。

And what about Ward’s notion of beauty? We’ve all railed against the fact that our languages weren’t designed for our problems. But Ward’s statement puts the onus back on us. He says that beautiful code makes the language look like it was made for the problem! So it’s our responsibility to make the language look simple! Language bigots everywhere, beware! It is not the language that makes programs appear simple. It is the programmer that make the language appear simple!

> 那 Ward 有关“美”的说法又如何呢?我们都曾面临语言不是为要解决的问题所设计的困境。但 Ward 的说法又把球踢回我们这边。他说,漂亮的代码让编程语言像是专为解决那个问题而存在!所以,让语言变得简单的责任就在我们身上了!当心,语言是冥顽不化的!是程序员让语言显得简单。

## 1.4 SCHOOLS OF THOUGHT 思想流派

What about me (Uncle Bob)? What do I think clean code is? This book will tell you, in hideous detail, what I and my compatriots think about clean code. We will tell you what we think makes a clean variable name, a clean function, a clean class, etc. We will present these opinions as absolutes, and we will not apologize for our stridence. To us, at this point in our careers, they are absolutes. They are our school of thought about clean code.

> 我(鲍勃大叔)又是怎么想的呢?在我眼中整洁代码是什么样的?本书将以详细到吓死人的程度告诉你,我和我的同道对整洁代码的看法。我们会告诉你关于整洁变量名的想法,关于整洁函数的想法,关于整洁类的想法,如此等等。我们视这些观点为当然,且不为其逆耳而致歉。对我们而言,在职业生涯的这个阶段,这些观点确属当然,也是我们整洁代码派的圭旨。

![](figures/ch1/1-11fig_martin.jpg)

Martial artists do not all agree about the best martial art, or the best technique within a martial art. Often master martial artists will form their own schools of thought and gather students to learn from them. So we see Gracie Jiu Jistu, founded and taught by the Gracie family in Brazil. We see Hakkoryu Jiu Jistu, founded and taught by Okuyama Ryuho in Tokyo. We see Jeet Kune Do, founded and taught by Bruce Lee in the United States.

> 武术家从不认同所谓最好的武术,也不认同所谓绝招。武术大师们常常创建自己的流派,聚徒而授。因此我们才看到格雷西家族在巴西开创并传授的格雷西柔术(Gracie Jiu Jistu),看到奥山龙峰(Okuyama Ryuho)在东京开创并传授的八光流柔术(Hakkoryu Jiu Jistu),看到李小龙(Bruce Lee)在美国开创并传授的截拳道(Jeet Kune Do)。

Students of these approaches immerse themselves in the teachings of the founder. They dedicate themselves to learn what that particular master teaches, often to the exclusion of any other master’s teaching. Later, as the students grow in their art, they may become the student of a different master so they can broaden their knowledge and practice. Some eventually go on to refine their skills, discovering new techniques and founding their own schools.

> 弟子们沉浸于创始人的授业。他们全心师从某位师傅,排斥其他师傅。弟子有所成就后,可以转投另一位师傅,扩展自己的知识与技能。有些弟子最终百炼成钢,创出新招数,开宗立派。

None of these different schools is absolutely right. Yet within a particular school we act as though the teachings and techniques are right. After all, there is a right way to practice Hakkoryu Jiu Jitsu, or Jeet Kune Do. But this rightness within a school does not invalidate the teachings of a different school.

> 任何门派都并非绝对正确。不过,身处某一门派时,我们总以其所传之技为善。归根结底,练习八光流柔术或截拳道,自有其善法,但这并不能否定其他门派所授之法。

Consider this book a description of the Object Mentor School of Clean Code. The techniques and teachings within are the way that we practice our art. We are willing to claim that if you follow these teachings, you will enjoy the benefits that we have enjoyed, and you will learn to write code that is clean and professional. But don’t make the mistake of thinking that we are somehow “right” in any absolute sense. There are other schools and other masters that have just as much claim to professionalism as we. It would behoove you to learn from them as well.

> 可以把本书看作是对象导师(Object Mentor)[7]整洁代码派的说明。里面要传授的就是我们勤操己艺的方法。如果你遵从这些教诲,你就会如我们一般乐受其益,你将学会如何编写整洁而专业的代码。但无论如何也别错以为我们是“正确的”。其他门派和师傅和我们一样专业。你有必要也向他们学习。

Indeed, many of the recommendations in this book are controversial. You will probably not agree with all of them. You might violently disagree with some of them. That’s fine. We can’t claim final authority. On the other hand, the recommendations in this book are things that we have thought long and hard about. We have learned them through decades of experience and repeated trial and error. So whether you agree or disagree, it would be a shame if you did not see, and respect, our point of view.

> 实际上,书中很多建议都存在争议。或许你并不完全同意这些建议。你可能会强烈反对其中一些建议。这样挺好的。我们不能要求做最终权威。另外一方面,书中列出的建议,乃是我们长久苦思、从数十年的从业经验和无数尝试与错误中得来。无论你同意与否,如果你没看到或是不尊敬我们的观点,就真该自己害臊。

## 1.5 WE ARE AUTHORS 我们是作者

The @author field of a Javadoc tells us who we are. We are authors. And one thing about authors is that they have readers. Indeed, authors are responsible for communicating well with their readers. The next time you write a line of code, remember you are an author, writing for readers who will judge your effort.

> Javadoc 中的@author 字段告诉我们自己是什么人。我们是作者。作者都有读者。实际上,作者有责任与读者做良好沟通。下次你写代码的时候,记得自己是作者,要为评判你工作的读者写代码。

You might ask: How much is code really read? Doesn’t most of the effort go into writing it?

> 你或许会问:代码真正“读”的成分有多少呢?难道力量主要不是用在“写”上吗?

Have you ever played back an edit session? In the 80s and 90s we had editors like Emacs that kept track of every keystroke. You could work for an hour and then play back your whole edit session like a high-speed movie. When I did this, the results were fascinating.

> 你是否玩过“编辑器回放”?20 世纪 80、90 年代,Emac 之类编辑器记录每次击键动作。你可以在一小时工作之后,回放击键过程,就像是看一部高速电影。我这么做过,结果很有趣。

- The vast majority of the playback was scrolling and navigating to other modules!
- Bob enters the module.
- He scrolls down to the function needing change.
- He pauses, considering his options.
- Oh, he’s scrolling up to the top of the module to check the initialization of a variable.
- Now he scrolls back down and begins to type.
- Ooops, he’s erasing what he typed!
- He types it again.
- He erases it again!
- He types half of something else but then erases that!
- He scrolls down to another function that calls the function he’s changing to see how it is called.
- He scrolls back up and types the same code he just erased.
- He pauses.
- He erases that code again!
- He pops up another window and looks at a subclass. Is that function overridden?
- …

---

> - 回放过程显示,多数时间都是在滚动屏幕、浏览其他模块!
> - 鲍勃进入模块。
> - 他向下滚动到要修改的函数。
> - 他停下来考虑可以做什么。
> - 哦,他滚动到模块顶端,检查变量初始化。
> - 现在他回到修改处,开始键入。
> - 喔,他删掉了键入的内容。
> - 他重新键入。
> - 他又删除了!
> - 他键入了一半什么东西,又删除掉。
> - 他滚动到调用要修改函数的另一函数,看看是怎么调用的。
> - 他回到修改处,重新键入刚才删掉的代码。
> - 他停下来。
> - 他再一次删掉代码!
> - 他打开另一个窗口,查看别的子类。那是个复载函数吗?
> - ……

You get the drift. Indeed, the ratio of time spent reading vs. writing is well over 10:1. We are constantly reading old code as part of the effort to write new code.

> 你该明白了。读与写花费时间的比例超过 10:1。写新代码时,我们一直在读旧代码。

Because this ratio is so high, we want the reading of code to be easy, even if it makes the writing harder. Of course there’s no way to write code without reading it, so making it easy to read actually makes it easier to write.

> 既然比例如此之高,我们就想让读的过程变得轻松,即便那会使得编写过程更难。没可能光写不读,所以使之易读实际也使之易写。

There is no escape from this logic. You cannot write code if you cannot read the surrounding code. The code you are trying to write today will be hard or easy to write depending on how hard or easy the surrounding code is to read. So if you want to go fast, if you want to get done quickly, if you want your code to be easy to write, make it easy to read.

> 这事概无例外。不读周边代码的话就没法写代码。编写代码的难度,取决于读周边代码的难度。要想干得快,要想早点做完,要想轻松写代码,先让代码易读吧。

## 1.6 THE BOY SCOUT RULE 童子军军规

It’s not enough to write the code well. The code has to be kept clean over time. We’ve all seen code rot and degrade as time passes. So we must take an active role in preventing this degradation.

> 光把代码写好可不够。必须时时保持代码整洁。我们都见过代码随时间流逝而腐坏。我们应当更积极地阻止腐坏的发生。

The Boy Scouts of America have a simple rule that we can apply to our profession.

> 借用美国童子军一条简单的军规,应用到我们的专业领域:

Leave the campground cleaner than you found it.5

> 让营地比你来时更干净。

5. This was adapted from Robert Stephenson Smyth Baden-Powell’s farewell message to the Scouts: “Try and leave this world a little better than you found it…”

If we all checked-in our code a little cleaner than when we checked it out, the code simply could not rot. The cleanup doesn’t have to be something big. Change one variable name for the better, break up one function that’s a little too large, eliminate one small bit of duplication, clean up one composite if statement.

> 如果每次签入时,代码都比签出时干净,那么代码就不会腐坏。清理并不一定要花多少功夫,也许只是改好一个变量名,拆分一个有点过长的函数,消除一点点重复代码,清理一个嵌套 if 语句。

Can you imagine working on a project where the code simply got better as time passed? Do you believe that any other option is professional? Indeed, isn’t continuous improvement an intrinsic part of professionalism?

> 你想要为一个代码随时间流逝而越变越好的项目工作吗?你还能相信有其他更专业的做法吗?难道持续改进不是专业性的内在组成部分吗?

## 1.7 PREQUEL AND PRINCIPLES 前传与原则

In many ways this book is a “prequel” to a book I wrote in 2002 entitled Agile Software Development: Principles, Patterns, and Practices (PPP). The PPP book concerns itself with the principles of object-oriented design, and many of the practices used by professional developers. If you have not read PPP, then you may find that it continues the story told by this book. If you have already read it, then you’ll find many of the sentiments of that book echoed in this one at the level of code.

> 从许多角度看,本书都是我 2002 年写那本 Agile SoftwareDevelopment:Principles,Patterns,and Practices(中译版《敏捷软件开发:原则、模式与实践》,简称 PPP)的“前传”。PPP 关注面向对象设计的原则,以及专业开发者采用的许多实践方法。假如你没读过 PPP,你会发现它像这本书的延续。如果你读过,会发现那本书的主张在代码层面于本书中回响。

In this book you will find sporadic references to various principles of design. These include the Single Responsibility Principle (SRP), the Open Closed Principle (OCP), and the Dependency Inversion Principle (DIP) among others. These principles are described in depth in PPP.

> 在本书中,你会发现对不同设计原则的引用,包括单一权责原则(Single Responsibility Principle,SRP)、开放闭合原则(Open Closed Principle,OCP)和依赖倒置原则(Dependency Inversion Principle,DIP)等。

## 1.8 CONCLUSION 小结

Books on art don’t promise to make you an artist. All they can do is give you some of the tools, techniques, and thought processes that other artists have used. So too this book cannot promise to make you a good programmer. It cannot promise to give you “code-sense.” All it can do is show you the thought processes of good programmers and the tricks, techniques, and tools that they use.

> 艺术书并不保证你读过之后能成为艺术家,只能告诉你其他艺术家用过的工具、技术和思维过程。本书同样也不担保让你成为好程序员。它不担保能给你“代码感”。它所能做的,只是展示好程序员的思维过程,还有他们使用的技巧、技术和工具。

Just like a book on art, this book will be full of details. There will be lots of code. You’ll see good code and you’ll see bad code. You’ll see bad code transformed into good code. You’ll see lists of heuristics, disciplines, and techniques. You’ll see example after example. After that, it’s up to you.

> 和艺术书一样,本书也充满了细节。代码会很多。你会看到好代码,也会看到糟糕的代码。你会看到糟糕的代码如何转化为好代码。你会看到启发、规条和技巧的列表。你会看到一个又一个例子。但最终结果取决于你自己。

Remember the old joke about the concert violinist who got lost on his way to a performance? He stopped an old man on the corner and asked him how to get to Carnegie Hall. The old man looked at the violinist and the violin tucked under his arm, and said: “Practice, son. Practice!”

> 还记得那个关于小提琴家在去表演的路上迷路的老笑话吗?他在街角拦住一位长者,问他怎么才能去卡耐基音乐厅(Carnegie Hall)。长者看了看小提琴家,又看了看他手中的琴,说道:“你还得练,孩子,还得练!”


================================================
FILE: docs/ch10.md
================================================
# 第 10 章 Classes
with Jeff Langr

![](figures/ch10/10_1fig_martin.jpg)

So far in this book we have focused on how to write lines and blocks of code well. We have delved into proper composition of functions and how they interrelate. But for all the attention to the expressiveness of code statements and the functions they comprise, we still don’t have clean code until we’ve paid attention to higher levels of code organization. Let’s talk about clean classes.

CLASS ORGANIZATION
Following the standard Java convention, a class should begin with a list of variables. Public static constants, if any, should come first. Then private static variables, followed by private instance variables. There is seldom a good reason to have a public variable.

Public functions should follow the list of variables. We like to put the private utilities called by a public function right after the public function itself. This follows the stepdown rule and helps the program read like a newspaper article.

Encapsulation
We like to keep our variables and utility functions private, but we’re not fanatic about it. Sometimes we need to make a variable or utility function protected so that it can be accessed by a test. For us, tests rule. If a test in the same package needs to call a function or access a variable, we’ll make it protected or package scope. However, we’ll first look for a way to maintain privacy. Loosening encapsulation is always a last resort.

CLASSES SHOULD BE SMALL!
The first rule of classes is that they should be small. The second rule of classes is that they should be smaller than that. No, we’re not going to repeat the exact same text from the Functions chapter. But as with functions, smaller is the primary rule when it comes to designing classes. As with functions, our immediate question is always “How small?”

With functions we measured size by counting physical lines. With classes we use a different measure. We count responsibilities.1

1. [RDD]

Listing 10-1 outlines a class, SuperDashboard, that exposes about 70 public methods. Most developers would agree that it’s a bit too super in size. Some developers might refer to SuperDashboard as a “God class.”


Listing 10-1 Too Many Responsibilities
```java
   public class SuperDashboard extends JFrame implements MetaDataUser
      public String getCustomizerLanguagePath()
      public void setSystemConfigPath(String systemConfigPath)
      public String getSystemConfigDocument()
      public void setSystemConfigDocument(String systemConfigDocument)
      public boolean getGuruState()
      public boolean getNoviceState()
      public boolean getOpenSourceState()
      public void showObject(MetaObject object)
      public void showProgress(String s)
      public boolean isMetadataDirty()
      public void setIsMetadataDirty(boolean isMetadataDirty)
      public Component getLastFocusedComponent()
      public void setLastFocused(Component lastFocused)
      public void setMouseSelectState(boolean isMouseSelected)
      public boolean isMouseSelected()
      public LanguageManager getLanguageManager()
      public Project getProject()
      public Project getFirstProject()
      public Project getLastProject()
      public String getNewProjectName()
      public void setComponentSizes(Dimension dim)
      public String getCurrentDir()
      public void setCurrentDir(String newDir)
      public void updateStatus(int dotPos, int markPos)
      public Class[] getDataBaseClasses()
      public MetadataFeeder getMetadataFeeder()
      public void addProject(Project project)
      public boolean setCurrentProject(Project project)
      public boolean removeProject(Project project)
      public MetaProjectHeader getProgramMetadata()
      public void resetDashboard() 
      public Project loadProject(String fileName, String projectName)
      public void setCanSaveMetadata(boolean canSave)
      public MetaObject getSelectedObject()
      public void deselectObjects()
      public void setProject(Project project)
      public void editorAction(String actionName, ActionEvent event)
      public void setMode(int mode)
      public FileManager getFileManager()
      public void setFileManager(FileManager fileManager)
      public ConfigManager getConfigManager()
      public void setConfigManager(ConfigManager configManager)
      public ClassLoader getClassLoader()
      public void setClassLoader(ClassLoader classLoader)
      public Properties getProps()
      public String getUserHome()
      public String getBaseDir()
      public int getMajorVersionNumber()
      public int getMinorVersionNumber()
      public int getBuildNumber()
      public MetaObject pasting(
         MetaObject target, MetaObject pasted, MetaProject project)
      public void processMenuItems(MetaObject metaObject)
      public void processMenuSeparators(MetaObject metaObject)
      public void processTabPages(MetaObject metaObject)
      public void processPlacement(MetaObject object)
      public void processCreateLayout(MetaObject object)
      public void updateDisplayLayer(MetaObject object, int layerIndex)
      public void propertyEditedRepaint(MetaObject object)
      public void processDeleteObject(MetaObject object)
      public boolean getAttachedToDesigner()
      public void processProjectChangedState(boolean hasProjectChanged)
      public void processObjectNameChanged(MetaObject object)
      public void runProject() 
      public void setAçowDragging(boolean allowDragging)
      public boolean allowDragging()
      public boolean isCustomizing()
      public void setTitle(String title)
      public IdeMenuBar getIdeMenuBar()
      public void showHelper(MetaObject metaObject, String propertyName)
      // … many non-public methods follow …
   }
```
But what if SuperDashboard contained only the methods shown in Listing 10-2?


Listing 10-2 Small Enough?
```java
   public class SuperDashboard extends JFrame implements MetaDataUser
       public Component getLastFocusedComponent()
       public void setLastFocused(Component lastFocused)
       public int getMajorVersionNumber()
       public int getMinorVersionNumber()
       public int getBuildNumber()
   }
```
Five methods isn’t too much, is it? In this case it is because despite its small number of methods, SuperDashboard has too many responsibilities.

The name of a class should describe what responsibilities it fulfills. In fact, naming is probably the first way of helping determine class size. If we cannot derive a concise name for a class, then it’s likely too large. The more ambiguous the class name, the more likely it has too many responsibilities. For example, class names including weasel words like Processor or Manager or Super often hint at unfortunate aggregation of responsibilities.

We should also be able to write a brief description of the class in about 25 words, without using the words “if,” “and,” “or,” or “but.” How would we describe the SuperDashboard? “The SuperDashboard provides access to the component that last held the focus, and it also allows us to track the version and build numbers.” The first “and” is a hint that SuperDashboard has too many responsibilities.

The Single Responsibility Principle
The Single Responsibility Principle (SRP)2 states that a class or module should have one, and only one, reason to change. This principle gives us both a definition of responsibility, and a guidelines for class size. Classes should have one responsibility—one reason to change.

2. You can read much more about this principle in [PPP].

The seemingly small SuperDashboard class in Listing 10-2 has two reasons to change. First, it tracks version information that would seemingly need to be updated every time the software gets shipped. Second, it manages Java Swing components (it is a derivative of JFrame, the Swing representation of a top-level GUI window). No doubt we’ll want to update the version number if we change any of the Swing code, but the converse isn’t necessarily true: We might change the version information based on changes to other code in the system.

Trying to identify responsibilities (reasons to change) often helps us recognize and create better abstractions in our code. We can easily extract all three SuperDashboard methods that deal with version information into a separate class named Version. (See Listing 10-3.) The Version class is a construct that has a high potential for reuse in other applications!


Listing 10-3 A single-responsibility class
```java
   public class Version {
       public int getMajorVersionNumber()
       public int getMinorVersionNumber()
       public int getBuildNumber()
   }
```
SRP is one of the more important concept in OO design. It’s also one of the simpler concepts to understand and adhere to. Yet oddly, SRP is often the most abused class design principle. We regularly encounter classes that do far too many things. Why?

Getting software to work and making software clean are two very different activities. Most of us have limited room in our heads, so we focus on getting our code to work more than organization and cleanliness. This is wholly appropriate. Maintaining a separation of concerns is just as important in our programming activities as it is in our programs.

The problem is that too many of us think that we are done once the program works. We fail to switch to the other concern of organization and cleanliness. We move on to the next problem rather than going back and breaking the overstuffed classes into decoupled units with single responsibilities.

At the same time, many developers fear that a large number of small, single-purpose classes makes it more difficult to understand the bigger picture. They are concerned that they must navigate from class to class in order to figure out how a larger piece of work gets accomplished.

However, a system with many small classes has no more moving parts than a system with a few large classes. There is just as much to learn in the system with a few large classes. So the question is: Do you want your tools organized into toolboxes with many small drawers each containing well-defined and well-labeled components? Or do you want a few drawers that you just toss everything into?

Every sizable system will contain a large amount of logic and complexity. The primary goal in managing such complexity is to organize it so that a developer knows where to look to find things and need only understand the directly affected complexity at any given time. In contrast, a system with larger, multipurpose classes always hampers us by insisting we wade through lots of things we don’t need to know right now.

To restate the former points for emphasis: We want our systems to be composed of many small classes, not a few large ones. Each small class encapsulates a single responsibility, has a single reason to change, and collaborates with a few others to achieve the desired system behaviors.

Cohesion
Classes should have a small number of instance variables. Each of the methods of a class should manipulate one or more of those variables. In general the more variables a method manipulates the more cohesive that method is to its class. A class in which each variable is used by each method is maximally cohesive.

In general it is neither advisable nor possible to create such maximally cohesive classes; on the other hand, we would like cohesion to be high. When cohesion is high, it means that the methods and variables of the class are co-dependent and hang together as a logical whole.

Consider the implementation of a Stack in Listing 10-4. This is a very cohesive class. Of the three methods only size() fails to use both the variables.


Listing 10-4 Stack.java A cohesive class.
```java
   public class Stack {
     private int topOfStack = 0;
     List<Integer> elements = new LinkedList<Integer>();
 
     public int size() {
       return topOfStack;
     }
 
     public void push(int element) {
       topOfStack++;
       elements.add(element);
     }
 
     public int pop() throws PoppedWhenEmpty {
       if (topOfStack == 0)
         throw new PoppedWhenEmpty();
       int element = elements.get(--topOfStack);
       elements.remove(topOfStack);
       return element;
    }
   }
```
The strategy of keeping functions small and keeping parameter lists short can sometimes lead to a proliferation of instance variables that are used by a subset of methods. When this happens, it almost always means that there is at least one other class trying to get out of the larger class. You should try to separate the variables and methods into two or more classes such that the new classes are more cohesive.

Maintaining Cohesion Results in Many Small Classes
Just the act of breaking large functions into smaller functions causes a proliferation of classes. Consider a large function with many variables declared within it. Let’s say you want to extract one small part of that function into a separate function. However, the code you want to extract uses four of the variables declared in the function. Must you pass all four of those variables into the new function as arguments?

Not at all! If we promoted those four variables to instance variables of the class, then we could extract the code without passing any variables at all. It would be easy to break the function up into small pieces.

Unfortunately, this also means that our classes lose cohesion because they accumulate more and more instance variables that exist solely to allow a few functions to share them. But wait! If there are a few functions that want to share certain variables, doesn’t that make them a class in their own right? Of course it does. When classes lose cohesion, split them!

So breaking a large function into many smaller functions often gives us the opportunity to split several smaller classes out as well. This gives our program a much better organization and a more transparent structure.

As a demonstration of what I mean, let’s use a time-honored example taken from Knuth’s wonderful book Literate Programming.3 Listing 10-5 shows a translation into Java of Knuth’s PrintPrimes program. To be fair to Knuth, this is not the program as he wrote it but rather as it was output by his WEB tool. I’m using it because it makes a great starting place for breaking up a big function into many smaller functions and classes.

3. [Knuth92].


Listing 10-5 PrintPrimes.java
```java
   package literatePrimes;
   
   public class PrintPrimes {
     public static void main(String[] args) {
       final int M = 1000;
       final int RR = 50;
       final int CC = 4;
       final int WW = 10;
       final int ORDMAX = 30;
       int P[] = new int[M + 1];
       int PAGENUMBER;
       int PAGEOFFSET;
       int ROWOFFSET;
       int C;

       int J;
       int K;
       boolean JPRIME;
       int ORD;
       int SQUARE;
       int N;
       int MULT[] = new int[ORDMAX + 1];

       J = 1;
       K = 1;
       P[1] = 2;
       ORD = 2;
       SQUARE = 9;

       while (K < M) {
         do {
           J = J + 2;
           if (J == SQUARE) {
             ORD = ORD + 1;
             SQUARE = P[ORD] * P[ORD];
             MULT[ORD - 1] = J;
           }
           N = 2;
           JPRIME = true;
           while (N < ORD && JPRIME) {
             while (MULT[N] < J)
               MULT[N] = MULT[N] + P[N] + P[N];
             if (MULT[N] == J)
               JPRIME = false;
             N = N + 1;
          }
        } while (!JPRIME);
        K = K + 1;
        P[K] = J;
     }
     {
        PAGENUMBER = 1;
        PAGEOFFSET = 1;
        while (PAGEOFFSET <= M) {
          System.out.println(”The First ” + M +
                                     ” Prime Numbers --- Page ” + PAGENUMBER);
          System.out.println(””);
          for (ROWOFFSET = PAGEOFFSET; ROWOFFSET < PAGEOFFSET + RR; ROWOFFSET++){
             for (C = 0; C < CC;C++)
             if (ROWOFFSET + C * RR <= M)
               System.out.format(”%10d”, P[ROWOFFSET + C * RR]);
             System.out.println(””);
          }
          System.out.println(”\f”);
          PAGENUMBER = PAGENUMBER + 1;
          PAGEOFFSET = PAGEOFFSET + RR * CC;
      }
     }
    }
   }
```
This program, written as a single function, is a mess. It has a deeply indented structure, a plethora of odd variables, and a tightly coupled structure. At the very least, the one big function should be split up into a few smaller functions.

Listing 10-6 through Listing 10-8 show the result of splitting the code in Listing 10-5 into smaller classes and functions, and choosing meaningful names for those classes, functions, and variables.


Listing 10-6 PrimePrinter.java (refactored)
```java
   package literatePrimes;
 
   public class PrimePrinter {
     public static void main(String[] args) {
     final int NUMBER_OF_PRIMES = 1000;
     int[] primes = PrimeGenerator.generate(NUMBER_OF_PRIMES);
 
     final int ROWS_PER_PAGE = 50;
     final int COLUMNS_PER_PAGE = 4;
     RowColumnPagePrinter tablePrinter =
       new RowColumnPagePrinter(ROWS_PER_PAGE,
                                COLUMNS_PER_PAGE,
                                ”The First ” + NUMBER_OF_PRIMES +
                                        ” Prime Numbers”);
       tablePrinter.print(primes);
     }
 
   }
```

Listing 10-7 RowColumnPagePrinter.java
```java
   package literatePrimes;
 
   import java.io.PrintStream;
 
   public class RowColumnPagePrinter {
     private int rowsPerPage;
     private int columnsPerPage;
     private int numbersPerPage;
     private String pageHeader;
     private PrintStream printStream;
 
     public RowColumnPagePrinter(int rowsPerPage,
                                 int columnsPerPage,
                                 String pageHeader) {
       this.rowsPerPage = rowsPerPage;
       this.columnsPerPage = columnsPerPage;
       this.pageHeader = pageHeader;
       numbersPerPage = rowsPerPage * columnsPerPage;
       printStream = System.out;
   }
 
   public void print(int data[]) {
     int pageNumber = 1;
     for (int firstIndexOnPage = 0;
          firstIndexOnPage < data.length;
          firstIndexOnPage += numbersPerPage) {
       int lastIndexOnPage =
         Math.min(firstIndexOnPage + numbersPerPage - 1,
                  data.length - 1);
       printPageHeader(pageHeader, pageNumber);
       printPage(firstIndexOnPage, lastIndexOnPage, data);
       printStream.println(”\f”);
       pageNumber++;
     }
   }
 
   private void printPage(int firstIndexOnPage,
                          int lastIndexOnPage,
                          int[] data) {
     int firstIndexOfLastRowOnPage =
       firstIndexOnPage + rowsPerPage - 1;
     for (int firstIndexInRow = firstIndexOnPage;
          firstIndexInRow <= firstIndexOfLastRowOnPage;
          firstIndexInRow++) {
       printRow(firstIndexInRow, lastIndexOnPage, data);
       printStream.println(””);
      }
    }
 
    private void printRow(int firstIndexInRow,
                          int lastIndexOnPage,
                          int[] data) {
      for (int column = 0; column < columnsPerPage; column++) {
        int index = firstIndexInRow + column * rowsPerPage;
        if (index <= lastIndexOnPage)
         printStream.format(”%10d”, data[index]);
      }
    }
 
     private void printPageHeader(String pageHeader,
                                int pageNumber) {
      printStream.println(pageHeader + ” --- Page ” + pageNumber);
      printStream.println(””);
    }
 
    public void setOutput(PrintStream printStream) {
      this.printStream = printStream;
    }
   }
```

Listing 10-8 PrimeGenerator.java
```java
   package literatePrimes;
 
   import java.util.ArrayList;
 
   public class PrimeGenerator {
     private static int[] primes;
     private static ArrayList<Integer> multiplesOfPrimeFactors;
 
     protected static int[] generate(int n) {
       primes = new int[n];
       multiplesOfPrimeFactors = new ArrayList<Integer>();
       set2AsFirstPrime();
       checkOddNumbersForSubsequentPrimes();
       return primes;
     }
 
     private static void set2AsFirstPrime() {
       primes[0] = 2;
       multiplesOfPrimeFactors.add(2);
     }
 
     private static void checkOddNumbersForSubsequentPrimes() {
       int primeIndex = 1;
       for (int candidate = 3;
            primeIndex < primes.length;
            candidate += 2) {
         if (isPrime(candidate))
           primes[primeIndex++] = candidate;
       }
     }
 
     private static boolean isPrime(int candidate) {
       if (isLeastRelevantMultipleOfNextLargerPrimeFactor(candidate)) {
         multiplesOfPrimeFactors.add(candidate);
         return false;
       }
       return isNotMultipleOfAnyPreviousPrimeFactor(candidate);
     }
 
     private static boolean
     isLeastRelevantMultipleOfNextLargerPrimeFactor(int candidate) {
       int nextLargerPrimeFactor = primes[multiplesOfPrimeFactors.size()];
       int leastRelevantMultiple = nextLargerPrimeFactor * nextLargerPrimeFactor;
       return candidate == leastRelevantMultiple;
     }
 
     private static boolean
     isNotMultipleOfAnyPreviousPrimeFactor(int candidate) {
       for (int n = 1; n < multiplesOfPrimeFactors.size(); n++) {
         if (isMultipleOfNthPrimeFactor(candidate, n))
           return false;
       }
       return true;
     }
 
     private static boolean
     isMultipleOfNthPrimeFactor(int candidate, int n) {
      return
        candidate == smallestOddNthMultipleNotLessThanCandidate(candidate, n);
     }
 
     private static int
     smallestOddNthMultipleNotLessThanCandidate(int candidate, int n) {
       int multiple = multiplesOfPrimeFactors.get(n);
       while (multiple < candidate)
         multiple += 2 * primes[n];
       multiplesOfPrimeFactors.set(n, multiple);
       return multiple;
     }
   }
```
The first thing you might notice is that the program got a lot longer. It went from a little over one page to nearly three pages in length. There are several reasons for this growth. First, the refactored program uses longer, more descriptive variable names. Second, the refactored program uses function and class declarations as a way to add commentary to the code. Third, we used whitespace and formatting techniques to keep the program readable.

Notice how the program has been split into three main responsibilities. The main program is contained in the PrimePrinter class all by itself. Its responsibility is to handle the execution environment. It will change if the method of invocation changes. For example, if this program were converted to a SOAP service, this is the class that would be affected.

The RowColumnPagePrinter knows all about how to format a list of numbers into pages with a certain number of rows and columns. If the formatting of the output needed changing, then this is the class that would be affected.

The PrimeGenerator class knows how to generate a list prime numbers. Notice that it is not meant to be instantiated as an object. The class is just a useful scope in which its variables can be declared and kept hidden. This class will change if the algorithm for computing prime numbers changes.

This was not a rewrite! We did not start over from scratch and write the program over again. Indeed, if you look closely at the two different programs, you’ll see that they use the same algorithm and mechanics to get their work done.

The change was made by writing a test suite that verified the precise behavior of the first program. Then a myriad of tiny little changes were made, one at a time. After each change the program was executed to ensure that the behavior had not changed. One tiny step after another, the first program was cleaned up and transformed into the second.

ORGANIZING FOR CHANGE
For most systems, change is continual. Every change subjects us to the risk that the remainder of the system no longer works as intended. In a clean system we organize our classes so as to reduce the risk of change.

The Sql class in Listing 10-9 is used to generate properly formed SQL strings given appropriate metadata. It’s a work in progress and, as such, doesn’t yet support SQL functionality like update statements. When the time comes for the Sql class to support an update statement, we’ll have to “open up” this class to make modifications. The problem with opening a class is that it introduces risk. Any modifications to the class have the potential of breaking other code in the class. It must be fully retested.


Listing 10-9 A class that must be opened for change
```java
   public class Sql {   public Sql(String table, Column[] columns)
      public String create()
      public String insert(Object[] fields)
      public String selectAll()
      public String findByKey(String keyColumn, String keyValue)
      public String select(Column column, String pattern)
      public String select(Criteria criteria)
      public String preparedInsert()
      private String columnList(Column[] columns)
      private String valuesList(Object[] fields, final Column[] columns)
      private String selectWithCriteria(String criteria)
      private String placeholderList(Column[] columns)
   }
```
The Sql class must change when we add a new type of statement. It also must change when we alter the details of a single statement type—for example, if we need to modify the select functionality to support subselects. These two reasons to change mean that the Sql class violates the SRP.

We can spot this SRP violation from a simple organizational standpoint. The method outline of Sql shows that there are private methods, such as selectWithCriteria, that appear to relate only to select statements.

Private method behavior that applies only to a small subset of a class can be a useful heuristic for spotting potential areas for improvement. However, the primary spur for taking action should be system change itself. If the Sql class is deemed logically complete, then we need not worry about separating the responsibilities. If we won’t need update functionality for the foreseeable future, then we should leave Sql alone. But as soon as we find ourselves opening up a class, we should consider fixing our design.

What if we considered a solution like that in Listing 10-10? Each public interface method defined in the previous Sql from Listing 10-9 is refactored out to its own derivative of the Sql class. Note that the private methods, such as valuesList, move directly where they are needed. The common private behavior is isolated to a pair of utility classes, Where and ColumnList.


Listing 10-10 A set of closed classes
```java
   abstract public class Sql {
      public Sql(String table, Column[] columns)
      abstract public String generate();
   }
 
   public class CreateSql extends Sql {
      public CreateSql(String table, Column[] columns)
      @Override public String generate()
   }
 
   public class SelectSql extends Sql {
      public SelectSql(String table, Column[] columns)
      @Override public String generate()
   }
 
   public class InsertSql extends Sql {
      public InsertSql(String table, Column[] columns, Object[] fields)
      @Override public String generate()
      private String valuesList(Object[] fields, final Column[] columns)
   }
 
   public class SelectWithCriteriaSql extends Sql {
      public SelectWithCriteriaSql(
      String table, Column[] columns, Criteria criteria)
      @Override public String generate()
   }
 
   public class SelectWithMatchSql extends Sql {
      public SelectWithMatchSql(
         String table, Column[] columns, Column column, String pattern)
      @Override public String generate()
   }
 
   public class FindByKeySql extends Sql
      public FindByKeySql(
         String table, Column[] columns, String keyColumn, String keyValue)
      @Override public String generate()
   }
 
   public class PreparedInsertSql extends Sql {
      public PreparedInsertSql(String table, Column[] columns)
         @Override public String generate() {
      private String placeholderList(Column[] columns)
   }
 
   public class Where {
      public Where(String criteria)
      public String generate()
   }
 
   public class ColumnList {
      public ColumnList(Column[] columns)
      public String generate()
   }
```
The code in each class becomes excruciatingly simple. Our required comprehension time to understand any class decreases to almost nothing. The risk that one function could break another becomes vanishingly small. From a test standpoint, it becomes an easier task to prove all bits of logic in this solution, as the classes are all isolated from one another.

Equally important, when it’s time to add the update statements, none of the existing classes need change! We code the logic to build update statements in a new subclass of Sql named UpdateSql. No other code in the system will break because of this change.

Our restructured Sql logic represents the best of all worlds. It supports the SRP. It also supports another key OO class design principle known as the Open-Closed Principle, or OCP:4 Classes should be open for extension but closed for modification. Our restructured Sql class is open to allow new functionality via subclassing, but we can make this change while keeping every other class closed. We simply drop our UpdateSql class in place.

4. [PPP].

We want to structure our systems so that we muck with as little as possible when we update them with new or changed features. In an ideal system, we incorporate new features by extending the system, not by making modifications to existing code.

Isolating from Change
Needs will change, therefore code will change. We learned in OO 101 that there are concrete classes, which contain implementation details (code), and abstract classes, which represent concepts only. A client class depending upon concrete details is at risk when those details change. We can introduce interfaces and abstract classes to help isolate the impact of those details.

Dependencies upon concrete details create challenges for testing our system. If we’re building a Portfolio class and it depends upon an external TokyoStockExchange API to derive the portfolio’s value, our test cases are impacted by the volatility of such a lookup. It’s hard to write a test when we get a different answer every five minutes!

Instead of designing Portfolio so that it directly depends upon TokyoStockExchange, we create an interface, StockExchange, that declares a single method:
```java
   public interface StockExchange {
      Money currentPrice(String symbol);
   }
```
We design TokyoStockExchange to implement this interface. We also make sure that the constructor of Portfolio takes a StockExchange reference as an argument:
```java
   public Portfolio {
      private StockExchange exchange;
      public Portfolio(StockExchange exchange) {
         this.exchange = exchange;
      }
   // …
   }
```
Now our test can create a testable implementation of the StockExchange interface that emulates the TokyoStockExchange. This test implementation will fix the current value for any symbol we use in testing. If our test demonstrates purchasing five shares of Microsoft for our portfolio, we code the test implementation to always return $100 per share of Microsoft. Our test implementation of the StockExchange interface reduces to a simple table lookup. We can then write a test that expects $500 for our overall portfolio value.
```java
   public class PortfolioTest {
      private FixedStockExchangeStub exchange;
      private Portfolio portfolio;
 
      @Before
      protected void setUp() throws Exception {
        exchange = new FixedStockExchangeStub();
        exchange.fix(”MSFT”, 100);
        portfolio = new Portfolio(exchange);
      }
      
      @Test
      public void GivenFiveMSFTTotalShouldBe500() throws Exception {
        portfolio.add(5, ”MSFT”);
        Assert.assertEquals(500, portfolio.value());
      }
   }
```
If a system is decoupled enough to be tested in this way, it will also be more flexible and promote more reuse. The lack of coupling means that the elements of our system are better isolated from each other and from change. This isolation makes it easier to understand each element of the system.

By minimizing coupling in this way, our classes adhere to another class design principle known as the Dependency Inversion Principle (DIP).5 In essence, the DIP says that our classes should depend upon abstractions, not on concrete details.

5. [PPP].

Instead of being dependent upon the implementation details of the TokyoStock-Exchange class, our Portfolio class is now dependent upon the StockExchange interface. The StockExchange interface represents the abstract concept of asking for the current price of a symbol. This abstraction isolates all of the specific details of obtaining such a price, including from where that price is obtained.

================================================
FILE: docs/ch11.md
================================================
# 第 11 章 Systems
by Dr. Kevin Dean Wampler

![](figures/ch11/11_1fig_martin.jpg)

“Complexity kills. It sucks the life out of developers, it makes products difficult to plan, build, and test.”

—Ray Ozzie, CTO, Microsoft Corporation

HOW WOULD YOU BUILD A CITY?
Could you manage all the details yourself? Probably not. Even managing an existing city is too much for one person. Yet, cities work (most of the time). They work because cities have teams of people who manage particular parts of the city, the water systems, power systems, traffic, law enforcement, building codes, and so forth. Some of those people are responsible for the big picture, while others focus on the details.

Cities also work because they have evolved appropriate levels of abstraction and modularity that make it possible for individuals and the “components” they manage to work effectively, even without understanding the big picture.

Although software teams are often organized like that too, the systems they work on often don’t have the same separation of concerns and levels of abstraction. Clean code helps us achieve this at the lower levels of abstraction. In this chapter let us consider how to stay clean at higher levels of abstraction, the system level.

SEPARATE CONSTRUCTING A SYSTEM FROM USING IT
First, consider that construction is a very different process from use. As I write this, there is a new hotel under construction that I see out my window in Chicago. Today it is a bare concrete box with a construction crane and elevator bolted to the outside. The busy people there all wear hard hats and work clothes. In a year or so the hotel will be finished. The crane and elevator will be gone. The building will be clean, encased in glass window walls and attractive paint. The people working and staying there will look a lot different too.

Software systems should separate the startup process, when the application objects are constructed and the dependencies are “wired” together, from the runtime logic that takes over after startup.

The startup process is a concern that any application must address. It is the first concern that we will examine in this chapter. The separation of concerns is one of the oldest and most important design techniques in our craft.

Unfortunately, most applications don’t separate this concern. The code for the startup process is ad hoc and it is mixed in with the runtime logic. Here is a typical example:
```java
   public Service getService() {
     if (service == null)
       service = new MyServiceImpl(…); // Good enough default for most cases?
     return service;
   }
```
This is the LAZY INITIALIZATION/EVALUATION idiom, and it has several merits. We don’t incur the overhead of construction unless we actually use the object, and our startup times can be faster as a result. We also ensure that null is never returned.

However, we now have a hard-coded dependency on MyServiceImpl and everything its constructor requires (which I have elided). We can’t compile without resolving these dependencies, even if we never actually use an object of this type at runtime!

Testing can be a problem. If MyServiceImpl is a heavyweight object, we will need to make sure that an appropriate TEST DOUBLE1 or MOCK OBJECT gets assigned to the service field before this method is called during unit testing. Because we have construction logic mixed in with normal runtime processing, we should test all execution paths (for example, the null test and its block). Having both of these responsibilities means that the method is doing more than one thing, so we are breaking the Single Responsibility Principle in a small way.

1. [Mezzaros07].

Perhaps worst of all, we do not know whether MyServiceImpl is the right object in all cases. I implied as much in the comment. Why does the class with this method have to know the global context? Can we ever really know the right object to use here? Is it even possible for one type to be right for all possible contexts?

One occurrence of LAZY-INITIALIZATION isn’t a serious problem, of course. However, there are normally many instances of little setup idioms like this in applications. Hence, the global setup strategy (if there is one) is scattered across the application, with little modularity and often significant duplication.

If we are diligent about building well-formed and robust systems, we should never let little, convenient idioms lead to modularity breakdown. The startup process of object construction and wiring is no exception. We should modularize this process separately from the normal runtime logic and we should make sure that we have a global, consistent strategy for resolving our major dependencies.

Separation of Main
One way to separate construction from use is simply to move all aspects of construction to main, or modules called by main, and to design the rest of the system assuming that all objects have been constructed and wired up appropriately. (See Figure 11-1.)

The flow of control is easy to follow. The main function builds the objects necessary for the system, then passes them to the application, which simply uses them. Notice the direction of the dependency arrows crossing the barrier between main and the application. They all go one direction, pointing away from main. This means that the application has no knowledge of main or of the construction process. It simply expects that everything has been built properly.

Factories
Sometimes, of course, we need to make the application responsible for when an object gets created. For example, in an order processing system the application must create the


Figure 11-1 Separating construction in main()

![](figures/ch11/11_2fig_martin.jpg)

LineItem instances to add to an Order. In this case we can use the ABSTRACT FACTORY2 pattern to give the application control of when to build the LineItems, but keep the details of that construction separate from the application code. (See Figure 11-2.)

2. [GOF].


Figure 11-2 Separation construction with factory

![](figures/ch11/11_3fig_martin.jpg)

Again notice that all the dependencies point from main toward the OrderProcessing application. This means that the application is decoupled from the details of how to build a LineItem. That capability is held in the LineItemFactoryImplementation, which is on the main side of the line. And yet the application is in complete control of when the LineItem instances get built and can even provide application-specific constructor arguments.

Dependency Injection
A powerful mechanism for separating construction from use is Dependency Injection (DI), the application of Inversion of Control (IoC) to dependency management.3 Inversion of Control moves secondary responsibilities from an object to other objects that are dedicated to the purpose, thereby supporting the Single Responsibility Principle. In the context of dependency management, an object should not take responsibility for instantiating dependencies itself. Instead, it should pass this responsibility to another “authoritative” mechanism, thereby inverting the control. Because setup is a global concern, this authoritative mechanism will usually be either the “main” routine or a special-purpose container.

3. See, for example, [Fowler].

JNDI lookups are a “partial” implementation of DI, where an object asks a directory server to provide a “service” matching a particular name.
```java
   MyService myService = (MyService)(jndiContext.lookup(“NameOfMyService”));
```
The invoking object doesn’t control what kind of object is actually returned (as long it implements the appropriate interface, of course), but the invoking object still actively resolves the dependency.

True Dependency Injection goes one step further. The class takes no direct steps to resolve its dependencies; it is completely passive. Instead, it provides setter methods or constructor arguments (or both) that are used to inject the dependencies. During the construction process, the DI container instantiates the required objects (usually on demand) and uses the constructor arguments or setter methods provided to wire together the dependencies. Which dependent objects are actually used is specified through a configuration file or programmatically in a special-purpose construction module.

The Spring Framework provides the best known DI container for Java.4 You define which objects to wire together in an XML configuration file, then you ask for particular objects by name in Java code. We will look at an example shortly.

4. See [Spring]. There is also a Spring.NET framework.

But what about the virtues of LAZY-INITIALIZATION? This idiom is still sometimes useful with DI. First, most DI containers won’t construct an object until needed. Second, many of these containers provide mechanisms for invoking factories or for constructing proxies, which could be used for LAZY-EVALUATION and similar optimizations.5

5. Don’t forget that lazy instantiation/evaluation is just an optimization and perhaps premature!

SCALING UP
Cities grow from towns, which grow from settlements. At first the roads are narrow and practically nonexistent, then they are paved, then widened over time. Small buildings and empty plots are filled with larger buildings, some of which will eventually be replaced with skyscrapers.

At first there are no services like power, water, sewage, and the Internet (gasp!). These services are also added as the population and building densities increase.

This growth is not without pain. How many times have you driven, bumper to bumper through a road “improvement” project and asked yourself, “Why didn’t they build it wide enough the first time!?”

But it couldn’t have happened any other way. Who can justify the expense of a six-lane highway through the middle of a small town that anticipates growth? Who would want such a road through their town?

It is a myth that we can get systems “right the first time.” Instead, we should implement only today’s stories, then refactor and expand the system to implement new stories tomorrow. This is the essence of iterative and incremental agility. Test-driven development, refactoring, and the clean code they produce make this work at the code level.

But what about at the system level? Doesn’t the system architecture require preplanning? Certainly, it can’t grow incrementally from simple to complex, can it?

Software systems are unique compared to physical systems. Their architectures can grow incrementally, ifwe maintain the proper separation of concerns.

The ephemeral nature of software systems makes this possible, as we will see. Let us first consider a counterexample of an architecture that doesn’t separate concerns adequately.

The original EJB1 and EJB2 architectures did not separate concerns appropriately and thereby imposed unnecessary barriers to organic growth. Consider an Entity Bean for a persistent Bank class. An entity bean is an in-memory representation of relational data, in other words, a table row.

First, you had to define a local (in process) or remote (separate JVM) interface, which clients would use. Listing 11-1 shows a possible local interface:


Listing 11-1 An EJB2 local interface for a Bank EJB
```java
   package com.example.banking;
   import java.util.Collections;
   import javax.ejb.*;
   
   public interface BankLocal extends java.ejb.EJBLocalObject {
     String getStreetAddr1() throws EJBException;
     String getStreetAddr2() throws EJBException;
     String getCity() throws EJBException;
     String getState() throws EJBException;
     String getZipCode() throws EJBException;
     void setStreetAddr1(String street1) throws EJBException;
     void setStreetAddr2(String street2) throws EJBException;
     void setCity(String city) throws EJBException;
     void setState(String state) throws EJBException;
     void setZipCode(String zip) throws EJBException;
     Collection getAccounts() throws EJBException;
     void setAccounts(Collection accounts) throws EJBException;
     void addAccount(AccountDTO accountDTO) throws EJBException;
   }
```
I have shown several attributes for the Bank’s address and a collection of accounts that the bank owns, each of which would have its data handled by a separate Account EJB. Listing 11-2 shows the corresponding implementation class for the Bank bean.


Listing 11-2 The corresponding EJB2 Entity Bean Implementation
```java
   package com.example.banking;
   import java.util.Collections;
   import javax.ejb.*;
   
   public abstract class Bank implements javax.ejb.EntityBean {
     // Business logic…
     public abstract String getStreetAddr1();
     public abstract String getStreetAddr2();
     public abstract String getCity();
     public abstract String getState();
     public abstract String getZipCode();
     public abstract void setStreetAddr1(String street1);
     public abstract void setStreetAddr2(String street2);
     public abstract void setCity(String city);
     public abstract void setState(String state);
     public abstract void setZipCode(String zip);
     public abstract Collection getAccounts();
     public abstract void setAccounts(Collection accounts);
     public void addAccount(AccountDTO accountDTO) {
       InitialContext context = new InitialContext();
       AccountHomeLocal accountHome = context.lookup(”AccountHomeLocal”);
       AccountLocal account = accountHome.create(accountDTO);
       Collection accounts = getAccounts();
       accounts.add(account);
     }
     // EJB container logic
     public abstract void setId(Integer id);
     public abstract Integer getId();
     public Integer ejbCreate(Integer id) { … }
     public void ejbPostCreate(Integer id) { … }
     // The rest had to be implemented but were usually empty:
     public void setEntityContext(EntityContext ctx) {} 
     public void unsetEntityContext() {}
     public void ejbActivate() {}
     public void ejbPassivate() {}
     public void ejbLoad() {}
     public void ejbStore() {}
     public void ejbRemove() {}
   }
```
I haven’t shown the corresponding LocalHome interface, essentially a factory used to create objects, nor any of the possible Bank finder (query) methods you might add.

Finally, you had to write one or more XML deployment descriptors that specify the object-relational mapping details to a persistence store, the desired transactional behavior, security constraints, and so on.

The business logic is tightly coupled to the EJB2 application “container.” You must subclass container types and you must provide many lifecycle methods that are required by the container.

Because of this coupling to the heavyweight container, isolated unit testing is difficult. It is necessary to mock out the container, which is hard, or waste a lot of time deploying EJBs and tests to a real server. Reuse outside of the EJB2 architecture is effectively impossible, due to the tight coupling.

Finally, even object-oriented programming is undermined. One bean cannot inherit from another bean. Notice the logic for adding a new account. It is common in EJB2 beans to define “data transfer objects” (DTOs) that are essentially “structs” with no behavior. This usually leads to redundant types holding essentially the same data, and it requires boilerplate code to copy data from one object to another.

Cross-Cutting Concerns
The EJB2 architecture comes close to true separation of concerns in some areas. For example, the desired transactional, security, and some of the persistence behaviors are declared in the deployment descriptors, independently of the source code.

Note that concerns like persistence tend to cut across the natural object boundaries of a domain. You want to persist all your objects using generally the same strategy, for example, using a particular DBMS6 versus flat files, following certain naming conventions for tables and columns, using consistent transactional semantics, and so on.

6. Database management system.

In principle, you can reason about your persistence strategy in a modular, encapsulated way. Yet, in practice, you have to spread essentially the same code that implements the persistence strategy across many objects. We use the term cross-cutting concerns for concerns like these. Again, the persistence framework might be modular and our domain logic, in isolation, might be modular. The problem is the fine-grained intersection of these domains.

In fact, the way the EJB architecture handled persistence, security, and transactions, “anticipated” aspect-oriented programming (AOP),7 which is a general-purpose approach to restoring modularity for cross-cutting concerns.

7. See [AOSD] for general information on aspects and [AspectJ]] and [Colyer] for AspectJ-specific information.

In AOP, modular constructs called aspects specify which points in the system should have their behavior modified in some consistent way to support a particular concern. This specification is done using a succinct declarative or programmatic mechanism.

Using persistence as an example, you would declare which objects and attributes (or patterns thereof) should be persisted and then delegate the persistence tasks to your persistence framework. The behavior modifications are made noninvasively8 to the target code by the AOP framework. Let us look at three aspects or aspect-like mechanisms in Java.

8. Meaning no manual editing of the target source code is required.

JAVA PROXIES
Java proxies are suitable for simple situations, such as wrapping method calls in individual objects or classes. However, the dynamic proxies provided in the JDK only work with interfaces. To proxy classes, you have to use a byte-code manipulation library, such as CGLIB, ASM, or Javassist.9

9. See [CGLIB], [ASM], and [Javassist].

Listing 11-3 shows the skeleton for a JDK proxy to provide persistence support for our Bank application, covering only the methods for getting and setting the list of accounts.


Listing 11-3 JDK Proxy Example
```java
   // Bank.java (suppressing package names…)
   import java.utils.*;
   
   // The abstraction of a bank.
   public interface Bank {
     Collection<Account> getAccounts();
     void setAccounts(Collection<Account> accounts);
   }
   // BankImpl.java
   import java.utils.*;
 
   // The “Plain Old Java Object” (POJO) implementing the abstraction.
   public class BankImpl implements Bank {
     private List<Account> accounts;
 
     public Collection<Account> getAccounts() { 
       return accounts; 
     }
     public void setAccounts(Collection<Account> accounts) { 
       this.accounts = new ArrayList<Account>(); 
       for (Account account: accounts) {
         this.accounts.add(account);
       }
     }
   }
   // BankProxyHandler.java
   import java.lang.reflect.*;
   import java.util.*;
   // “InvocationHandler” required by the proxy API.
   public class BankProxyHandler implements InvocationHandler {
     private Bank bank;
     
     public BankHandler (Bank bank) {
       this.bank = bank;
     }
     // Method defined in InvocationHandler
     public Object invoke(Object proxy, Method method, Object[] args) 
         throws Throwable {
     String methodName = method.getName();
     if (methodName.equals(”getAccounts”)) {
       bank.setAccounts(getAccountsFromDatabase());
       return bank.getAccounts();
     } else if (methodName.equals(”setAccounts”)) {
       bank.setAccounts((Collection<Account>) args[0]);
       setAccountsToDatabase(bank.getAccounts());
       return null;
     } else {
       …
     }
   }
   // Lots of details here:
   protected Collection<Account> getAccountsFromDatabase() { … }
   protected void setAccountsToDatabase(Collection<Account> accounts) { … }
   }
 
   // Somewhere else…
 
   Bank bank = (Bank) Proxy.newProxyInstance(
     Bank.class.getClassLoader(), 
     new Class[] { Bank.class },
     new BankProxyHandler(new BankImpl()));
```
We defined an interface Bank, which will be wrapped by the proxy, and a Plain-Old Java Object (POJO), BankImpl, that implements the business logic. (We will revisit POJOs shortly.)

The Proxy API requires an InvocationHandler object that it calls to implement any Bank method calls made to the proxy. Our BankProxyHandler uses the Java reflection API to map the generic method invocations to the corresponding methods in BankImpl, and so on.

There is a lot of code here and it is relatively complicated, even for this simple case.10 Using one of the byte-manipulation libraries is similarly challenging. This code “volume”

10. For more detailed examples of the Proxy API and examples of its use, see, for example, [Goetz].

and complexity are two of the drawbacks of proxies. They make it hard to create clean code! Also, proxies don’t provide a mechanism for specifying system-wide execution “points” of interest, which is needed for a true AOP solution.11

11. AOP is sometimes confused with techniques used to implement it, such as method interception and “wrapping” through proxies. The real value of an AOP system is the ability to specify systemic behaviors in a concise and modular way.

PURE JAVA AOP FRAMEWORKS
Fortunately, most of the proxy boilerplate can be handled automatically by tools. Proxies are used internally in several Java frameworks, for example, Spring AOP and JBoss AOP, to implement aspects in pure Java.12 In Spring, you write your business logic as Plain-Old Java Objects. POJOs are purely focused on their domain. They have no dependencies on enterprise frameworks (or any other domains). Hence, they are conceptually simpler and easier to test drive. The relative simplicity makes it easier to ensure that you are implementing the corresponding user stories correctly and to maintain and evolve the code for future stories.

12. See [Spring] and [JBoss]. “Pure Java” means without the use of AspectJ.

You incorporate the required application infrastructure, including cross-cutting concerns like persistence, transactions, security, caching, failover, and so on, using declarative configuration files or APIs. In many cases, you are actually specifying Spring or JBoss library aspects, where the framework handles the mechanics of using Java proxies or byte-code libraries transparently to the user. These declarations drive the dependency injection (DI) container, which instantiates the major objects and wires them together on demand.

Listing 11-4 shows a typical fragment of a Spring V2.5 configuration file, app.xml13:

13. Adapted from http://www.theserverside.com/tt/articles/article.tss?l=IntrotoSpring25.


Listing 11-4 Spring 2.X configuration file
```xml
   <beans>
     …
     <bean id=”appDataSource”
     class=”org.apache.commons.dbcp.BasicDataSource”
     destroy-method=”close”
     p:driverClassName=”com.mysql.jdbc.Driver”
     p:url=”jdbc:mysql://localhost:3306/mydb”
     p:username=”me”/>
 
     <bean id=”bankDataAccessObject”
     class=”com.example.banking.persistence.BankDataAccessObject”
     p:dataSource-ref=”appDataSource”/>
 
     <bean id=”bank”
   class=”com.example.banking.model.Bank”
   p:dataAccessObject-ref=”bankDataAccessObject”/>
   …
 </beans>
```
Each “bean” is like one part of a nested “Russian doll,” with a domain object for a Bank proxied (wrapped) by a data accessor object (DAO), which is itself proxied by a JDBC driver data source. (See Figure 11-3.)


Figure 11-3 The “Russian doll” of decorators

![](figures/ch11/11_4fig_martin.jpg)

The client believes it is invoking getAccounts() on a Bank object, but it is actually talking to the outermost of a set of nested DECORATOR14 objects that extend the basic behavior of the Bank POJO. We could add other decorators for transactions, caching, and so forth.

14. [GOF].

In the application, a few lines are needed to ask the DI container for the top-level objects in the system, as specified in the XML file.
```java
   XmlBeanFactory bf =
     new XmlBeanFactory(new ClassPathResource(”app.xml”, getClass()));
   Bank bank = (Bank) bf.getBean(”bank”);
```
Because so few lines of Spring-specific Java code are required, the application is almost completely decoupled from Spring, eliminating all the tight-coupling problems of systems like EJB2.

Although XML can be verbose and hard to read,15 the “policy” specified in these configuration files is simpler than the complicated proxy and aspect logic that is hidden from view and created automatically. This type of architecture is so compelling that frameworks like Spring led to a complete overhaul of the EJB standard for version 3. EJB3

15. The example can be simplified using mechanisms that exploit convention over configuration and Java 5 annotations to reduce the amount of explicit “wiring” logic required.

largely follows the Spring model of declaratively supporting cross-cutting concerns using XML configuration files and/or Java 5 annotations.

Listing 11-5 shows our Bank object rewritten in EJB316.

16. Adapted from http://www.onjava.com/pub/a/onjava/2006/05/17/standardizing-with-ejb3-java-persistence-api.html


Listing 11-5 An EBJ3 Bank EJB

```java
   package com.example.banking.model;
   import javax.persistence.*;
   import java.util.ArrayList;
   import java.util.Collection;
 
   @Entity
   @Table(name = “BANKS”)
   public class Bank implements java.io.Serializable {
      @Id @GeneratedValue(strategy=GenerationType.AUTO)
      private int id;
 
      @Embeddable // An object “inlined” in Bank’s DB row
      public class Address {
         protected String streetAddr1;
         protected String streetAddr2;
         protected String city;
         protected String state;
         protected String zipCode;
      }
      @Embedded
      private Address address;
 
      @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER,
                 mappedBy=”bank”)
      private Collection<Account> accounts = new ArrayList<Account>();
 
      public int getId() {
         return id;
      }
 
      public void setId(int id) {
         this.id = id;
      }
 
      public void addAccount(Account account) {
         account.setBank(this);
         accounts.add(account);
      }
      public Collection<Account> getAccounts() {
         return accounts;
      }
   public void setAccounts(Collection<Account> accounts) {
      this.accounts = accounts;
   }
 }
```
This code is much cleaner than the original EJB2 code. Some of the entity details are still here, contained in the annotations. However, because none of that information is outside of the annotations, the code is clean, clear, and hence easy to test drive, maintain, and so on.

Some or all of the persistence information in the annotations can be moved to XML deployment descriptors, if desired, leaving a truly pure POJO. If the persistence mapping details won’t change frequently, many teams may choose to keep the annotations, but with far fewer harmful drawbacks compared to the EJB2 invasiveness.

ASPECTJ ASPECTS
Finally, the most full-featured tool for separating concerns through aspects is the AspectJ language,17 an extension of Java that provides “first-class” support for aspects as modularity constructs. The pure Java approaches provided by Spring AOP and JBoss AOP are sufficient for 80–90 percent of the cases where aspects are most useful. However, AspectJ provides a very rich and powerful tool set for separating concerns. The drawback of AspectJ is the need to adopt several new tools and to learn new language constructs and usage idioms.

17. See [AspectJ] and [Colyer].

The adoption issues have been partially mitigated by a recently introduced “annotation form” of AspectJ, where Java 5 annotations are used to define aspects using pure Java code. Also, the Spring Framework has a number of features that make incorporation of annotation-based aspects much easier for a team with limited AspectJ experience.

A full discussion of AspectJ is beyond the scope of this book. See [AspectJ], [Colyer], and [Spring] for more information.

TEST DRIVE THE SYSTEM ARCHITECTURE
The power of separating concerns through aspect-like approaches can’t be overstated. If you can write your application’s domain logic using POJOs, decoupled from any architecture concerns at the code level, then it is possible to truly test drive your architecture. You can evolve it from simple to sophisticated, as needed, by adopting new technologies on demand. It is not necessary to do a Big Design Up Front18 (BDUF). In fact, BDUF is even harmful because it inhibits adapting to change, due to the psychological resistance to discarding prior effort and because of the way architecture choices influence subsequent thinking about the design.

18. Not to be confused with the good practice of up-front design, BDUF is the practice of designing everything up front before implementing anything at all.

Building architects have to do BDUF because it is not feasible to make radical architectural changes to a large physical structure once construction is well underway.19 Although software has its own physics,20 it is economically feasible to make radical change, if the structure of the software separates its concerns effectively.

19. There is still a significant amount of iterative exploration and discussion of details, even after construction starts.

20. The term software physics was first used by [Kolence].

This means we can start a software project with a “naively simple” but nicely decoupled architecture, delivering working user stories quickly, then adding more infrastructure as we scale up. Some of the world’s largest Web sites have achieved very high availability and performance, using sophisticated data caching, security, virtualization, and so forth, all done efficiently and flexibly because the minimally coupled designs are appropriately simple at each level of abstraction and scope.

Of course, this does not mean that we go into a project “rudderless.” We have some expectations of the general scope, goals, and schedule for the project, as well as the general structure of the resulting system. However, we must maintain the ability to change course in response to evolving circumstances.

The early EJB architecture is but one of many well-known APIs that are over-engineered and that compromise separation of concerns. Even well-designed APIs can be overkill when they aren’t really needed. A good API should largely disappear from view most of the time, so the team expends the majority of its creative efforts focused on the user stories being implemented. If not, then the architectural constraints will inhibit the efficient delivery of optimal value to the customer.

To recap this long discussion,

An optimal system architecture consists of modularized domains of concern, each of which is implemented with Plain Old Java (or other) Objects. The different domains are integrated together with minimally invasive Aspects or Aspect-like tools. This architecture can be test-driven, just like the code.

OPTIMIZE DECISION MAKING
Modularity and separation of concerns make decentralized management and decision making possible. In a sufficiently large system, whether it is a city or a software project, no one person can make all the decisions.

We all know it is best to give responsibilities to the most qualified persons. We often forget that it is also best to postpone decisions until the last possible moment. This isn’t lazy or irresponsible; it lets us make informed choices with the best possible information. A premature decision is a decision made with suboptimal knowledge. We will have that much less customer feedback, mental reflection on the project, and experience with our implementation choices if we decide too soon.

The agility provided by a POJO system with modularized concerns allows us to make optimal, just-in-time decisions, based on the most recent knowledge. The complexity of these decisions is also reduced.

USE STANDARDS WISELY, WHEN THEY ADD DEMONSTRABLE VALUE
Building construction is a marvel to watch because of the pace at which new buildings are built (even in the dead of winter) and because of the extraordinary designs that are possible with today’s technology. Construction is a mature industry with highly optimized parts, methods, and standards that have evolved under pressure for centuries.

Many teams used the EJB2 architecture because it was a standard, even when lighter-weight and more straightforward designs would have been sufficient. I have seen teams become obsessed with various strongly hyped standards and lose focus on implementing value for their customers.

Standards make it easier to reuse ideas and components, recruit people with relevant experience, encapsulate good ideas, and wire components together. However, the process of creating standards can sometimes take too long for industry to wait, and some standards lose touch with the real needs of the adopters they are intended to serve.

SYSTEMS NEED DOMAIN-SPECIFIC LANGUAGES
Building construction, like most domains, has developed a rich language with a vocabulary, idioms, and patterns21 that convey essential information clearly and concisely. In software, there has been renewed interest recently in creating Domain-Specific Languages (DSLs),22 which are separate, small scripting languages or APIs in standard languages that permit code to be written so that it reads like a structured form of prose that a domain expert might write.

21. The work of [Alexander] has been particularly influential on the software community.

22. See, for example, [DSL]. [JMock] is a good example of a Java API that creates a DSL.

A good DSL minimizes the “communication gap” between a domain concept and the code that implements it, just as agile practices optimize the communications within a team and with the project’s stakeholders. If you are implementing domain logic in the same language that a domain expert uses, there is less risk that you will incorrectly translate the domain into the implementation.

DSLs, when used effectively, raise the abstraction level above code idioms and design patterns. They allow the developer to reveal the intent of the code at the appropriate level of abstraction.

Domain-Specific Languages allow all levels of abstraction and all domains in the application to be expressed as POJOs, from high-level policy to low-level details.

CONCLUSION
Systems must be clean too. An invasive architecture overwhelms the domain logic and impacts agility. When the domain logic is obscured, quality suffers because bugs find it easier to hide and stories become harder to implement. If agility is compromised, productivity suffers and the benefits of TDD are lost.

At all levels of abstraction, the intent should be clear. This will only happen if you write POJOs and you use aspect-like mechanisms to incorporate other implementation concerns noninvasively.

Whether you are designing systems or individual modules, never forget to use the simplest thing that can possibly work.

================================================
FILE: docs/ch12.md
================================================
# 第 12 章 Emergence
by Jeff Langr

![](figures/ch12/12_1fig_martin.jpg)

GETTING CLEAN VIA EMERGENT DESIGN
What if there were four simple rules that you could follow that would help you create good designs as you worked? What if by following these rules you gained insights into the structure and design of your code, making it easier to apply principles such as SRP and DIP? What if these four rules facilitated the emergence of good designs?

Many of us feel that Kent Beck’s four rules of Simple Design1 are of significant help in creating well-designed software.

1. [XPE].

According to Kent, a design is “simple” if it follows these rules:

- Runs all the tests
- Contains no duplication
- Expresses the intent of the programmer
- Minimizes the number of classes and methods

The rules are given in order of importance.

SIMPLE DESIGN RULE 1: RUNS ALL THE TESTS
First and foremost, a design must produce a system that acts as intended. A system might have a perfect design on paper, but if there is no simple way to verify that the system actually works as intended, then all the paper effort is questionable.

A system that is comprehensively tested and passes all of its tests all of the time is a testable system. That’s an obvious statement, but an important one. Systems that aren’t testable aren’t verifiable. Arguably, a system that cannot be verified should never be deployed.

Fortunately, making our systems testable pushes us toward a design where our classes are small and single purpose. It’s just easier to test classes that conform to the SRP. The more tests we write, the more we’ll continue to push toward things that are simpler to test. So making sure our system is fully testable helps us create better designs.

Tight coupling makes it difficult to write tests. So, similarly, the more tests we write, the more we use principles like DIP and tools like dependency injection, interfaces, and abstraction to minimize coupling. Our designs improve even more.

Remarkably, following a simple and obvious rule that says we need to have tests and run them continuously impacts our system’s adherence to the primary OO goals of low coupling and high cohesion. Writing tests leads to better designs.

SIMPLE DESIGN RULES 2–4: REFACTORING
Once we have tests, we are empowered to keep our code and classes clean. We do this by incrementally refactoring the code. For each few lines of code we add, we pause and reflect on the new design. Did we just degrade it? If so, we clean it up and run our tests to demonstrate that we haven’t broken anything. The fact that we have these tests eliminates the fear that cleaning up the code will break it!

During this refactoring step, we can apply anything from the entire body of knowledge about good software design. We can increase cohesion, decrease coupling, separate concerns, modularize system concerns, shrink our functions and classes, choose better names, and so on. This is also where we apply the final three rules of simple design: Eliminate duplication, ensure expressiveness, and minimize the number of classes and methods.

NO DUPLICATION
Duplication is the primary enemy of a well-designed system. It represents additional work, additional risk, and additional unnecessary complexity. Duplication manifests itself in many forms. Lines of code that look exactly alike are, of course, duplication. Lines of code that are similar can often be massaged to look even more alike so that they can be more easily refactored. And duplication can exist in other forms such as duplication of implementation. For example, we might have two methods in a collection class:
```java
   int size() {}
   boolean isEmpty() {}
```
We could have separate implementations for each method. The isEmpty method could track a boolean, while size could track a counter. Or, we can eliminate this duplication by tying isEmpty to the definition of size:
```java
   boolean isEmpty() {
      return 0 == size();
   }
```
Creating a clean system requires the will to eliminate duplication, even in just a few lines of code. For example, consider the following code:
```java
   public void scaleToOneDimension(
        float desiredDimension, float imageDimension) {
     if (Math.abs(desiredDimension - imageDimension) < errorThreshold)
        return;
     float scalingFactor = desiredDimension / imageDimension;
     scalingFactor = (float)(Math.floor(scalingFactor * 100) * 0.01f);
 
     RenderedOp newImage = ImageUtilities.getScaledImage(
        image, scalingFactor, scalingFactor);
     image.dispose();
     System.gc();
     image = newImage;
   }
   public synchronized void rotate(int degrees) {
      RenderedOp newImage = ImageUtilities.getRotatedImage(
         image, degrees);
      image.dispose();
      System.gc();
      image = newImage;
   }
```
To keep this system clean, we should eliminate the small amount of duplication between the scaleToOneDimension and rotate methods:

```java
   public void scaleToOneDimension(
        float desiredDimension, float imageDimension) {
     if (Math.abs(desiredDimension - imageDimension) < errorThreshold)
        return;
     float scalingFactor = desiredDimension / imageDimension;
     scalingFactor = (float)(Math.floor(scalingFactor * 100) * 0.01f);
     replaceImage(ImageUtilities.getScaledImage(
        image, scalingFactor, scalingFactor));
   }
   public synchronized void rotate(int degrees) {
      replaceImage(ImageUtilities.getRotatedImage(image, degrees));
   }
   privatex void replaceImage(RenderedOp newImage) {
      image.dispose();
      System.gc();
      image = newImage;
   }
```
As we extract commonality at this very tiny level, we start to recognize violations of SRP. So we might move a newly extracted method to another class. That elevates its visibility. Someone else on the team may recognize the opportunity to further abstract the new method and reuse it in a different context. This “reuse in the small” can cause system complexity to shrink dramatically. Understanding how to achieve reuse in the small is essential to achieving reuse in the large.

The TEMPLATE METHOD2 pattern is a common technique for removing higher-level duplication. For example:
```java
   public class VacationPolicy {
      public void accrueUSDivisionVacation() {
         // code to calculate vacation based on hours worked to date
         // …
         // code to ensure vacation meets US minimums
         // …
         // code to apply vaction to payroll record
         // …
      }
 
      public void accrueEUDivisionVacation() {
         // code to calculate vacation based on hours worked to date
         // …
         // code to ensure vacation meets EU minimums
         // …
         // code to apply vaction to payroll record
         // …
      }
   }
```
The code across accrueUSDivisionVacation and accrueEuropeanDivisionVacation is largely the same, with the exception of calculating legal minimums. That bit of the algorithm changes based on the employee type.

We can eliminate the obvious duplication by applying the TEMPLATE METHOD pattern.
```java
   abstract public class VacationPolicy {
      public void accrueVacation() {
         calculateBaseVacationHours();


         alterForLegalMinimums();
         applyToPayroll();
      }
 
      private void calculateBaseVacationHours() { /* … */ };
      abstract protected void alterForLegalMinimums();
      private void applyToPayroll() { /* … */ };
   }
   public class USVacationPolicy extends VacationPolicy {
      @Override protected void alterForLegalMinimums() {
          // US specific logic
      }
   }
 
   public class EUVacationPolicy extends VacationPolicy {
      @Override protected void alterForLegalMinimums() {
          // EU specific logic
      }
   }
```
The subclasses fill in the “hole” in the accrueVacation algorithm, supplying the only bits of information that are not duplicated.

EXPRESSIVE
Most of us have had the experience of working on convoluted code. Many of us have produced some convoluted code ourselves. It’s easy to write code that we understand, because at the time we write it we’re deep in an understanding of the problem we’re trying to solve. Other maintainers of the code aren’t going to have so deep an understanding.

The majority of the cost of a software project is in long-term maintenance. In order to minimize the potential for defects as we introduce change, it’s critical for us to be able to understand what a system does. As systems become more complex, they take more and more time for a developer to understand, and there is an ever greater opportunity for a misunderstanding. Therefore, code should clearly express the intent of its author. The clearer the author can make the code, the less time others will have to spend understanding it. This will reduce defects and shrink the cost of maintenance.

You can express yourself by choosing good names. We want to be able to hear a class or function name and not be surprised when we discover its responsibilities.

You can also express yourself by keeping your functions and classes small. Small classes and functions are usually easy to name, easy to write, and easy to understand.

You can also express yourself by using standard nomenclature. Design patterns, for example, are largely about communication and expressiveness. By using the standard pattern names, such as COMMAND or VISITOR, in the names of the classes that implement those patterns, you can succinctly describe your design to other developers.

Well-written unit tests are also expressive. A primary goal of tests is to act as documentation by example. Someone reading our tests should be able to get a quick understanding of what a class is all about.

But the most important way to be expressive is to try. All too often we get our code working and then move on to the next problem without giving sufficient thought to making that code easy for the next person to read. Remember, the most likely next person to read the code will be you.

So take a little pride in your workmanship. Spend a little time with each of your functions and classes. Choose better names, split large functions into smaller functions, and generally just take care of what you’ve created. Care is a precious resource.

MINIMAL CLASSES AND METHODS
Even concepts as fundamental as elimination of duplication, code expressiveness, and the SRP can be taken too far. In an effort to make our classes and methods small, we might create too many tiny classes and methods. So this rule suggests that we also keep our function and class counts low.

High class and method counts are sometimes the result of pointless dogmatism. Consider, for example, a coding standard that insists on creating an interface for each and every class. Or consider developers who insist that fields and behavior must always be separated into data classes and behavior classes. Such dogma should be resisted and a more pragmatic approach adopted.

Our goal is to keep our overall system small while we are also keeping our functions and classes small. Remember, however, that this rule is the lowest priority of the four rules of Simple Design. So, although it’s important to keep class and function count low, it’s more important to have tests, eliminate duplication, and express yourself.

CONCLUSION
Is there a set of simple practices that can replace experience? Clearly not. On the other hand, the practices described in this chapter and in this book are a crystallized form of the many decades of experience enjoyed by the authors. Following the practice of simple design can and does encourage and enable developers to adhere to good principles and patterns that otherwise take years to learn.

================================================
FILE: docs/ch13.md
================================================
# 第 13 章 Concurrency
by Brett L. Schuchert

![](figures/ch13/13_1fig_martin.jpg)

“Objects are abstractions of processing. Threads are abstractions of schedule.”

—James O. Coplien1

1. Private correspondence.

Writing clean concurrent programs is hard—very hard. It is much easier to write code that executes in a single thread. It is also easy to write multithreaded code that looks fine on the surface but is broken at a deeper level. Such code works fine until the system is placed under stress.

In this chapter we discuss the need for concurrent programming, and the difficulties it presents. We then present several recommendations for dealing with those difficulties, and writing clean concurrent code. Finally, we conclude with issues related to testing concurrent code.

Clean Concurrency is a complex topic, worthy of a book by itself. Our strategy in this book is to present an overview here and provide a more detailed tutorial in “Concurrency II” on page 317. If you are just curious about concurrency, then this chapter will suffice for you now. If you have a need to understand concurrency at a deeper level, then you should read through the tutorial as well.

WHY CONCURRENCY?
Concurrency is a decoupling strategy. It helps us decouple what gets done from when it gets done. In single-threaded applications what and when are so strongly coupled that the state of the entire application can often be determined by looking at the stack backtrace. A programmer who debugs such a system can set a breakpoint, or a sequence of breakpoints, and know the state of the system by which breakpoints are hit.

Decoupling what from when can dramatically improve both the throughput and structures of an application. From a structural point of view the application looks like many little collaborating computers rather than one big main loop. This can make the system easier to understand and offers some powerful ways to separate concerns.

Consider, for example, the standard “Servlet” model of Web applications. These systems run under the umbrella of a Web or EJB container that partially manages concurrency for you. The servlets are executed asynchronously whenever Web requests come in. The servlet programmer does not have to manage all the incoming requests. In principle, each servlet execution lives in its own little world and is decoupled from all the other servlet executions.

Of course if it were that easy, this chapter wouldn’t be necessary. In fact, the decoupling provided by Web containers is far less than perfect. Servlet programmers have to be very aware, and very careful, to make sure their concurrent programs are correct. Still, the structural benefits of the servlet model are significant.

But structure is not the only motive for adopting concurrency. Some systems have response time and throughput constraints that require hand-coded concurrent solutions. For example, consider a single-threaded information aggregator that acquires information from many different Web sites and merges that information into a daily summary. Because this system is single threaded, it hits each Web site in turn, always finishing one before starting the next. The daily run needs to execute in less than 24 hours. However, as more and more Web sites are added, the time grows until it takes more than 24 hours to gather all the data. The single-thread involves a lot of waiting at Web sockets for I/O to complete. We could improve the performance by using a multithreaded algorithm that hits more than one Web site at a time.

Or consider a system that handles one user at a time and requires only one second of time per user. This system is fairly responsive for a few users, but as the number of users increases, the system’s response time increases. No user wants to get in line behind 150 others! We could improve the response time of this system by handling many users concurrently.

Or consider a system that interprets large data sets but can only give a complete solution after processing all of them. Perhaps each data set could be processed on a different computer, so that many data sets are being processed in parallel.

Myths and Misconceptions
And so there are compelling reasons to adopt concurrency. However, as we said before, concurrency is hard. If you aren’t very careful, you can create some very nasty situations. Consider these common myths and misconceptions:

- Concurrency always improves performance.
Concurrency can sometimes improve performance, but only when there is a lot of wait time that can be shared between multiple threads or multiple processors. Neither situation is trivial.

- Design does not change when writing concurrent programs.
In fact, the design of a concurrent algorithm can be remarkably different from the design of a single-threaded system. The decoupling of what from when usually has a huge effect on the structure of the system.

- Understanding concurrency issues is not important when working with a container such as a Web or EJB container.
In fact, you’d better know just what your container is doing and how to guard against the issues of concurrent update and deadlock described later in this chapter.

Here are a few more balanced sound bites regarding writing concurrent software:

- Concurrency incurs some overhead, both in performance as well as writing additional code.
- Correct concurrency is complex, even for simple problems.
- Concurrency bugs aren’t usually repeatable, so they are often ignored as one-offs2 instead of the true defects they are.

2. Cosmic-rays, glitches, and so on.

- Concurrency often requires a fundamental change in design strategy.

CHALLENGES
What makes concurrent programming so difficult? Consider the following trivial class:
```java
   public class X {
      private int lastIdUsed;

      public int getNextId() {
           return ++lastIdUsed;
       }
   }
```
Let’s say we create an instance of X, set the lastIdUsed field to 42, and then share the instance between two threads. Now suppose that both of those threads call the method getNextId(); there are three possible outcomes:

- Thread one gets the value 43, thread two gets the value 44, lastIdUsed is 44.

- Thread one gets the value 44, thread two gets the value 43, lastIdUsed is 44.

- Thread one gets the value 43, thread two gets the value 43, lastIdUsed is 43.

The surprising third result3 occurs when the two threads step on each other. This happens because there are many possible paths that the two threads can take through that one line of Java code, and some of those paths generate incorrect results. How many different paths are there? To really answer that question, we need to understand what the Just-In-Time Compiler does with the generated byte-code, and understand what the Java memory model considers to be atomic.

3. See “Digging Deeper” on page 323.

A quick answer, working with just the generated byte-code, is that there are 12,870 different possible execution paths4 for those two threads executing within the getNextId method. If the type of lastIdUsed is changed from int to long, the number of possible paths increases to 2,704,156. Of course most of those paths generate valid results. The problem is that some of them don’t.

4. See “Possible Paths of Execution” on page 321.

CONCURRENCY DEFENSE PRINCIPLES
What follows is a series of principles and techniques for defending your systems from the problems of concurrent code.

Single Responsibility Principle
The SRP5 states that a given method/class/component should have a single reason to change. Concurrency design is complex enough to be a reason to change in it’s own right and therefore deserves to be separated from the rest of the code. Unfortunately, it is all too common for concurrency implementation details to be embedded directly into other production code. Here are a few things to consider:

5. [PPP]

- Concurrency-related code has its own life cycle of development, change, and tuning.
- Concurrency-related code has its own challenges, which are different from and often more difficult than nonconcurrency-related code.
- The number of ways in which miswritten concurrency-based code can fail makes it challenging enough without the added burden of surrounding application code.

Recommendation: Keep your concurrency-related code separate from other code.6

6. See “Client/Server Example” on page 317.

Corollary: Limit the Scope of Data
As we saw, two threads modifying the same field of a shared object can interfere with each other, causing unexpected behavior. One solution is to use the synchronized keyword to protect a critical section in the code that uses the shared object. It is important to restrict the number of such critical sections. The more places shared data can get updated, the more likely:

- You will forget to protect one or more of those places—effectively breaking all code that modifies that shared data.
- There will be duplication of effort required to make sure everything is effectively guarded (violation of DRY7).

7. [PRAG].

- It will be difficult to determine the source of failures, which are already hard enough to find.

Recommendation: Take data encapsulation to heart; severely limit the access of any data that may be shared.

Corollary: Use Copies of Data
A good way to avoid shared data is to avoid sharing the data in the first place. In some situations it is possible to copy objects and treat them as read-only. In other cases it might be possible to copy objects, collect results from multiple threads in these copies and then merge the results in a single thread.

If there is an easy way to avoid sharing objects, the resulting code will be far less likely to cause problems. You might be concerned about the cost of all the extra object creation. It is worth experimenting to find out if this is in fact a problem. However, if using copies of objects allows the code to avoid synchronizing, the savings in avoiding the intrinsic lock will likely make up for the additional creation and garbage collection overhead.

Corollary: Threads Should Be as Independent as Possible
Consider writing your threaded code such that each thread exists in its own world, sharing no data with any other thread. Each thread processes one client request, with all of its required data coming from an unshared source and stored as local variables. This makes each of those threads behave as if it were the only thread in the world and there were no synchronization requirements.

For example, classes that subclass from HttpServlet receive all of their information as parameters passed in to the doGet and doPost methods. This makes each Servlet act as if it has its own machine. So long as the code in the Servlet uses only local variables, there is no chance that the Servlet will cause synchronization problems. Of course, most applications using Servlets eventually run into shared resources such as database connections.

Recommendation: Attempt to partition data into independent subsets than can be operated on by independent threads, possibly in different processors.

KNOW YOUR LIBRARY
Java 5 offers many improvements for concurrent development over previous versions. There are several things to consider when writing threaded code in Java 5:

- Use the provided thread-safe collections.
- Use the executor framework for executing unrelated tasks.
- Use nonblocking solutions when possible.
- Several library classes are not thread safe.

Thread-Safe Collections
When Java was young, Doug Lea wrote the seminal book8 Concurrent Programming in Java. Along with the book he developed several thread-safe collections, which later became part of the JDK in the java.util.concurrent package. The collections in that package are safe for multithreaded situations and they perform well. In fact, the ConcurrentHashMap implementation performs better than HashMap in nearly all situations. It also allows for simultaneous concurrent reads and writes, and it has methods supporting common composite operations that are otherwise not thread safe. If Java 5 is the deployment environment, start with ConcurrentHashMap.

8. [Lea99].

There are several other kinds of classes added to support advanced concurrency design. Here are a few examples:

![](figures/ch13/t0183-01.jpg)

Recommendation: Review the classes available to you. In the case of Java, become familiar with java.util.concurrent, java.util.concurrent.atomic, java.util.concurrent.locks.

KNOW YOUR EXECUTION MODELS
There are several different ways to partition behavior in a concurrent application. To discuss them we need to understand some basic definitions.

![](figures/ch13/t0183-02.jpg)

Given these definitions, we can now discuss the various execution models used in concurrent programming.

Producer-Consumer9
9. http://en.wikipedia.org/wiki/Producer-consumer

One or more producer threads create some work and place it in a buffer or queue. One or more consumer threads acquire that work from the queue and complete it. The queue between the producers and consumers is a bound resource. This means producers must wait for free space in the queue before writing and consumers must wait until there is something in the queue to consume. Coordination between the producers and consumers via the queue involves producers and consumers signaling each other. The producers write to the queue and signal that the queue is no longer empty. Consumers read from the queue and signal that the queue is no longer full. Both potentially wait to be notified when they can continue.

Readers-Writers10
10. http://en.wikipedia.org/wiki/Readers-writers_problem

When you have a shared resource that primarily serves as a source of information for readers, but which is occasionally updated by writers, throughput is an issue. Emphasizing throughput can cause starvation and the accumulation of stale information. Allowing updates can impact throughput. Coordinating readers so they do not read something a writer is updating and vice versa is a tough balancing act. Writers tend to block 
Download .txt
gitextract_0vuhkduc/

├── .gitignore
├── LICENSE
├── README.md
├── docs/
│   ├── .vuepress/
│   │   └── config.js
│   ├── README.md
│   ├── apA.md
│   ├── ch1.md
│   ├── ch10.md
│   ├── ch11.md
│   ├── ch12.md
│   ├── ch13.md
│   ├── ch14.md
│   ├── ch15.md
│   ├── ch16.md
│   ├── ch17.md
│   ├── ch2.md
│   ├── ch3.md
│   ├── ch4.md
│   ├── ch5.md
│   ├── ch6.md
│   ├── ch7.md
│   ├── ch8.md
│   └── ch9.md
├── gitee-deploy.sh
└── package.json
Condensed preview — 25 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (833K chars).
[
  {
    "path": ".gitignore",
    "chars": 34,
    "preview": "node_modules/\ndocs/.vuepress/dist/"
  },
  {
    "path": "LICENSE",
    "chars": 1090,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2018-present, Yuxi (Evan) You\n\nPermission is hereby granted, free of charge, to any"
  },
  {
    "path": "README.md",
    "chars": 1071,
    "preview": "# Clean-Code-zh\n\n《代码整洁之道》中文翻译\n\n在线阅读:[http://gdut_yy.gitee.io/doc-cleancode/](http://gdut_yy.gitee.io/doc-cleancode/)\n\n<i"
  },
  {
    "path": "docs/.vuepress/config.js",
    "chars": 1097,
    "preview": "// .vuepress/config.js\nmodule.exports = {\n  // 网站的标题\n  title: \"Clean Code 中文\",\n  // 上下文根\n  base: \"/doc-cleancode/\",\n  th"
  },
  {
    "path": "docs/README.md",
    "chars": 543,
    "preview": "# Clean Code 中文\n\n<div style=\"margin: 0 auto; width: 40%;\">\n  <img src='./cover.jpg'/>\n</div>\n\n## 序\n\n## 目录\n\n- [第 1 章 整洁代码"
  },
  {
    "path": "docs/apA.md",
    "chars": 56822,
    "preview": "# Appendix A\nConcurrency II\nby Brett L. Schuchert\n\nThis appendix supports and amplifies the Concurrency chapter on page "
  },
  {
    "path": "docs/ch1.md",
    "chars": 44302,
    "preview": "# 第 1 章 Clean Code 整洁代码\n\n![](figures/ch1/1-1fig_martin.jpg)\n\nYou are reading this book for two reasons. First, you are a"
  },
  {
    "path": "docs/ch10.md",
    "chars": 32956,
    "preview": "# 第 10 章 Classes\nwith Jeff Langr\n\n![](figures/ch10/10_1fig_martin.jpg)\n\nSo far in this book we have focused on how to wr"
  },
  {
    "path": "docs/ch11.md",
    "chars": 34977,
    "preview": "# 第 11 章 Systems\nby Dr. Kevin Dean Wampler\n\n![](figures/ch11/11_1fig_martin.jpg)\n\n“Complexity kills. It sucks the life o"
  },
  {
    "path": "docs/ch12.md",
    "chars": 11734,
    "preview": "# 第 12 章 Emergence\nby Jeff Langr\n\n![](figures/ch12/12_1fig_martin.jpg)\n\nGETTING CLEAN VIA EMERGENT DESIGN\nWhat if there "
  },
  {
    "path": "docs/ch13.md",
    "chars": 31126,
    "preview": "# 第 13 章 Concurrency\nby Brett L. Schuchert\n\n![](figures/ch13/13_1fig_martin.jpg)\n\n“Objects are abstractions of processin"
  },
  {
    "path": "docs/ch14.md",
    "chars": 103831,
    "preview": "# 第 14 章 Successive Refinement\nCase Study of a Command-Line Argument Parser\n\n![](figures/ch14/14_1fig_martin.jpg)\n\nThis "
  },
  {
    "path": "docs/ch15.md",
    "chars": 27459,
    "preview": "# 第 15 章 JUnit Internals\n![](figures/ch15/15_1fig_martin.jpg)\n\nJUnit is one of the most famous of all Java frameworks. A"
  },
  {
    "path": "docs/ch16.md",
    "chars": 41077,
    "preview": "# 第 16 章 Refactoring SerialDate\n![](figures/ch16/16_1fig_martin.jpg)\n\nIf you go to http://www.jfree.org/jcommon/index.ph"
  },
  {
    "path": "docs/ch17.md",
    "chars": 64898,
    "preview": "# 第 17 章 Smells and Heuristics\n\n![](figures/ch17/17_1fig_martin.jpg)\n\nIn his wonderful book Refactoring,1 Martin Fowler "
  },
  {
    "path": "docs/ch2.md",
    "chars": 37778,
    "preview": "# 第 2 章 Meaningful Names 有意义的命名\n\n![](figures/ch2/2_1fig_martin.jpg)\n\nby Tim Ottinger\n\n## 2.1 INTRODUCTION 介绍\n\nNames are "
  },
  {
    "path": "docs/ch3.md",
    "chars": 57046,
    "preview": "# 第 3 章 Functions 函数\n\n![](figures/ch3/3_1fig_martin.jpg)\n\nIn the early days of programming we composed our systems of ro"
  },
  {
    "path": "docs/ch4.md",
    "chars": 47381,
    "preview": "# 第 4 章 Comments 注释\n\n![](figures/ch4/4_1fig_martin.jpg)\n\n“Don’t comment bad code—rewrite it.”—Brian W. Kernighan and P. "
  },
  {
    "path": "docs/ch5.md",
    "chars": 37199,
    "preview": "# 第 5 章 Formatting 格式\n\n![](figures/ch5/5_1fig_martin.jpg)\n\nWhen people look under the hood, we want them to be impressed"
  },
  {
    "path": "docs/ch6.md",
    "chars": 19990,
    "preview": "# 第 6 章 Objects and Data Structures 对象和数据结构\n\n![](figures/ch6/6_1fig_martin.jpg)\n\nThere is a reason that we keep our vari"
  },
  {
    "path": "docs/ch7.md",
    "chars": 24177,
    "preview": "# 第 7 章 Error Handling 错误处理\n\nby Michael Feathers\n\n![](figures/ch7/103fig01.jpg)\n\nIt might seem odd to have a section abo"
  },
  {
    "path": "docs/ch8.md",
    "chars": 17626,
    "preview": "# 第 8 章 Boundaries 边界\n\nby James Grenning\n\n![](figures/ch8/113fig01.jpg)\n\nWe seldom control all the software in our syste"
  },
  {
    "path": "docs/ch9.md",
    "chars": 26898,
    "preview": "# 第 9 章 Unit Tests\n\n![](figures/ch9/9_1fig_martin.jpg)\n\nOur profession has come a long way in the last ten years. In 199"
  },
  {
    "path": "gitee-deploy.sh",
    "chars": 527,
    "preview": "#!/usr/bin/env sh\n\n# abort on errors\nset -e\n\n# build\nyarn docs:build\n\n# navigate into the build output directory\ncd docs"
  },
  {
    "path": "package.json",
    "chars": 100,
    "preview": "{\n  \"scripts\": {\n    \"docs:dev\": \"vuepress dev docs\",\n    \"docs:build\": \"vuepress build docs\"\n  }\n}\n"
  }
]

About this extraction

This page contains the full source code of the glen9527/Clean-Code-zh GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 25 files (704.8 KB), approximately 184.9k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!