[TOC]
在我開始接觸架構設計的時候,我對于這個知識點覺得很奇怪。因為架構設計看上去是一個很復雜的話題,然而他是屬于設計的一部分。如果你懂得什么是美、什么是丑,那么我想你也是懂得設計的。而設計是一件很有意思的事——剛開始寫字時,我們被要求去臨摹別人的字體,到了一定的時候,我們就可以真正的去設計。
## 自我總結
總結在某種意義上相當于自己對自己的反饋:

Output is Input
當我們向自己輸入更多反饋的時候,我們就可以更好地調整我們的方向。它屬于輸出的一部分,而我們也在不斷調整我們的輸入的時候,我們也在導向更好地輸出。
### 吾日三省吾身
> 為什么你不看不到自己的方向?
## Retro
Retro,又可以稱為回顧,它的目的是對團隊的激勵、改進。它的模式的特點就是讓我們更關注于 Less Well,即不好的地方。當我們無法變得更好的時候,它可以幫助我們反觀團隊自身,即不要讓現狀變得更差,避免讓破窗效應[2](http://growth.phodal.com/#fn2)難以發生。
在敏捷團隊里,Retro 通常會發生一個迭代的結束與下一個迭代的開始之間,這看上去就是我們的除舊迎新。相信很多人都會對自我進行總結,隨后改進。而 Retro 便是對團隊進行改進,即發生了一些什么不好的事,而這些事可以變好,那么我們就應該對此進行改進。
Retro 是以整個團隊為核心去考慮問題的,通常來說沒有理由以個人為對象。因為敏捷回顧有一個最高指導原則,即:
> 無論我們發現了什么,考慮到當時的已知情況、個人的技術水平和能力、可用的資源,以及手上的狀況,我們理解并堅信:每個人對自己的工作都已全力以赴。
下面就讓我們來看看在一個團隊里是如何 Retro 的。
### Retro 的過程
它不僅僅可以幫助我們發現團隊里的問題,也可以集思廣益的尋找出一些合適的解決方案。Retro 的過程和我們之前說的數據分析是差不多的,如下圖所示:

Retro 流程
即:
1. 設定會議目標。在會議最開始的時候我們就應該對會議的內容達成一種共識,我們要回顧的主題是啥,我們要回顧哪些內容。如果是一般性的迭代 Retro,那么我們的會議主題就很明顯了。如果是針對某一個特定項目的 Retro,那么主題也很明顯。
2. Retro 的回顧。即回顧上一個 Retro 會議的 Action 情況,并進行一個簡單的小結。
3. 收集數據。收集數據需要依賴于我們收集數據的模式,要下面將會說到的四種基本維度,或者是雷達圖等等。不同的收集數據的形式有不同的特別,團隊里的每個人都應該好好去參與。
4. 激發靈感。當我們尋找到團隊中一個值得去慶祝的事,或者一個出了問題的事,我們就應該對這個問題進行討論。并且對其展開了解、調查,讓大家進一步看到問題,看到問題的根源。
5. 決定做什么。現在我們已經做了一系列的事,最重要的來了,就是決定我們去做什么。我們應該對之前的問題做出怎樣的改進。
6. 總結和收尾。記錄會議成果,更新文檔等等。
### 三個維度
以我們為例,我們以下面的三個維度去進行 Retro:
1. Well.
2. Less Well.
3. Suggestion
當然最后還會有一個Action:
1. Action
該模式的特點是會讓我們更多的關注 Less Well,關注我們做的不好的那些。

Retro
**Well**。我們在 Well 里記錄一些讓我們開心的事,如最近天氣好、迭代及時完成、沒有加班等等,這些事從理論上來說應該繼續保持(KEEP)下去。
**Less Well**。關注于在這個迭代的過程中,發生了一些什么不愉快的事。一般來說,我們就會對 Less Well 加以細致的討論,找出問題的根源,并試圖找到一個解決方案。換句話來說,就是改變(CHANGE)。
**Suggestion/Puzzle**。如果我們可以直接找到一些建議,那么我們就可以直接提出來。并且如果我們對當前團隊里的一些事情,有一些困惑那么也應該及早的提出來。
**Action**。當我們對一些事情有定論的時候,我們就會提出相應的 Action。這些 Action 應該有相應的人去執行,并且由團隊來追蹤。
## 架構模式
> 模式就是最好的架構。
#### 架構的產生
在我開始接觸架構設計的時候,我買了幾本書然后我就開始學習了。我發現在這些書中都出現了一些相似的東西,如基本的分層設計、Pipe and Filters 模式、MVC 模式。然后,我開始意料到這些模式本身就是最好的架構。
MVC 模式本身也是接于分層而設計的,如下圖是 Spring MVC 的請求處理過程:

Spring MVC
而這個框架只是框架本身的架構,這一類也是我們預先設計好的框架。
在框架之上,我們會有自己本身的業務所帶來的模式。如下圖是我的網上搜羅到的一個簡單的發送郵件的架構:

發送郵件中的 Pipe and Filters 模式
這樣的模式則是由業務發展的過程中演進出來的。
### 預設計式架構
在我們日常使用的框架多數是預先設計的構架,因為這個架構本身的目標是明確的。系統會圍繞一定的架構去構建,并且在這個過程中架構會幫助我們更好地理解系統。如下圖所示的是 Emacs 的架構:

Emacs 架構
它采用的是交互式應用程序員應用廣泛的模型-視圖-控制器模式。
無論是瀑布式開發——設計好系統的框架,然后對系統的每個部分進行獨立的完善和設計,最后系統再集成到一起。還是敏捷式開發——先做出 MVP,再一步步完善。他們都需要一定的預先式設計,只是傳統的開發模式讓兩者看上去是等同的。
在過去由于 IT 技術變革小,新技術產生的速率也比較低,預先設計系統的架構是一種很不錯的選擇。然而,技術的發展趨勢是越來越快,現有的設計往往在很短的一些時間里就需要推倒重來。
### 演進式架構:擁抱變化
演進式架構則是我們日常工作的業務代碼庫演進出來的。由于業務本身在不斷發展,我們不斷地演進系統的架構。也就是這樣模式下產生的架構系統會更加穩定,也更加優美。僅僅依賴于事先的設計,而不考慮架構在后期業務中的變化是一種不可取的設計模式。
這不并意味著不采用預先式設計,而是不一味著去靠原先系統的架構。
## 浮現式設計
設計模式不是一開始就有的,好的軟件也不是一開始就設計成現在這樣的,好的設計亦是如此。
導致我們重構現有系統的原因有很多,但是多數是因為原來的代碼變得越來越不可讀,并且重構的風險太大了。在實現業務邏輯的時候,我們快速地用代碼實現,沒有測試,沒有好的設計。
而下圖算是最近兩年來想要的一個答案:

浮現式設計
浮現式設計是一種敏捷技術,強調在開發過程中不斷演進。軟件本身就不應該是一開始就設計好的,他需要經歷一個演化的過程。
### 意圖導向
就和 Growth 一樣在最開始的時候,我不知道我想要的是怎樣的——我只有一個想法以及一些相對應的實踐。接著我便動手開始做了,這是我的風格。不得不說這是結果導向編程,也是大部分軟件開發采用的方法。
所以在一開始的時候,我們就有了下面的代碼:
~~~
if (rating) {
$scope.showSkillMap = true;
skillFlareChild[skill.text] = [rating];
$scope.ratings = $scope.ratings + rating;
if (rating >= 0) {
$scope.learnedSkills.push({
skill: skill.text,
rating: rating
});
}
if ($scope.ratings > 250) {
$scope.isInfinite = true;
}
}
~~~
代碼在不經意間充斥著各種 Code Smell:
1. Magic Number
2. 超長的類
3. 等等
### 重構
還好我們在一開始的時候寫了一些測試,這讓我們可以有足夠的可能性來重構代碼,而使得其不至于變成遺留代碼。而這也是我們推崇的一些基本實踐:
> 紅 -> 綠 -> 重構
測試是系統不至于腐爛的一個后勤保障,除此我們還需要保持對于 Code Smell 的嗅覺。如上代碼:
~~~
if ($scope.ratings > 250) {
$scope.isInfinite = true;
}
~~~
上面代碼中的“250”指的到底是?這樣的數字怎么能保證別人一看代碼就知道250到底是什么?
如下的代碼就好一些:
~~~
var MAX_SKILL_POINTS = 250;
if ($scope.ratings > MAX_SKILL_POINTS) {
$scope.isInfinite = true;
}
~~~
而在最開始的時候我們想不到這樣的結果。最初我們的第一直覺都是一樣的,然而只要我們保持著對 Code Smell 的警惕,情況就會發生更多的變化。
重構是區分普通程序員和專業程序員的一個門檻,而這也是練習得來的一個結果。
### 模式與演進
如果你還懂得一些設計模式,那么想來,軟件開發這件事就變得非常簡單——我們只需要理解好需求即可。
從一開始就使用模式,要么你是專家,要么你是在自尋苦惱。模式更多的是一些實現的總結,對于多數的實現來說,他們有著諸多的相似之處,他們可以使用相同的模式。
而在需求變化的過程中,一個設計的模式本身也是在不斷的改變。如果我們還固執于原有的模式,那么我們就會犯下一個又一個的錯誤。
在適當的時候改變原有的模式,進行一些演進變顯得更有意義一些。如果我們不能在適當的時候引進一些新的技術來,那么舊有的技術就會不斷累積。這些技術債就會不斷往下疊加,那么這個系統將會接近于崩塌。而我們在一開始所設定的一些業務邏輯,也會隨著系統而逝去,這個公司似乎也要到盡頭了。
而如果我們可以不斷地演進系統——抽象服務、拆分模塊等等。業務在技術不斷演進地過程中,得以保留下來。
## 每個人都是架構師
每一個程序員都是架構師。平時在我們工作的時候,架構師這個 Title 都被那些非常有經歷的開發人員占據著。然而,如果你喜歡刷刷 Github,喜歡做一些有意思的東西,那么你也將是一個架構師。
### 如何構建一個博客系統
#### 如果你需要幫人搭建一個博客你先會想到什么?
先問一個問題,如果要讓你搭建一個博客你會想到什么技術解決方案?
1. 靜態博客(類似于 GitHub Page)
2. 動態博客(可以在線更新,如 WordPress)
3. 半動態的靜態博客(可以動態更新,但是依賴于后臺構建系統)
4. 使用第三方博客
這只是基本的骨架。因此如果只有這點需求,我們無法規劃出整體的方案。現在我們又多了一點需求,我們要求是獨立的博客,這樣我們就把第4個方案去掉了。但是就現在的過程來說,我們還是有三個方案。
接著,我們就需要看看 Ta 需要怎樣的博客,以及他有怎樣的更新頻率?以及他所能接受的價格?
先說說價格——從價格上來說,靜態博客是最便宜的,可以使用 AWS S3 或者國內的云存儲等等。從費用上來說,一個月只需要幾塊錢,并且快速穩定,可以接受大量的流量訪問。而動態博客就貴了很多倍——我們需要一直開著這個服務器,并且如果用戶的數量比較大,我們就需要考慮使用緩存。用戶數量再增加,我們就需要更多地服務器了。而對于半動態的靜態博客來說,需要有一個 Hook 檢測文章的修改,這樣的 Hook 可以是一個客戶端。當修改發生的時候,運行服務器,隨后生成靜態網頁。最后,這個網頁接部署到靜態服務器上。
從操作難度上來說,動態博客是最簡單的,靜態博客緊隨其后,半動態的靜態博客是最難的。
整的性價比考慮如下:
| x | 動態博客 | 靜態博客 | 半動態的靜態博客 |
| --- | --- | --- | --- |
| 價格 | 幾十到幾百元 | 幾元 | 依賴于更新頻率 幾元~幾十元 |
| 難度 | 容易 | 稍有難度 | 難度稍大 |
| 運維 | 不容易 | 容易 | 容易 |
| 數據存儲 | 數據庫 | 無 | 基于 git 的數據庫 |
現在,我們已經達到了一定的共識。現在,我們已經有了幾個方案可以提用戶選擇。而這時,我們并不了解進一步的需求,只能等下面的結果。
客戶需要可以看到文章的修改變化,這時就去除了靜態博客。現在還有第1和第3種方案可以選,考慮到第3種方案實現難度比較大,不易短期內實現。并且第3種方案可以依賴于第1種方案,就采取了動態博客的方案。
但是,問題實現上才剛剛開始。
#### 我們使用怎樣的技術?
作為一個團隊,我們需要優先考慮這個問題。使用怎樣的技術解決方案?而這是一個更復雜的問題,這取決于我們團隊的技術組成,以及未來的團隊組成。
如果在現有的系統中,我們使用的是 Java 語言。并不意味著,每個人都喜歡使用 Java 語言。因為隨著團隊的變動,做這個技術決定的那些人有可能已經不在這個團隊里。并且即使那些人還在,也不意味著我們喜歡在未來使用這個語言。當時的技術決策都是在當時的環境下產生的,在現在看來很扯的技術決策,有可能在當時是最好的技術決策。
對于一個優秀的團隊來說,不存在一個人對所有的技術棧都擅長的情況——除非這個團隊所從事的范圍比較小。在一個復雜的系統里,每個人都負責系統的相應的一部分。盡管到目前為止并沒有好的機會去構建自己的團隊,但是也希望總有一天有這樣的機會。在這樣的團隊里,只需要有一個人負責整個系統的架構。其中的人可以在自己擅長的層級里構建自己的架構。因此,讓我們再回到我們的博客中去,現在我們已經決定使用動態的博客。然后呢?
作為一個博客我們至少有前后臺,這樣我們可能就需要兩個開發人員。

前后臺
(PS:當然,我們也可以使用 React,但是在這里先讓我們忽略掉這個框架,緊耦合會削弱系統的健壯性。)
接著,作為一個前端開發人員,我們還需要考慮的兩個問題是:
1. **我們的博客系統是否是單頁面應用?**。
2. **要不要做成響應式設計**。
第二個問題不需要和后臺開發人員做溝通就可以做決定了。而第一個問題,我們則需要和后臺開發人員做決定。單頁面應用的天然優勢就是:由于系統本身是解耦的,他與后臺模板系統脫離。這樣在我們更換前端或者后臺的時候,我們都不需要去考慮使用何種技術——因為我們使用 API 作為接口。現在,我們決定做成單頁面應用,那么我們就需要定義一個 API。而在這時,我們就可以決定在前臺使用何種框架: AngularJS、Backbone、Vue.js、jQuery,接著我們的架構可以進一步完善:

含前端的架構
在這時,后臺人員也可以自由地選擇自己的框架、語言。后臺開發人員只需要關注于生成一個 RESTful API 即可,而他也需要一個好的 Model 層來與數據庫交付。

含前端后臺的架構
現在,我們似乎已經完成了大部分的工作?我們還需要:
1. 部署到何處操作系統
2. 使用何處數據庫
3. 如何部署
4. 如何去分析數據
5. 如何做測試
6. 。。。
相信看完之前的章節,你也有了一定的經驗了,你也可以成為一個架構師了。
### 相關閱讀資料
-《程序員必讀之軟件架構》
## 架構解耦
解耦是一件很有意思的過程,它也能反應架構的變遷。
### 從 MVC 與微服務
在我初識架構是什么的時候,我看到了 MVC 模式架構。這種模式是基于分層的結構,要理解起邏輯也很簡單。這個模式如下圖所示:

Spring MVC
由我們的 Front controller 來處理由客戶端(瀏覽器)發過來的請求,實際上這里的 Front controller 是 DispatcherServlet。 DispatcherServlet 負責將請求派發到特定的 handler,接著交由對應的Controller來處理這個請求。依據請求的內容,Controller 將創建相應 model。隨后這個 model 將傳到前端框架中渲染,最后再返回給瀏覽器。
但是這樣的架構充滿了太多的問題,如 view 與 controller 的緊密耦合、controller 粒度難以把控的問題等等。
#### Django MTV
我使用 Django 差不多有四年了,主要是用在我的博客上。與 MVC 模式一對比,我發現 Django 在分層上還是很有鮮明特性的:

Django MTV架構
在 Django 中沒有 Controller 的概念,Controller 做的事都交由 URL Dispatcher,而這是一個高級的 URL Dispatcher。它使用正則表達式匹配 URL,然后調用合適的 Python 函數。然后這個函數就交由相應的 View 層來處理,而這個 View 層則是處理業務邏輯的地方。處理完后,Model 將傳到 Template 層來處理。
對比如下圖如示:
| 傳統的MVC架構 | Django 架構 |
| --- | --- |
| Model | Model(Data Access Logic) |
| View | Template(Presentation Logic) |
| View | View(Business Logic) |
| Controller | Django itself |
從上面的對比中,我們可以發現 Django 把 View 分層了。以 Django 對于 MVC 的解釋來說,視圖用來描述要展現給用戶的數據。 而在 ROR 等其他的 MVC 框架中,控制器負責決定向用戶展現哪些數據,而視圖決定如何展現數據。
聯想起我最近在學的 Scala 中的 Play 框架,我發現了其中諸多的相似之處:

Play 框架異步請求
雖然在 Play 中,也有 Controller 的概念。但是對于 URL 的處理先交給了 Routes 來處理,隨后再交給 Controller 中的函數來處理。
不過與一般 MVC 架構的最大不同之處,怕是在于 Django 的 APP 架構。Django 中有一個名為 APP 的概念,它是實現某種功能的Web 應用程序。如果我們要設計一個博客系統的話,那么在這個項目中,Blogpost 是一個 APP、評論是一個 APP、用戶管理是一個 APP等等。每個 APP 之中,都會有自己的 Model、View 和 Controller。其架構如下圖所示:

Django APP 架構
當我們需要創建一個新的功能的時候,我們只需要創建一個新的 APP 即可——為這個 APP 配置新的 URL、創建新的 Model 以及新的 View。如果功能上沒有與原來的代碼重復的話,那么這就是一個獨立的 APP,并且我們可以將這個 APP 的代碼 Copy/Paste 到一個新的項目中,并且不需要做修改。
與一般的 MVC 架構相比,我們會發現我們細化了這些業務邏輯原來的三層結構,會隨著 APP 的數量發生變化。如果我們有三個 APP 的話,那么我們相當于有3*三層,但是他不是等于九層。這樣做可以從代碼上直接減少邏輯的思考,讓我們可以更加集中注意力于業務實現,同時也利于我們后期維護。
雖是如此,后來我意識到了這樣的架構并沒有太多的先進之處。而這實際上是一個美好但是不現實的東西,因為我們還是使用同一個數據庫。
#### 微服務與 Reactive
在微服務架構中,它提倡將單一應用程序劃分成一組小的服務,這些服務之間互相協調、互相配合。每個服務運行在其獨立的進程中,服務與服務間采用輕量級的通信機制互相溝通。每個服務都應該有自己獨立的數據庫來存儲數據。

分散數據
Django 從某種意義上有點接近微服務的概念,只是實際上并沒有。因為它沒有實現 Play 框架的異步請求機制。抱句話來說,應用很容易就會在調用 JDBC、Streaming API、HTTP 請求等一系列的請求中發生阻塞。
這些服務都是獨立的,對于服務的請求也是獨立的。使用微服務來構建的應用,不會因為一個服務的癱瘓讓整個系統癱瘓。最后,這一個個的微服務將合并成這個系統。

Combined List
我們將我們后臺的服務變成微服務的架構,在我們的前臺使用 Reactive 編程,這樣我們就可以結合兩者的優勢,解耦出更好的架構模式。然而,這其中還有一個讓人不爽的問題,即數據庫。如果我們使用多個數據庫,那么維護成本也隨著上升。而如果我們可以在后臺使用類似于微服務的 Django MTV 架構,并且它可以支持異步請求的話,并在前臺使用 Reactive 來編程,是不是就會更爽一點?
### CQRS
對于復雜的系統來說,上面的做法做確實很不錯。但是對于一個簡單地系統來說,這樣做是不是玩過火了?如果我們要設計一個博客系統的話,那么我們是不是可以考慮將 Write/Read 分離就可以了?
> 命令和查詢責任分離 Command Query Responsibility Segregation(CQRS)是一種將系統的讀寫操作分離為兩種獨立模型的架構模式。
#### CQS
對于這個架構的深入思考是起源于之前在理解 DDD。據說在 DDD 領域中被廣泛使用。理解 CQRS 可以用分離 Model 和 API 集合來處理讀取和寫入請求開始,即 CQS(Command Query Separation,命令查詢分離)模式。CQS 模式最早由軟件大師Bertrand Meyer(Eiffel語言之父,面向對象開-閉原則 OCP 提出者)提出。他認為,對象的行為僅有兩種:命令和查詢。
這個類型的架構如下圖所示:

CQS Basic
> 除了編寫優化的查詢類型,它可以讓我們輕松換 API 的一部分讀一些緩存機制,甚至移動讀取 API 的請求到另一臺服務器。
對于讀取和寫入相差不多的應用來說,這種架構看起來還是不錯的。而這種架構還存在一個瓶頸問題,使用同一個 RDBMS。對于寫入多、讀取少的應用來說,這種架構還是存在著不合理性。
為了解決這個問題,人們自然是使用緩存來解決這個問題了。我們在我們的應用服務外有一個 HTTP 服務器,而在 HTTP 服務器之外有一個緩存服務器,用于緩存用戶常駐的一些資源。如下圖所示:

帶緩存的 Web 架構
而實際上這樣的服務器可能是多余的——我們為什么不直接生成HTML就好了?
#### 編輯-發布分離
或許你聽過 Martin Folwer 提出的編輯-發布分享式架構:即文章在編輯時是一個形式,而發表時是另一個形式,比如用 Markdown 編輯,而用 HTML 發表。

編輯-發布分離
而最典型的應用就是流行于 GitHub 的 Hexo、Jekyll 框架之類的靜態網站。如下圖所示的是 Hexo 的工作流:

Hexo 站點工作流
我們在本地生成我們的項目,然后可以創建一個新的博客、開始編寫內容等等。接著,我們可以在本地運行起這個服務,除了查看博客的內容,還可以修改樣式等等。完成上面的工作后,我們就可以生成靜態內容,然后部署我們的應用到GitHub Page上。這一切看上去都完美,我們有兩個不同的數據源——一個是 md 格式的文本,一個是最后生成的 HTML。它們已經實現了讀寫/分離:

CQRS 進階
但是作為一個前端開發人員,沒有 JSON,用不了 Ajax 請求,我怎么把我的博客做成一個單頁面應用?
#### 編輯-發布-開發分離
因為我們需要交我們的博客轉為 JSON,而不是一個 hexo 之類的格式。有了這些 JSON 文件的存在,我們就可以把 Git 當成一個 NoSQL 數據庫。同時這些 JSON 文件也可以直接當成 API 來

Git As NoSQL DB
其次,這些博客還需要 hexo 一樣生成 HTML。
并且,開發人員在開發的時候不會影響到編輯的使用,于是就有了下面的架構:

基于 Git 的編輯-發布分離
在這其中我們有兩種不同的數據形式,即存儲著 Markdown 數據的 JSON 文件和最后生成的 HTML。
對博客數量不是很大的網站,或者說一般的網站來說,用上面的技術都不是問題。然而有大量數據的網站怎么辦?使用 EventBus:

CQRS 和 EventBus
在我之前玩的一個 Demo 中,使用 Python 中的 Scrapy 爬蟲來抓取現有的動態網站,并將其變成靜態網站部署到 AWS S3上。
但是上面僅僅只是實現了文章的顯示,我們還存在一些問題:
1. 搜索功能
2. AutoComplete
等等的這些服務是沒有用靜態 API 來實現的。
### CQRS 結合微服務
既然可以有這么多分法,并且我們都已經準備好分他們了。那么分了之后,我們就可以把他們都合到一起了。
#### Nginx as Dispatcher
最常見的解耦應用的方式中,就有一種是基于 Nginx 來分發 URL 請求。在這種情況下,對于 API 的使用者,或者最終用戶來說,他們都是同一個 API。只是在后臺里,這個 API 已經是不同的幾個 API 組成,如下圖所示:

Nginx 解耦微服務
客戶端的請求來到 API Gateway,根據不同的請求類型,這些 URL 被分發到不同的 Service,如 Review Service、Order Service 等等。
對于我們想要設計的系統來說也是如此,我們可以通過這個 Dispatcher 來解耦我們的服務。
#### CQRS 結合微服務
現在,我們想要的系統的雛形已經出現了。
從源頭上來說,我們把能緩存的內容變成了靜態的 HTML,通過 CDN 來分發。并且,我們還可以將把不同的服務獨立出來。
從實現上來說,我們將博客的數據變成了兩部分: 一個以 Git + JSON 格式存在的 API,它除了可以用于生成 HTML,另外一部分作為 API 來使用。

CQRS 結合微服務
最后,我們可以通過上面說到的 Nginx 或者 Apache 來當這里的 Request Dispatcher。
* * *
1. [基于 Jenkins 快速搭建持續集成環境](https://www.ibm.com/developerworks/cn/java/j-lo-jenkins/)[?](http://growth.phodal.com/#fnref1)
2. 以一幢有少許破窗的建筑為例,如果那些窗不被修理好,可能將會有破壞者破壞更多的窗戶。最終他們甚至會闖入建筑內,如果發現無人居住,也許就在那里定居或者縱火。又或想像一條人行道有些許紙屑,如果無人清理,不久后就會有更多垃圾,最終人們會視為理所當然地將垃圾順手丟棄在地上。因此破窗理論強調著力打擊輕微罪行有助減少更嚴重罪案,應該以零容忍的態度面對罪案。[?](http://growth.phodal.com/#fnref2)