自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

重拾面向?qū)ο筌浖O(shè)計

開發(fā) 開發(fā)工具
在歐洲文藝復(fù)興時期,一位偉大的數(shù)學(xué)家天文學(xué)家-哥白尼,在當(dāng)時提出了日心說,駁斥了以地球為宇宙中心的天體思想,由于思想極其超前,直到半個世紀(jì)后開普勒伽利略等人經(jīng)過后期研究,才逐步認(rèn)可并確立了當(dāng)時哥白尼思想的先進(jìn)性。

 [[436419]]

你還在用面向?qū)ο蟮恼Z言,寫著面向過程的代碼嗎?

一、前言

在歐洲文藝復(fù)興時期,一位偉大的數(shù)學(xué)家天文學(xué)家-哥白尼,在當(dāng)時提出了日心說,駁斥了以地球為宇宙中心的天體思想,由于思想極其超前,直到半個世紀(jì)后開普勒伽利略等人經(jīng)過后期研究,才逐步認(rèn)可并確立了當(dāng)時哥白尼思想的先進(jìn)性。

無獨有偶,在軟件工程領(lǐng)域也上演著同樣的故事。半個世紀(jì)前 Kristen Nygaard發(fā)明了Simula語言,這也是現(xiàn)在被認(rèn)同的世界上第一個明確實現(xiàn)面向?qū)ο缶幊痰恼Z言,他提出了基于類的編程風(fēng)格,確定了"萬物皆對象"這一面向?qū)ο罄碚摰?quot;終極思想",但在當(dāng)時同樣未受到認(rèn)可。Peter Norvig 在 Design Patterns in Dynamic Programming 對此予以了駁斥,并表述我們并不需要什么面向?qū)ο?。半個世紀(jì)后 Robert C.Martin、Bertrand Meyer、Martin Fowler等人,再次印證并升華了面向?qū)ο蟮脑O(shè)計理念。編程思想的演進(jìn)也不是一蹴而就,但在這一個世紀(jì)得到了飛速的發(fā)展。

二、編程思想的演進(jìn)

從上個世紀(jì)五十年代馮·諾依曼創(chuàng)造第一臺計算機開始,一直到現(xiàn)在只有短短70年時間,從第一門計算機語言FORTRAN,到現(xiàn)在我們常用的C++,JAVA,PYTHON等,計算機語言的演進(jìn)速度遠(yuǎn)超我們所使用的任何一門自然語言。從最早的面向機器,再到面向過程,到演化為現(xiàn)在我們所使用的面向?qū)ο?。不變的是編程的宗旨,變化的是編程的思想?/p>

1.面向機器

計算機是01的世界,最早的程序就是通過這種01機器碼來控制計算機的,比如0000代表讀取,0001代表保存等。理論上這才是世界上最快的語言,無需翻譯直接運行。但弊端也很明顯,那就是幾乎無法維護(hù)。運行5毫秒,編程3小時。由于機器碼無法維護(hù),人們在此基礎(chǔ)上發(fā)明了匯編語言,READ代表0000,SAVE代表0001,這樣更易理解和維護(hù)。雖然匯編在機器碼上更可視更直觀,但本質(zhì)上還是一門面向機器的語言,依然還是存在很高的編程成本。

2.面向過程

面向過程是一種以事件為中心的編程思想,相比于面向機器的編程方式,是一種巨大的進(jìn)步。我們不用再關(guān)注機器指令,而是聚焦于具體的問題。它將一件事情拆分成若干個執(zhí)行的步驟,然后通過函數(shù)實現(xiàn)每一個環(huán)節(jié),最終串聯(lián)起來完成軟件設(shè)計。

流程化的設(shè)計讓編碼更加清晰,相比于機器碼或匯編,開發(fā)效率得到了極大改善,包括現(xiàn)在仍然有很多場景更適合面向過程來完成。但軟件工程最大的成本在于維護(hù),由于面向過程更多聚焦于問題的解決而非領(lǐng)域的設(shè)計,代碼的重用性與擴展性弊端逐步彰顯出來,隨著業(yè)務(wù)邏輯越來越復(fù)雜,軟件的復(fù)雜性也變得越來越不可控。

3.面向?qū)ο?/h3>

面向?qū)ο笠苑诸惖姆绞竭M(jìn)行思考和解決問題,面向?qū)ο蟮暮诵氖浅橄笏季S。通過抽象提取共性,通過封裝收斂邏輯,通過多態(tài)實現(xiàn)擴展。面向?qū)ο蟮乃枷氡举|(zhì)是將數(shù)據(jù)與行為做結(jié)合,數(shù)據(jù)與行為的載體稱之為對象,而對象要負(fù)責(zé)的是定義職責(zé)的邊界。面向過程簡單快捷,在處理簡單的業(yè)務(wù)系統(tǒng)時,面向?qū)ο蟮男Ч鋵嵅⒉蝗缑嫦蜻^程。但在復(fù)雜系統(tǒng)的設(shè)計上,通用性的業(yè)務(wù)流程,個性化的差異點,原子化的功能組件等等,更適合面向?qū)ο蟮木幊棠J健?/p>

但面向?qū)ο笠膊皇倾y彈,甚至有些場景用比不用還糟,一切的根源就是抽象。根據(jù) MECE法則 將一個事物進(jìn)行分類,if else 是軟件工程最嚴(yán)謹(jǐn)?shù)姆诸悺N覀冊谠O(shè)計抽象進(jìn)行分類時,不一定能抓住最合適的切入點,錯誤的抽象比沒有抽象復(fù)雜度更高。里氏替換原則的創(chuàng)始人Barbara Liskov 談抽象的力量 The Power of Abstraction。

三、面向領(lǐng)域設(shè)計

1.真在“面向?qū)ο?rdquo;嗎

  1. // 撿入客戶到銷售私海 
  2. public String pick(String salesId, String customerId){ 
  3.     // 校驗是否銷售角色 
  4.     Operator operator = dao.find("db_operator", salesId); 
  5.     if("SALES".equals(operator.getRole())){ 
  6.         return "operator not sales"
  7.     } 
  8.     // 校驗銷售庫容是否已滿 
  9.     int hold = dao.find("sales_hold", salesId); 
  10.     List<CustomerVo> customers = dao.find("db_sales_customer", salesId); 
  11.     if(customers.size() >= hold){ 
  12.         return "hold is full"
  13.     } 
  14.     // 校驗是否客戶可撿入 
  15.     Opportunity opp = dao.find("db_opportunity", customerId); 
  16.     if(opp.getOwnerId() != null){ 
  17.         return "can not pick other's customer"
  18.     } 
  19.     // 撿入客戶 
  20.     opp.setOwnerId(salesId); 
  21.     dao.save(opp); 
  22.     return "success"

這是一段CRM領(lǐng)域銷售撿入客戶的業(yè)務(wù)代碼。這是我們熟悉的Java-面向?qū)ο笳Z言,但這是一段面向?qū)ο蟠a嗎?完全面向事件,沒有封裝沒有抽象,難以復(fù)用不易擴展。相信在我們代碼庫,這樣的代碼不在少數(shù)。為什么?因為它將成本放到了未來。我們將此稱之為“披著面向?qū)ο蟮耐庖?,干著面向過程的勾當(dāng)。”

在系統(tǒng)設(shè)計的早期,業(yè)務(wù)規(guī)則不復(fù)雜,邏輯復(fù)用與擴展體現(xiàn)得也并不強烈,而面向過程的代碼在支撐這些相對簡單的業(yè)務(wù)場景是非常容易的。但軟件工程最大的成本在于維護(hù),當(dāng)系統(tǒng)足夠復(fù)雜時,當(dāng)初那些寫起來最easy的代碼,將來就是維護(hù)起來最hard的債務(wù)。

2.領(lǐng)域驅(qū)動設(shè)計

還有一種方式我們也可以這么來寫,新增“商機”模型,通過商機來關(guān)聯(lián)客戶與銷售之間的關(guān)系。而商機的歸屬也分為公海、私海等具體歸屬場景。商機除了有必要的數(shù)據(jù)外,還應(yīng)該收攏一些業(yè)務(wù)行為,撿入、開放、分發(fā)等。通過領(lǐng)域建模,利用面向?qū)ο蟮奶匦?,確定邊界、抽象封裝、行為收攏,對業(yè)務(wù)分而治之。

當(dāng)我們業(yè)務(wù)上說“商機分發(fā)到私海”,而我們代碼則是“opportunity.pickTo(privateSea)”。這是領(lǐng)域驅(qū)動所帶來的改變,面向領(lǐng)域設(shè)計,面向?qū)ο缶幊?,領(lǐng)域模型的抽象就是對現(xiàn)實世界的描述。但這并非一蹴而就的過程,當(dāng)你只觸碰到大象的身板時,你認(rèn)為這是一扇門,當(dāng)你觸碰到大象的耳朵時,你認(rèn)為是一片芭蕉。只有我們不斷抽象不斷重構(gòu),我們才能愈發(fā)接近業(yè)務(wù)的真實模型。

Use the model as the backbone of a language, Recognize that a change in the language is a change to the model.Then refactor the code, renaming classes, methods, and modules to conform to the new model

--- Eric Evans 《Domain-Driven Design Reference》

譯:使用模型作為語言的支柱,意識到言語的改變就是對模型的改變,然后重構(gòu)代碼,重命名類,方法和模塊以符合新模型。

3.軟件的復(fù)雜度

這是Martin Flowler在 Patterns of Enterprise Application Architecture 這本書中所提的關(guān)于復(fù)雜度的觀點,他將軟件開發(fā)分為數(shù)據(jù)驅(qū)動與領(lǐng)域驅(qū)動。很多時候開發(fā)的方式大家傾向于,拿到需求后看表怎么設(shè)計,然后看代碼怎么寫,這其實也是面向過程的一個表現(xiàn)。在軟件初期,這樣的方式復(fù)雜度是很低的,沒有復(fù)用沒有擴展,一人吃飽全家不餓。但隨著業(yè)務(wù)的發(fā)展系統(tǒng)的演進(jìn),復(fù)雜度會陡增。

而一開始通過領(lǐng)域建模方式,以面向?qū)ο笏季S進(jìn)行軟件設(shè)計,復(fù)雜度的上升可以得到很好的控制。先思考我們領(lǐng)域模型的設(shè)計,這是我們業(yè)務(wù)系統(tǒng)的核心,再逐步外延,到接口到緩存到數(shù)據(jù)庫。但領(lǐng)域的邊界,模型的抽象,從剛開始成本是高于數(shù)據(jù)驅(qū)動的。

The goal of software architecture is to minimize the human resources required to build and maintain the required system.

--- Robert C. Martin 《Clean Architecture》

譯:軟件架構(gòu)的終極目標(biāo)是,用最小的人力成本來滿足構(gòu)建和維護(hù)該系統(tǒng)的需求

如果剛開始我們直接以數(shù)據(jù)驅(qū)動面向過程的流程式代碼,可以很輕松的解決問題,并且之后也不會面向更復(fù)雜的場景與業(yè)務(wù),那這套模式就是最適合這套系統(tǒng)的架構(gòu)設(shè)計。如果我們的系統(tǒng)會隨著業(yè)務(wù)的發(fā)展逐漸復(fù)雜,每一次的發(fā)布都會提升下一次發(fā)布的成本,那么我們應(yīng)該考慮投入必要的成本來面向領(lǐng)域驅(qū)動設(shè)計。

四、抽象的品質(zhì)

抽象永遠(yuǎn)是軟件工程領(lǐng)域最難的命題,因為它沒有規(guī)則,沒有標(biāo)準(zhǔn),甚至沒有對錯,只分好壞,只分是否適合。同樣一份淘寶商品模型的領(lǐng)域抽象,可以算是業(yè)界標(biāo)桿了,但它并非適合你的系統(tǒng)。那我們該如何駕馭“抽象”呢?UML的創(chuàng)始人Grady booch在 Object Oriented Analysis and Design with Applications 一書中,提到了評判一種抽象的品質(zhì)可以通過如下5個指標(biāo)進(jìn)行測量:耦合性、內(nèi)聚性、充分性、完整性與基礎(chǔ)性。

1.耦合性

一個模塊與另一個模塊之間建立起來的關(guān)聯(lián)強度的測量稱之為耦合性。一個模塊與其他模塊高度相關(guān),那它就難以獨立得被理解、變化或修改。TCL語言發(fā)明者John Ousterhout教授也有同樣的觀點。我們應(yīng)該盡可能減少模塊間的耦合依賴,從而降低復(fù)雜度。

Complexity is caused by two things: dependencies and obscurity.

--- John Ousterhout 《A Philosophy of Software Design》

譯:復(fù)雜性是由兩件事引起的:依賴性和模糊性。

但這并不意味著我們就不需要耦合。軟件設(shè)計是朝著擴展性與復(fù)用性發(fā)展的,繼承天然就是強耦合,但它為我們提供了軟件系統(tǒng)的復(fù)用能力。如同摩擦力一般,起初以為它阻礙了我們前進(jìn)的步伐,實則沒有摩擦力,我們寸步難行。

2.內(nèi)聚性

內(nèi)聚性與耦合性都是結(jié)構(gòu)化設(shè)計中的概念,內(nèi)聚性測量的是單個模塊里,各個元素的的聯(lián)系程度。高內(nèi)聚低耦合,是寫在教科書里的觀點,但我們也并非何時何地都應(yīng)該盲目追求高內(nèi)聚。

內(nèi)聚性分為偶然性內(nèi)聚與功能性內(nèi)聚。金魚與消防栓,我們一樣可以因為它們都不會吹口哨,將他們抽象在一起,但很明顯我們不該這么干,這就是偶然性內(nèi)聚。最希望出現(xiàn)的內(nèi)聚是功能性內(nèi)聚,即一個類或模式的各元素一同工作,提供某種清晰界定的行為。比如我將消防栓、滅火器、探測儀等內(nèi)聚在一起,他們是都屬于消防設(shè)施,這是功能性內(nèi)聚。

3.充分性

充分性指一個類或模塊需要應(yīng)該記錄某個抽象足夠多的特征,否則組件將變得不用。比如Set集合類,如果我們只有remove、get卻沒有add,那這個類一定沒法用了,因為它沒有形成一個閉環(huán) 。不過這種情況相對出現(xiàn)較少,只要當(dāng)我們真正去使用,完成它的一系列流程操作后,缺失的一些內(nèi)容是比較容易發(fā)現(xiàn)并解決的。

4.完整性

完整性指類或模塊需要記錄某個抽象全部有意義的特征。完整性與充分性相對,充分性是模塊的最小內(nèi)涵,完整性則是模塊的最大外延。我們走完一個流程,可以清晰得知道我們?nèi)蹦男?,可以讓我們馬上補齊抽象的充分性,但可能在另一個場景這些特征就又不夠了,我們需要考慮模塊還需要具備哪些特征或者他應(yīng)該還補齊哪些能力。

5.基礎(chǔ)性

充分性、完整性與基礎(chǔ)性可以說是3個相互輔助相互制約的原則。基礎(chǔ)性指抽象底層表現(xiàn)形式最有效的基礎(chǔ)性操作(似乎用自己在解釋自己)。比如Set中的add操作,是一個基礎(chǔ)性操作,在已經(jīng)存在add的情況下,我們是否需要一次性添加2個元素的add2操作?很明顯我們不需要,因為我們可以通過調(diào)用2次add來完成,所以add2并不符合基礎(chǔ)性。

但我們試想另一個場景,如果要判斷一個元素是否在Set集合中,我們是否需要增加一個contains方法。Set已經(jīng)有foreach、get等操作了,按照基礎(chǔ)性理論,我們也可以把所有的元素遍歷一遍,然后看該元素是否包含其中。但基礎(chǔ)性有一個關(guān)鍵詞叫“有效”,雖然我們可以通過一些基礎(chǔ)操作進(jìn)行組合,但它會消耗大量資源或者復(fù)雜度,那它也可以作為基礎(chǔ)操作的一個候選者。

五、軟件設(shè)計原則

抽象的品質(zhì)可以指導(dǎo)我們抽象與建模,但總歸還是不夠具象,在此基礎(chǔ)上一些更落地更易執(zhí)行的設(shè)計原則涌現(xiàn)出來,最著名的當(dāng)屬面向?qū)ο蟮奈宕笤O(shè)計原則 S.O.L.I.D。

1.開閉原則OCP

Software entities should be open for extension,but closed for modification

-- Bertrand Meyer 《Object Oriented Software Construction》

譯:軟件實體應(yīng)當(dāng)對擴展開放,對修改關(guān)閉。

開閉原則是Bertrand Meyer 1988年在 Object Oriented Software Construction 書中所提到一個觀點,軟件實體應(yīng)該對擴展開放對修改關(guān)閉。

我們來看一個關(guān)于開閉原則的例子,需要傳進(jìn)來的用戶列表,分類型進(jìn)行二次排序,我們代碼可以這樣寫。

  1. public List<User> sort(List<User> users, Enum type){ 
  2.     if(type == AGE){ 
  3.         // 按年齡排序 
  4.         users = resortListByAge(users); 
  5.     }else if(type == NAME){ 
  6.         // 按名稱首字母排序 
  7.         users = resortListByName(users); 
  8.     }else if(type == NAME){ 
  9.         // 按客戶健康分排序 
  10.         users = resortListByHealth(users); 
  11.     } 
  12.     return users; 

上述代碼就是一個明顯違背開閉原則的例子,當(dāng)我們需要新增一種類似時,需要修改主流程。由于這些方法都定義在私有函數(shù)中,我們哪怕對現(xiàn)有邏輯做調(diào)整,我們也需要修改到這份代碼文件。

還有一種做法,可以實現(xiàn)對擴展開放對修改關(guān)閉,JDK的排序其實已經(jīng)為我們定義了這樣的標(biāo)準(zhǔn)。我們將不同的排序方式進(jìn)行抽象,每種邏輯單獨實現(xiàn),單個調(diào)整邏輯不影響其他內(nèi)容,新增排序方式也無需對已有模塊進(jìn)行調(diào)整。

2.依賴倒置DIP

High level modules shouldnot depend upon low level modules.Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions

--- Robert C.Martin C++ Report 1996

譯:高層模塊不應(yīng)該依賴低層模塊,兩者都應(yīng)該依賴抽象;抽象不應(yīng)該依賴細(xì)節(jié),細(xì)節(jié)應(yīng)該依賴抽象。

Robert C.Martin是《Clean Code》《Code Architecture》兩本經(jīng)典書籍的作者,1996年他在C++ Report中發(fā)表了一篇名為 The Dependency Inversion Principle 的文章。他認(rèn)為模塊間的依賴應(yīng)該是有序的,高層不應(yīng)該依賴低層,低層應(yīng)該依賴高層,抽象不應(yīng)該依賴細(xì)節(jié),細(xì)節(jié)應(yīng)該依賴抽象。

怎么理解Robert C.Martin的這一觀點。我們看這張圖,我們的手可以握住這個杯子,是我們依賴杯子嗎?有人說我們需要調(diào)杯子提供的hold服務(wù),我們才能握住它,所以是我們依賴杯子。但我們再思考一下,棍子我們是不是也可以握,水壺我們也可以握,但貓狗卻不行,為什么?因為我們的杯子是按照我們的手型進(jìn)行設(shè)計的,我們定義了一個可握持的holdable接口,杯子依賴我們的需求進(jìn)行設(shè)計。所以是杯子依賴我們,而非我們依賴杯子。

依賴倒置原則并非一個新創(chuàng)造的理論,我們生活的很多地方都有在運用。比如一家公司需要設(shè)立“法人”,如果這家公司出了問題,監(jiān)管局就會找公司法人。并非監(jiān)管局依賴公司提供的法人職位,它可以找到人,而是公司依賴監(jiān)管局的要求,才設(shè)立法人職位。這也是依賴倒置的一種表現(xiàn)。

3.其他設(shè)計原則

這里沒有一一將 S.O.L.I.D 一一列舉完,大家想了解的可以自行查閱。除了SOLID之外,還有一些其他的設(shè)計原則,同樣也非常優(yōu)秀。

PLOA最小驚訝原則

If a necessary feature has a high astonishment factor, it may be necessary to redesign the feature

-- Michael F. Cowlishaw

譯:如果必要的特征具有較高的驚人因素,則可能需要重新設(shè)計該特征。

PLOA最小驚訝原則是斯坦福大家計算機教授 Michael F. Cowlishaw 提出的。不管你的代碼有“多好”,如果大部分人都對此感到吃驚,或許我們應(yīng)該重新設(shè)計它。JDK中就存在一例違反PLOA原則的案例,我們來看下面這段代碼。

  1. /** 
  2.  * Set a <tt>Formatter</tt>.  This <tt>Formatter</tt> will be used 
  3.  * to format <tt>LogRecords</tt> for this <tt>Handler</tt>. 
  4.  * <p> 
  5.  * Some <tt>Handlers</tt> may not use <tt>Formatters</tt>, in 
  6.  * which case the <tt>Formatter</tt> will be remembered, but not used. 
  7.  * <p> 
  8.  * @param newFormatter the <tt>Formatter</tt> to use (may not be null
  9.  * @exception  SecurityException  if a security manager exists and if 
  10.  *             the caller does not have <tt>LoggingPermission("control")</tt>. 
  11.  */ 
  12. public synchronized void setFormatter(Formatter newFormatter) throws SecurityException { 
  13.     checkPermission(); 
  14.     // Check for a null pointer: 
  15.     newFormatter.getClass(); 
  16.     formatter = newFormatter; 

在分享會上,我故意將這行注釋遮蓋起來,大家都猜不到 newFormatter.getClass() 這句代碼寫在這里的作用。如果要檢查空指針,完全可以用Objects工具類提供的方法,實現(xiàn)完全一樣,但代碼表現(xiàn)出來的含義就千差萬別了。

  1. public static T requireNonNull(T obj) { 
  2.  
  3. if (obj == null
  4.  
  5. throw new NullPointerException(); 
  6.  
  7. return obj; 
  8.  

KISS簡單原則

Keep it Simple and Stupid

-- Robert S. Kaplan

譯:保持愚蠢,保持簡單

KISS原則是 Robert S. Kaplan 提出的一個理論,Kaplan并非是一個軟件學(xué)家,他是平衡積分卡Balanced Scorecard創(chuàng)始人,而他所提出的這個理論對軟件行業(yè)依然適用。把事情變復(fù)雜很簡單,把事情變簡單很復(fù)雜。我們需要盡量讓復(fù)雜的問題簡明化、簡單化。

六、寫在最后

軟件設(shè)計的最大目標(biāo),就是降低復(fù)雜性,萬物不為我所有,但萬物皆為我用。引用JDK集合框架創(chuàng)辦人Josh Bloch 的一句話來結(jié)束。學(xué)習(xí)編程藝術(shù)首先要學(xué)會基本的規(guī)則,然后才能知道什么時候可以打破這些規(guī)則。

You should not slavishly follow these rules, but violate them only occasionally and with good reason. Learning the art of programming, like most other disciplines, consists of first learning the rules and then learning when to break them.

--- Josh Bloch 《Effective Java》

譯:你不該盲目的遵從這些規(guī)則,應(yīng)該只在偶爾情況下,有充分理由后才去打破這些規(guī)則

學(xué)習(xí)編程藝術(shù)首先要學(xué)會基本的規(guī)則,然后才能知道什么時候可以打破這些規(guī)則

參閱書籍

1、《Object Oriented Analysis and Design with Applications》https://niexiaolong.github.io/Object%20Oriented%20Analysis%20and%20Design%20with%20Applications.pdf

2、《Clean Architecture》

https://detail.tmall.com/item.htm?id=654392764249

3、《A Philosophy of Software Design》

https://www.amazon.com/-/zh/dp/173210221X/ref=sr_1_1?qid=1636246895

 

責(zé)任編輯:武曉燕 來源: 51CTO專欄
相關(guān)推薦

2013-06-07 11:31:36

面向?qū)ο?/a>設(shè)計模式

2010-06-11 14:01:47

面向?qū)ο骍ML技術(shù)

2010-06-13 10:33:54

面向?qū)ο骍ML技術(shù)

2011-01-19 10:50:31

軟件設(shè)計師

2015-02-12 10:10:56

交互設(shè)計

2014-01-09 11:28:21

Windows 9

2009-06-30 08:50:55

微軟Windows 7操作系統(tǒng)

2010-06-12 15:52:18

面向?qū)ο骍ML技術(shù)

2013-04-17 10:46:54

面向?qū)ο?/a>

2013-05-08 09:12:44

2011-12-26 15:59:49

4GLTE無線基礎(chǔ)

2012-07-11 13:54:42

網(wǎng)頁重構(gòu)

2012-06-07 10:11:01

面向?qū)ο?/a>設(shè)計原則Java

2021-01-20 12:44:22

JAVA編程語言軟件

2021-01-20 12:43:07

編程語言Java

2017-08-28 15:00:20

軟件系統(tǒng)架構(gòu)風(fēng)格

2019-08-12 14:45:50

軟件設(shè)計Java

2011-12-01 20:03:18

Android

2013-05-08 09:21:51

開始按鈕Windows Blu

2013-04-17 17:20:59

Windows 8.1開始按鈕
點贊
收藏

51CTO技術(shù)棧公眾號