2011-11-01 42 views
0

所以我做了這個簡單的接口:如何創建一個接受同一個函數名稱的不同參數的子類?

package{ 
    public interface GraphADT{ 
     function addNode(newNode:Node):Boolean;  
    } 
} 

我還創建了一個簡單的類圖:

package{ 

    public class Graph implements GraphADT{ 

     protected var nodes:LinkedList; 

     public function Graph(){ 
      nodes = new LinkedList(); 
     } 

     public function addNode (newNode:Node):Boolean{ 
      return nodes.add(newNode); 
     } 
} 

最後但並非最不重要的我創造了另一種簡單的類AdjacancyListGraph:

package{ 
    public class AdjacancyListGraph extends Graph{ 

     public function AdjacancyListGraph(){ 
      super(); 
     } 

     override public function addNode(newNode:AwareNode):Boolean{ 
      return nodes.add(newNode); 
     } 
} 

這裏有這個設置給我錯誤,即:

1144: Interface method addNode in namespace GraphADT is implemented with an incompatible signature in class AdjacancyListGraph. 

經仔細檢查很顯然,AS3不喜歡從圖不同的圖類newNode:Node不同的參數類型,以及newNode:AwareNode從AdjacancyListGraph

但是我不明白爲什麼這樣做是因爲一個問題AwareNodeNode的子類。

有什麼辦法可以讓我的代碼工作,同時保持代碼的完整性?

+0

您正在重寫addNode函數來接受AwareNode,它應該可以用於擴展類,但現在接口還需要遵循基類節點的擴展。至少這是它對我的看法。我避免了像鼠疫這樣的界面。 –

+2

@The_asMan,讓我難過。只要你明白它是什麼,接口就是解耦的偉大之處。它的*合約*無論你的對象做什麼,它都會做這些事情。 –

+0

+1獲取接口的用途,@ 32bitkid。實現可重用和可維護架構的唯一方法。而單元測試絕對必要。 – weltraumpirat

回答

3

答案很簡單

如果你不真的需要你的「ADDNODE()」函數只接受AwareNode,您可以將參數類型更改爲Node。由於AwareNode擴展了Node,因此您可以毫無問題地傳入AwareNode。你可以在函數體內檢查對錯:

subclass... { 
    override public function addNode (node:Node) : Boolean { 
     if (node is AwareNode) return nodes.add(node); 
     return false; 
    } 
} 

再回應

我@ 32bitkid同意,您收到錯誤,因爲參數類型ADDNODE()中定義的接口不同於你的子類中的類型。

但是,主要問題在於ActionScript通常不允許重載函數(具有多個同名的方法,但具有不同的參數或返回值),因爲每個函數都被視爲泛型類成員 - 變量的相同方式。你可以這樣調用一個函數:

myClass.addNode (node); 

但你可能也這樣稱呼它:

myClass["addNode"](node); 

每個成員都用名稱保存 - 你可以隨時使用該名稱來訪問它。不幸的是,這意味着只允許在班級中使用每個功能名稱,而不管它使用哪種類型的參數 - 沒有任何價格沒有:您在一個方面獲得靈活性,在另一個方面會失去一些安慰。

因此,您只能使用完全相同的簽名覆蓋方法 - 這是一種讓您堅持您在編寫基類時所做決定的方法。雖然你明顯可能認爲這是一個壞主意,並且使用重載或在子類中允許不同的簽名更有意義,但AS處理函數的方式有一些優點,最終可以幫助您解決問題:您可以使用類型檢查功能,甚至可以通過一個參數!

考慮一下:

class... { 

    protected function check (node:Node) : Boolean { 
     return node is Node; 
    }  

    public function addNode (node:Node) : Boolean { 
     if (check(node)) return nodes.add(node); 
     return false; 
    } 
} 

在這個例子中,你可以覆蓋檢查(節點:節點):

subclass... { 
    override protected function check (node:Node) : Boolean { 
     return node is AwareNode; 
    } 
} 

,並達到你所期望的完全一樣的效果,而不會破壞界面合同 - 除了在你的例子中,如果你傳入了錯誤的類型,編譯器會拋出一個錯誤,而在這個錯誤中,錯誤只會在運行時顯示(返回值爲false)。

您也可以讓這個更動態:

class... { 
    public function addNode (node:Node, check : Function) : Boolean { 
     if (check(node)) return nodes.add(node); 
     return false; 
    } 
} 

注意,這ADDNODE函數接受一個函數作爲參數,而我們調用該函數,而不是一個類的方法:

var f:Function = function (node:Node) : Boolean { 
    return node is AwareNode; 
} 

addNode (node, f); 

這可以讓你在實現時變得非常靈活 - 甚至可以在匿名函數中進行合理性檢查,例如驗證節點的內容。你甚至不需要擴展你的類,除非你打算添加其他功能,而不僅僅是輸入正確性。

擁有一個接口也可以讓你創建不從原始基類繼承的實現 - 你可以編寫一個完全不同的類層次結構,它只需要實現接口,並且你以前的代碼將保持有效。

+0

+1爲annon功能的想法。但是對於GC來說有多難? –

+0

不是。你把這個函數當作一個變量來處理:如果它需要重用,那麼把它變成一個類成員('private var f:Function'(!))。如果設置爲「null」,它將被GC'ed。如果沒有,它會像其他任何臨時變量一樣在使用後被丟棄。 – weltraumpirat

2

我想問題是這樣的:你想完成什麼?

至於爲什麼你得到一個錯誤,可以這樣考慮:

public class AnotherNode extends Node { } 

然後:

var alGraph:AdjacancyListGraph = new AdjacancyListGraph(); 

alGraph.addNode(new AnotherNode()); 
// Wont work. AnotherNode isn't compatable with the signature 
// for addNode(node:AwareNode) 

// but what about the contract? 

var igraphADT:GraphADT = GraphADT(alGraph); 
igraphADT.addNode(new AnotherNode()); // WTF? 

根據接口這應該是罰款。但是你的實現說不然,你的實現說它只會接受AwareNode。有明顯的不匹配。如果你打算有一個接口,一個你的對象應該遵循的合約,那麼你也可以遵循它。否則,首先界面的要點是什麼。

如果您正在嘗試執行此操作,那麼我會將該體系結構搞糟。即使語言都支持的話,我會說,它是一個「壞主意™」

+0

這不是我在做的事情。 我只是想要問一個'AwareNode'而不是一般'Node'。對我來說應該沒問題,因爲'AwareNode'會將所有必需的方法用作Node(從界面的角度來看)。我對嗎? – vvMINOvv

+0

@vvMINOvv,但你可以。這纔是重點。這就是編譯器抱怨的原因。因爲它沒有意義。 –

+0

我想我所說的是,由於'AwareNode'將具有'Node'中的所有方法,如果我將'AwareNode'傳入'Graph',它應該沒有問題,因爲所有它的「習慣」方法在那裏。這是我對接口有用的理解,如果我錯了,請糾正我的錯誤:D – vvMINOvv

1

有一個更簡單的方法,那麼上述建議,但不太安全:

public class Parent { 
public function get foo():Function { return this._foo; } 
protected var _foo:Function = function(node:Node):void { ... }} 

public class Child extends Parent { 
public function Child() { 
super(); 
this._foo = function(node:AnotherNode):void { ... }}} 

當然_foo需求不到位的聲明中,使用的語法僅用於急促和示範的目的。 您將失去編譯器檢查類型的能力,但運行時類型匹配仍然適用。

另一種方式去了解它 - 不要在他們專門在類聲明的方法,而不是讓他們靜態的,那麼你就不會自動繼承他們:

public class Parent { 
public static function foo(parent:Parent, node:Node):Function { ... }} 

public class Child extends Parent { 
public static function foo(parent:Child, node:Node):Function { ... }} 

注意,在第二種情況下保護字段可以在靜態方法內部訪問,所以你可以實現一定的封裝。另外,如果你有很多Parent或者Child實例,你可以節省單個實例的內存空間(因爲靜態方法因此是靜態的,只存在它們的一個副本,但是每個實例都會複製實例方法)。缺點是你將無法使用接口(實際上可以改進......取決於你的個人喜好)。

+0

有趣的想法 – vvMINOvv

相關問題