目錄
- 大家理解的Context
- 我的Context
- 舉例子
- 總結
- 進階
大家理解的Context
搜索關鍵詞“程序上下文” “Context”,能搜到一些答案,但講得都不是很細,比如:
-
維持一段程序正常運行的所需要的外部變量的值的集合,叫做上下文
來自https://www.jianshu.com/p/6e07d31fa190
-
用例子解釋不同場景下的意思:小美氣呼呼對我說:“你去死吧”,我當時就哭了
來自https://blog.csdn.net/fenghuoliuxing990124/article/details/84502676
講了很多, 不知道其他讀者是否領會了精神
我的Context
核心含義:上下文是完成一個邏輯涉及到的信息。
上下文需要區分程序上下文和業務上下文。
-
程序上下文
程序運行時,一個方法對應的數據空間,有一個專有名詞:棧幀、stack frame。他代表了方法可以訪問的數據。感興趣可以自己查一下。
-
業務上下文
這是一個抽象概念,我解釋的上下文傾向於業務上下文。業務上下文可以和程序上下文一致,也可以跨越程序上下文。
在實際使用的時候儘量讓業務上下文和程序上下文一致。
舉例子
-
入參
一個像someOne打招呼的代碼
public void hello(String someOne) { System.out.println("Hello " + someOne) }
上面代碼中hello方法就是一個邏輯,這個邏輯涉及到了需要向誰打招呼。
業務上下文包含:someOne
hello程序棧幀就是程序上下文,
程序上下文包含:參數變量someOne
-
Local變量
需求變成了連續hello兩次,代碼如下
public void helloTwice(String someOne) { for (int i = 0; i < 2; i++) { System.out.println("Hello " + someOne) } }
業務上下文包含:someOne、當前執行次數i、需要執行次數2
helloTwice程序棧幀就是程序上下文,
程序上下文包含:someOne、當前執行次數i、需要執行次數2
-
成員變量
需求變成,每個人按照自己習慣的次數say hello。
public class Person { private int habitHelloTime; public void hello(String someOne) { for (int i = 0; i < habitHelloTime; i++) { System.out.println("Hello " + someOne) } } }
業務上下文包含:someOne、當前執行次數i、habitHelloTime
程序上下文包含:someOne、當前執行次數i、當前對象this
雖然程序只是用到了this.habitHelloTime,但程序可以訪問整個對象this。此時程序上下文和業務上下文不完全一致。程序中Person只有一個成員,所以程序上下文和業務上下文差別不大。
-
非靜態內部類訪問parent
需求變化爲給someOne捎個話,所以延遲hello。
public class Person { private int habitHelloTime; public void helloLater(String someOne) { threadPool.run(new Speaker(someOne)); } private class Speaker { private String someOne; public void run() { for (int i = 0; i < habitHelloTime; i++) { System.out.println("Hello " + someOne) } } } }
業務上下文包含:someOne、當前執行次數i、habitHelloTime
這裏有多個方法,涉及到多個程序上下文
- helloLater程序上下文包括:someOne、threadPool、this(helloLater通過this創建了Speaker)
- Speaker.run程序上下文包括:Speaker.this、Person.this和當前執行次數i。實際使用Speaker.this.someOne、Person.this.habitHelloTime和當前執行次數i
這裏最接近業務上下文的程序上下文是Speaker.run,但是Speaker.run具有的訪問權限遠超他所需要的權限。
-
全局
需求沒有變,代碼也沒有變。補充一下,我們在代碼中順其自然的使用了System.out。這是一個全局信息,所有的邏輯都可以訪問到,使用起來很方便,但是他不在我們的業務範圍內,我們在表達業務邏輯時,默認使用了控制檯。
-
進一步優化
需求沒有變,只是修改了程序
public class Person { private int habitHelloTime; public void helloLater(String someOne) { threadPool.run(new Speaker(someOne, habitHelloTime, System.out)); } } private class Speaker { private String someOne; private int time; private PrintStream out; public void speak() { for (int i = 0; i < time; i++) { out.println("Hello " + someOne) } } }
業務上下文包含:someOne、當前執行次數i、habitHelloTime
程序上下文包含:this.someOne、當前執行次數i、this.time、this.out
這裏的業務只是按照個人方式向某人打招呼,通過什麼方式打招呼,並不在業務範圍內,所以業務上並沒有提出打招呼的方式,實現時也不能具體化爲System.out。
現在的Speaker對象就是完整的業務上下文,同時包含了業務邏輯 。
總結
- 核心含義:上下文是完成一個邏輯涉及到的信息。
- 表現:代碼可以訪問到的東西。
- 沒有邏輯,沒有上下文。如果沒有邏輯,也訪問到了,說明這個變量的作用域太大了
進階
這裏有一個更爲複雜的場景:讀書。
讀書邏輯:
1、開燈
2、讀書
3、關燈
-
實現代碼如下:
// 代碼A-1 public class Person { public void readBook(String book) { openLight(); lookBook(book); closeLight(); } }
業務上下文:書
程序上下文:書
-
實際業務中我們有很多燈,打開燈的時候會得到一個燈的ID,關閉也要關閉確定的燈
// 代碼A-2 public class Person { public void readBook(String book) { String lightId = openLight(); lookBook(book); closeLight(lightId); } }
業務上下文:燈、書
程序上下文:燈、書
這裏的lightId在readBook上下文中,它的作用域很小。也可以寫成另外的樣子
// 代碼B-2 public class Light { private String lightId; public void open(); public void close(); } public class Person { public void readBook(String book) { Light light = new Light(); light.open(); lookBook(book); light.close(); } }
根據上下文,抽象出了一個燈的對象。
這裏的lightId在Light中,Light對象成爲一個關於燈的上下文,而light在readBook上下文中。
-
現在還有一個情況,我們有很多燈,看書也應該在打開的燈下看,所以lookBook上下文也應該有lightId。
下面有三種實現方式
// 代碼A-3 public class Person { public void readBook(String book) { String lightId = openLight(); lookBook(book, lightId); closeLight(lightId); } } // 代碼B-3 public class Light { private String lightId; public void open(); public void close(); } public class Person { public void readBook(String book) { Light light = new Light(); light.open(); lookBook(book, light.id); light.close(); } } // 代碼C-3 public class ReadBookContext { private String lightId; private String book; } public class Person { public void readBook(String book) { ReadBookContext context = new ReadBookContext(book); openLight(context); lookBook(context); closeLight(context); } }
A、B、C三個方案都比較好的實現了這個邏輯
- A:適合開燈關燈操作簡單,複用性不強的場景
- B:適合開關燈邏輯複雜,可以複用,讀書場景涉及的動作比較少時
- C:適合業務上下文內容很多時。當前只有兩個不明顯。
到這裏大家可以看到讀書的業務上下文,在C中完全和程序上下文分家了。
遇到複雜業務,通過上下文的方式可以有效解決擴展和傳參問題,提升可維護性。