面向对象理论(6)-Interface Programming-[A]
生活随笔
收集整理的這篇文章主要介紹了
面向对象理论(6)-Interface Programming-[A]
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
面向接口編程(A)
前面的章節(jié)對于本篇來說,只是基礎(chǔ)和鋪墊,而且講的很簡單,因為那些很容易理解。我們從這個章節(jié)開始,用大量的代碼的配合,來闡述面向接口編程。
接口的演化形式
現(xiàn)在我們回顧一下繼承相關(guān)的知識。我們現(xiàn)在給出一組新的繼承體系。它們是和圖形相關(guān)的,我們可以假設(shè)這樣的一種需求,就是我們要實現(xiàn)一個畫圖板(例如Windows的畫圖板),至少要能在上面繪制幾個圓形和矩形。于是,我們很自然底定義了如下的classes。 class?Shape??? //abstract in fact
{
????public?virtual?void?draw();
}
class?Circle?:?Shape
{
????public?override?void?draw()
????{
????????/*?draw?a?circle?on?some?device?*/
????}
}
class?Rect?:?Shape
{
????public?override?void?draw()
????{
????????/*?draw?a?Rect?on?some?device?*/
????}
}
首先要說的是,我們確實是無法給出Shape類的方法draw()的實現(xiàn)。因為它是一個抽象的類型(不要和語言中的抽象類或abstract關(guān)鍵字混淆,但是它們確實有莫大的聯(lián)系,但是我所謂的抽象是一種真實的抽象),我們不知道一個所謂的“圖形”應(yīng)該如何被畫出來。這就像你讓我畫一個圖形出來一樣,我感到很為難。我畫個圓圈或者是多邊形,五角星,似乎都不是一個具有抽象意義的圖形。
我們再回顧一下多態(tài),下面的代碼更好的詮釋了多態(tài)的作用。
Array<Shape>?shapes?=?{?new?Circle(),?new?Rect(),?new?Circle(),?new?Circle(),?new?Rect()?};
void?drawShapes()
{
??? foreach(Shape?shape?in?shapes)
??? {
??????? shape.draw();
??? }
} 這是一段偽碼,但是很自然地表現(xiàn)除了多態(tài)的從容,foreach從容器中循環(huán)枚舉出Shape類的各種不同的派生類對象,它們都多態(tài)性地調(diào)用了它們各自類型的draw()方法。而這個過程又絕沒有很明顯的顯露出某一個具體的派生類的類型的參與。于是,我們還可以很自然地再派生出圓角矩形,或者是五角星,都讓它們繼承自Shape類即可,它們也可以很自然地被放進shapes容器中,而且又不會修改循環(huán)處的分毫代碼,這就是我們所追求的可擴展性和“新增代碼不會影響已有的代碼”。
嗯,這一切都很完美,不是嘛? 然而,這真的很完美嘛?
表格(Table)是一個Shape(對象)嘛?文本(Text)是一個Shape嘛?如果我們要有若干個圖層(Layer),每一個圖層是一個Shape嘛?如果是,這些出現(xiàn)在畫圖板上的元素,它們很好的詮釋了is-a的信念嘛?
沒有,因為一個Table不是一個Shape,Text也不是Shape,但是它們也都可以被畫在上面,于是我們需要進行一次重要的演化。
我們在C++的語法教材中不強調(diào)接口的概念,而是用純抽象類來表達這一個含義,但是我更愿意用Java的interface關(guān)鍵字作為表達。但是我們要清楚,無論是接口,還是基類,抽象基類,多態(tài)性都是存在于這樣的語法關(guān)系中的。
interface?IDraw?{
????void?draw();
}
class?Circle?implements?IDraw?{
????void?draw()?{
????????/*?draw?a?circle?on?some?device?*/
????}
}
class?Rect?implements?IDraw?{
????void?draw() {
????????/*?draw?a?Rect?on?some?device?*/
????}
} 這次演化,似乎是沒有本質(zhì)改變的,特別是對于C++的編譯器來說,編譯出來的代碼都可能沒有絲毫的不同。也許很多人開始嘆氣了,認(rèn)為這種形式上的變化根本是無所謂的,甚至就是在浪費時間。
但是我想說的是,對于設(shè)計來說,這種變化是本質(zhì)的變化。因為對象的關(guān)系變化了,之前,我們說一個Circle對象也是一個Shape,它滿足is-a的經(jīng)典關(guān)系,但是現(xiàn)在,這種關(guān)系被打破了。
Can-do & Constraint
我們在前面的章節(jié)中,已經(jīng)提到了has-a和is-a的對象關(guān)系了,現(xiàn)在,我們的重點是can-do的關(guān)系,這種關(guān)系表示約束(Constraint)。
于是,在上面的代碼中,我們說Circle類實現(xiàn)了IDraw接口,或者說Circle對象能夠完成IDraw接口所要求的行為。
void?draw(IDraw?d)
{
????d.draw();
}
而這個函數(shù)就更直觀地表達出約束的概念了,“畫可畫之對象”,準(zhǔn)確地說,這個函數(shù)更直接地完成 IDraw接口的約束語義表達:只有實現(xiàn)了約束的對象才可以被傳入,并調(diào)用其draw()方法(注意,而并不是有draw()方法的對象都可以被傳入和調(diào)用,相關(guān)問題可以對比C++09的concept)。
Array<IDraw>?drawList?=?{?new?Circle(),?new?Rect(),?new?Circle(),?new?Table(),?new?Rect(),?new?TextArea()?};
void?draw()
{
????foreach(IDraw?d?in?drawList?)
????{
????????d.draw();
????}
} 我用這段偽碼,明確地表示了這樣的一個新情況,Circle被畫到畫圖板中去了,Table和TextArea也可以被畫到上面去了。
依然是存有懷疑,難道Table派生自Shape就有問題嘛?難道一定要弄出一個IDraw,讓它看起來有道理才是合理的?
不是的。首先,如果IDraw接口沒有取代Shape類,我們也要承認(rèn)這樣幾個事情,Shape類還是帶著IDraw接口的含義,盡管我們不把它抽象出來。
而且,設(shè)計沒有對錯之分,是不是適應(yīng)需求才是最實際的評判標(biāo)準(zhǔn)。我們當(dāng)前的例子還很簡單,我們只涉及到了元素的繪制(draw),但是也許還有其他的問題 (新需求總是很多的),比如ALPHA混合,圖層的遮擋,元素的選擇,螞蟻線的繪制,等等。例如,當(dāng)我們選擇了一個Rect的時候,他的四周有螞蟻線,而我們選擇了一個TextAread的時候,它的四周是帶有8個調(diào)節(jié)點的邊框。可是,我們不打算讓螞蟻線和邊框參與其他元素的遮擋計算和ALPHA混合計算,而且它們也不參與序列化,于是,它們既不是Shape,也不應(yīng)該實現(xiàn)IDraw接口。經(jīng)驗告訴我們,Shape類是不足以成為所有元素的基類的,IDraw接口也不是萬能的。具體的解決方案要看需求,如何應(yīng)對這些需求,我們會在后面的內(nèi)容中有所涉及。
前面的章節(jié)對于本篇來說,只是基礎(chǔ)和鋪墊,而且講的很簡單,因為那些很容易理解。我們從這個章節(jié)開始,用大量的代碼的配合,來闡述面向接口編程。
接口的演化形式
現(xiàn)在我們回顧一下繼承相關(guān)的知識。我們現(xiàn)在給出一組新的繼承體系。它們是和圖形相關(guān)的,我們可以假設(shè)這樣的一種需求,就是我們要實現(xiàn)一個畫圖板(例如Windows的畫圖板),至少要能在上面繪制幾個圓形和矩形。于是,我們很自然底定義了如下的classes。 class?Shape??? //abstract in fact
{
????public?virtual?void?draw();
}
class?Circle?:?Shape
{
????public?override?void?draw()
????{
????????/*?draw?a?circle?on?some?device?*/
????}
}
class?Rect?:?Shape
{
????public?override?void?draw()
????{
????????/*?draw?a?Rect?on?some?device?*/
????}
}
首先要說的是,我們確實是無法給出Shape類的方法draw()的實現(xiàn)。因為它是一個抽象的類型(不要和語言中的抽象類或abstract關(guān)鍵字混淆,但是它們確實有莫大的聯(lián)系,但是我所謂的抽象是一種真實的抽象),我們不知道一個所謂的“圖形”應(yīng)該如何被畫出來。這就像你讓我畫一個圖形出來一樣,我感到很為難。我畫個圓圈或者是多邊形,五角星,似乎都不是一個具有抽象意義的圖形。
我們再回顧一下多態(tài),下面的代碼更好的詮釋了多態(tài)的作用。
Array<Shape>?shapes?=?{?new?Circle(),?new?Rect(),?new?Circle(),?new?Circle(),?new?Rect()?};
void?drawShapes()
{
??? foreach(Shape?shape?in?shapes)
??? {
??????? shape.draw();
??? }
} 這是一段偽碼,但是很自然地表現(xiàn)除了多態(tài)的從容,foreach從容器中循環(huán)枚舉出Shape類的各種不同的派生類對象,它們都多態(tài)性地調(diào)用了它們各自類型的draw()方法。而這個過程又絕沒有很明顯的顯露出某一個具體的派生類的類型的參與。于是,我們還可以很自然地再派生出圓角矩形,或者是五角星,都讓它們繼承自Shape類即可,它們也可以很自然地被放進shapes容器中,而且又不會修改循環(huán)處的分毫代碼,這就是我們所追求的可擴展性和“新增代碼不會影響已有的代碼”。
嗯,這一切都很完美,不是嘛? 然而,這真的很完美嘛?
表格(Table)是一個Shape(對象)嘛?文本(Text)是一個Shape嘛?如果我們要有若干個圖層(Layer),每一個圖層是一個Shape嘛?如果是,這些出現(xiàn)在畫圖板上的元素,它們很好的詮釋了is-a的信念嘛?
沒有,因為一個Table不是一個Shape,Text也不是Shape,但是它們也都可以被畫在上面,于是我們需要進行一次重要的演化。
我們在C++的語法教材中不強調(diào)接口的概念,而是用純抽象類來表達這一個含義,但是我更愿意用Java的interface關(guān)鍵字作為表達。但是我們要清楚,無論是接口,還是基類,抽象基類,多態(tài)性都是存在于這樣的語法關(guān)系中的。
interface?IDraw?{
????void?draw();
}
class?Circle?implements?IDraw?{
????void?draw()?{
????????/*?draw?a?circle?on?some?device?*/
????}
}
class?Rect?implements?IDraw?{
????void?draw() {
????????/*?draw?a?Rect?on?some?device?*/
????}
} 這次演化,似乎是沒有本質(zhì)改變的,特別是對于C++的編譯器來說,編譯出來的代碼都可能沒有絲毫的不同。也許很多人開始嘆氣了,認(rèn)為這種形式上的變化根本是無所謂的,甚至就是在浪費時間。
但是我想說的是,對于設(shè)計來說,這種變化是本質(zhì)的變化。因為對象的關(guān)系變化了,之前,我們說一個Circle對象也是一個Shape,它滿足is-a的經(jīng)典關(guān)系,但是現(xiàn)在,這種關(guān)系被打破了。
Can-do & Constraint
我們在前面的章節(jié)中,已經(jīng)提到了has-a和is-a的對象關(guān)系了,現(xiàn)在,我們的重點是can-do的關(guān)系,這種關(guān)系表示約束(Constraint)。
于是,在上面的代碼中,我們說Circle類實現(xiàn)了IDraw接口,或者說Circle對象能夠完成IDraw接口所要求的行為。
void?draw(IDraw?d)
{
????d.draw();
}
而這個函數(shù)就更直觀地表達出約束的概念了,“畫可畫之對象”,準(zhǔn)確地說,這個函數(shù)更直接地完成 IDraw接口的約束語義表達:只有實現(xiàn)了約束的對象才可以被傳入,并調(diào)用其draw()方法(注意,而并不是有draw()方法的對象都可以被傳入和調(diào)用,相關(guān)問題可以對比C++09的concept)。
Array<IDraw>?drawList?=?{?new?Circle(),?new?Rect(),?new?Circle(),?new?Table(),?new?Rect(),?new?TextArea()?};
void?draw()
{
????foreach(IDraw?d?in?drawList?)
????{
????????d.draw();
????}
} 我用這段偽碼,明確地表示了這樣的一個新情況,Circle被畫到畫圖板中去了,Table和TextArea也可以被畫到上面去了。
依然是存有懷疑,難道Table派生自Shape就有問題嘛?難道一定要弄出一個IDraw,讓它看起來有道理才是合理的?
不是的。首先,如果IDraw接口沒有取代Shape類,我們也要承認(rèn)這樣幾個事情,Shape類還是帶著IDraw接口的含義,盡管我們不把它抽象出來。
而且,設(shè)計沒有對錯之分,是不是適應(yīng)需求才是最實際的評判標(biāo)準(zhǔn)。我們當(dāng)前的例子還很簡單,我們只涉及到了元素的繪制(draw),但是也許還有其他的問題 (新需求總是很多的),比如ALPHA混合,圖層的遮擋,元素的選擇,螞蟻線的繪制,等等。例如,當(dāng)我們選擇了一個Rect的時候,他的四周有螞蟻線,而我們選擇了一個TextAread的時候,它的四周是帶有8個調(diào)節(jié)點的邊框。可是,我們不打算讓螞蟻線和邊框參與其他元素的遮擋計算和ALPHA混合計算,而且它們也不參與序列化,于是,它們既不是Shape,也不應(yīng)該實現(xiàn)IDraw接口。經(jīng)驗告訴我們,Shape類是不足以成為所有元素的基類的,IDraw接口也不是萬能的。具體的解決方案要看需求,如何應(yīng)對這些需求,我們會在后面的內(nèi)容中有所涉及。
轉(zhuǎn)載于:https://www.cnblogs.com/healerkx/articles/1233252.html
總結(jié)
以上是生活随笔為你收集整理的面向对象理论(6)-Interface Programming-[A]的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 图片推理1
- 下一篇: ADO.NET与XML的结合