2010-05-29 36 views
12

Hiện đã có nhiều SO câu hỏi về lý do tại sao không có trừu tượng tĩnh phương pháp/lĩnh vực như vậy, nhưng tôi đang tự hỏi về cách người ta sẽ đi về thực hiện psuedo-mã sau:Phương pháp "Tóm tắt tĩnh" - làm cách nào?

class Animal { 
    abstract static int getNumberOfLegs(); // not possible 
} 

class Chicken inherits Animal { 
    static int getNumberOfLegs() { return 2; } 


class Dog inherits Animal { 
    static int getNumberOfLegs() { return 4; } 

Dưới đây là vấn đề Giả sử rằng tôi muốn đảm bảo rằng mọi lớp thừa hưởng Animal để chứa phương thức getNumberOfLegs() (ví dụ như một giao diện, ngoại trừ tôi muốn lớp trừu tượng triển khai một số phương thức chung cho tất cả các lớp con, do đó giao diện thuần túy không hoạt động ở đây). getNumberOfLegs() rõ ràng nên là một phương pháp tĩnh (giả sử rằng trong một thế giới hoàn hảo, chúng tôi không 'có tê liệt gà và chó vì vậy getNumberOfLegs không phải là trường hợp phụ thuộc).

Nếu không có phương pháp "trừu tượng tĩnh"/trường, người ta có thể rời phương thức ra khỏi lớp Animal, thì có nguy cơ một số lớp con không có phương pháp đó. Hoặc người ta có thể làm cho getNumberOfLegs một phương pháp thể hiện, nhưng sau đó người ta phải khởi tạo một lớp để tìm hiểu xem có bao nhiêu chân mà động vật có - mặc dù nó không cần thiết.

Làm thế nào để người ta thường thực hiện tình huống này?


EDIT: Ở đây cách tôi có thể sử dụng tính năng này. Giả sử (bây giờ điều này là vô lý, nhưng dù sao đi nữa ...) rằng số chân của mỗi con vật là duy nhất, vì vậy tôi có thể có một cái gì đó như:

Animal getModelAnimal(int numberOfLegs) { 
    if (numberOfLegs == Chicken.getNumberOfLegs()) return new Chicken(); 
    else if (numberOfLegs == Dog.getNumberOfLegs()) return new Dog(); 
} 

Trả lời

3

giả của bạn trông rất giống Java, vì vậy tôi giả sử nó là Java bạn đang sử dụng.

"Một phương pháp trừu tượng yêu cầu thực hiện mỗi thể hiện. Phương pháp tĩnh liên quan đến một lớp tổng thể. Một phương pháp tĩnh trong lớp trừu tượng thuộc về lớp trừu tượng, không triển khai tiềm năng. Do đó, không có ý nghĩa gì để cho phép tĩnh trừu tượng Hơn nữa, các phương thức tĩnh không thể bị ghi đè, vì vậy một lần nữa, các phương thức tĩnh trừu tượng sẽ là một sự bất thường. "

Từ http://forums.sun.com/thread.jspa?threadID=597378

hãy cũng nhìn vào Why can't I define a static method in a Java interface?

+0

-1: Bạn không thể nói điều đó nói chung. phương pháp trừu tượng không phải luôn luôn không có ý nghĩa (xem bài viết của tôi). – Simon

+0

Đã chỉnh sửa câu trả lời của tôi để phản ánh điều này. –

+0

Câu hỏi của tôi có nghĩa là không thuyết phục về ngôn ngữ (tôi đã gặp phải vấn đề tương tự trong C# trước đây). Tôi có thể thấy lý do tại sao không có công cụ sửa đổi "trừu tượng tĩnh" trong ngôn ngữ, nhưng tôi quan tâm nhiều hơn để xem cách người ta có thể thực hiện một cách rõ ràng vấn đề tôi đã nêu trong câu hỏi. – polyglot

0

Thậm chí nếu bạn có thể có một phương pháp tĩnh trừu tượng, làm thế nào bạn sẽ sử dụng nó? Hãy suy nghĩ về cách bạn sẽ sử dụng nó trước khi bạn nghĩ về cách thực hiện nó, như thực hiện của bạn phải phù hợp với việc sử dụng.

+0

@Downvoter: tại sao không cho mọi người biết vấn đề là gì với câu trả lời của tôi? –

3

Đây là điểm thực sự tốt và đôi khi abstract static thực sự bị thiếu. Tuy nhiên, vì ngày nay bộ nhớ không phải là vấn đề, bạn chắc chắn có thể triển khai phương thức getNumberLegs() -method như một phương thức thể hiện.

Giả sử rằng tóm tắt tĩnh là không có ý nghĩa, không đúng. PHP cho phép các phương thức tĩnh trừu tượng (xem this) và kịch bản của bạn cho thấy rằng nó có thể hữu ích trong một số trường hợp.

Cũng không đúng khi nói rằng không thể ghi đè phương thức static; Các phương thức final không thể ghi đè. Trong các ngôn ngữ như Java và C#, static đi kèm với final. Đó là lý do tại sao nhiều giả định rằng static bằng với "không thể ghi đè".

Nói về C# (sau khi đọc ý kiến ​​của bạn, tôi giả sử bạn "nói chuyện" C#), bạn có thể xem xét sử dụng Generics và các thuộc tính (hoặc Generics và chú thích trong Java):

public class Animal 
{ 
    public static int GetNumberOfLegs<T>() where T : Animal 
    { 
    //Get T's custom attribute "NumberOfLegs" and return its value 
    } 

    //EDIT: Added runtime-version of GetNumberOfLegs. 
    public static int GetNumberOfLegs(Type t) 
    { 
    //Get t's custom attribute "NumberOfLegs" and return its value 

    } 
} 

[NumberOfLegs(4)] 
public class Cat { ... }; 

Điều này sẽ cho phép bạn để có được số lượng chân của mỗi loại mà không cần khởi tạo nó. Chỉ cần nhớ để chỉ ra thuộc tính [NumberOfLegs(x)]. Bạn cũng phải biết loại tại thời gian biên dịch (đối với phiên bản chung của phương thức).

EDIT: Tôi đã thêm phiên bản thời gian chạy là GetNumberOfLegs() -method, để bạn có thể vượt qua đối tượng Type (phải là Class đối với Java). Bạn sẽ phải thực hiện kiểm tra kiểu khi chạy trong trường hợp này, tức là kiểm tra xem loại được thể hiện bằng Type -/Class -object được kế thừa từ Animal và sau đó truy lục giá trị được chuyển trong thuộc tính/chú thích.

Cách sử dụng:

int numberOfLegs1 = Animal.GetNumberOfLegs<Cat>(); 
int numberOfLegs2 = Animal.GetNumberOfLegs(typeof(Cat)); //runtime version 
+0

So sánh Java/C# với PHP giống như so sánh táo với cam .. – BalusC

+2

Nó không phải là một so sánh ... Tôi cho thấy tĩnh trừu tượng không phải là không có ý nghĩa. Tôi đang chỉ ra một khái niệm OOP chung. – Simon

+0

xin lỗi, nhưng tự nó, Tóm tắt tĩnh vẫn vô nghĩa. Bạn cần thuộc tính tùy chỉnh để hiểu rõ trường hợp sử dụng này. –

2

Làm thế nào để người ta thường đi về thực hiện tình huống này?

Trong thuật ngữ Java, tôi chỉ khai báo một hàm tạo trong lớp trừu tượng có tham số cố định. Mỗi lớp con sau đó được yêu cầu để gọi nó, nếu không nó sẽ không biên dịch.

abstract class Animal { 
    private int numberOfLegs; 

    public Animal(int numberOfLegs) { 
     this.numberOfLegs = numberOfLegs; 
    } 

    public int getNumberOfLegs() { 
     return numberOfLegs; 
    } 
} 

class Chicken extends Animal { 
    public Chicken() { 
     super(2); 
    } 
} 

class Dog extends Animal { 
    public Dog() { 
     super(4); 
    } 
} 

Cập nhật: theo cập nhật của bạn

EDIT: Dưới đây làm thế nào tôi có thể sử dụng này. Giả (bây giờ điều này là vô lý, nhưng dù sao đi nữa ...) rằng số lượng chân mỗi con vật là duy nhất, vì vậy tôi có thể có cái gì đó như:

Animal getModelAnimal(int numberOfLegs) { 
    if (numberOfLegs == Chicken.getNumberOfLegs()) return new Chicken(); 
    else if (numberOfLegs == Dog.getNumberOfLegs()) return new Dog(); 
} 

này thực sự là vô lý, điều này đòi hỏi rằng tất cả những động vật cụ thể được biết trước trong phương pháp nhà máy trừu tượng. Bạn sẽ cần phải cập nhật phương pháp trừu tượng của nhà máy mỗi khi một loại động vật bê tông mới được thêm vào. Điểm của nhà máy trừu tượng là gì? Bạn đã biết tất cả mọi thứ trước? Không, chỉ cần để phương thức nhà máy trừu tượng lấy tên lớp đầy đủ đủ điều kiện làm định danh hoặc vì vậy, để nó có thể thử tải từ classpath (vẫn đang nói trong các thuật ngữ Java).

+0

Nhưng 'getNumberOfLegs()' vẫn là một phương thức thể hiện và tôi phải khởi tạo các lớp để tìm hiểu số lượng chân của con vật ... – polyglot

+0

Xem cập nhật câu trả lời của tôi. – BalusC

+0

Phương thức 'getModelAnimal()' không nhất thiết phải được định nghĩa trong lớp Animal. – polyglot

0

Đây là một câu hỏi thú vị. Theo tôi, các phương pháp "trừu tượng tĩnh" hiếm khi cần thiết, và luôn có những lựa chọn thay thế tốt.

Trong trường hợp sử dụng mà bạn đã cung cấp, ví dụ, phương pháp nhà máy đề cập đến các lớp động vật cụ thể theo tên; đối với mỗi lớp động vật mới, nên thêm mã cụ thể mới. Do đó, có vẻ như trình độ "trừu tượng" không thực sự cần thiết. Quy ước cung cấp phương thức tĩnh getNumberLegs() là đủ.

Và tổng quát hơn, kết hợp trừu tượng và tĩnh không có ý nghĩa (trong Java) từ trừu tượng nghĩa đa hình, trong khi tĩnh cuộc gọi đều là phòng không đa hình ở tất cả, và làm việc trên lớp được biết đến tại thời gian biên dịch.

4

Làm thế nào để thường đi về triển khai tình huống này?

Giải pháp thông thường là làm cho phương pháp được đề cập đến một phương pháp thể hiện.

getNumberOfLegs() rõ ràng nên một phương pháp tĩnh (giả định rằng trong một thế giới hoàn hảo chúng tôi không' đã làm tê liệt gà và chó để getNumberOfLegs là không dụ phụ thuộc).

Đó là nhấn mạnh không hiển nhiên! Chúng tôi không lập trình cho một thế giới hoàn hảo, và trong thế giới thực bốn chân động vật đôi khi có một, hai, hoặc ba (hoặc năm) chân.

Nếu chương trình của bạn cần động vật định nghĩa chứ không phải là động vật trường, đi trước và tạo ra một lớp cho rằng.

class AnimalDefinition { 
    public string getScientificName(); 
    public string getCommonName(); 
    public int getNumberOfLegs(); 
    public bool getIsAmphibious(); 
    // etc. 
} 

Sau đó, khởi tạo bộ sưu tập ở đầu chương trình - lý tưởng từ cơ sở dữ liệu hoặc tệp cấu hình mà bạn có thể thêm hoặc biên dịch một dòng mã khác. (Và bạn có thể nhận được ngay với loại rất ít.)

0

Bạn có thể cludge một runtime kiểm tra rằng phương pháp này được thực hiện bằng việc có lớp cơ sở của bạn ném một ngoại lệ trong việc thực hiện của nó. Nó không có giá trị nhiều, nhưng có lẽ tốt hơn không có gì ...

0

Phương pháp trừu tượng có ý nghĩa nếu bạn gọi chúng là trợ giúp của lớp cơ sở. Lưu ý rằng trong ví dụ của bạn, bạn không sử dụng tính đa hình. Trong ví dụ nên một cái gì đó như:

(Animal)Dog.getNumberOfLegs() //cast Dog to Animal first 

Dù sao PHP thực hiện cái gọi là "cuối tĩnh ràng buộc" mà có lẽ những gì bạn đang tìm kiếm

http://php.net/manual/en/language.oop5.late-static-bindings.php

trong chức năng tương tự C++ có thể đạt được bằng tamplates và tính đa hình thời gian biên dịch. Các phương pháp

0

abstract static chỉ có ý nghĩa trong các ngôn ngữ mà trong đó các biến có thể chứa các loại thực tế chứ không chỉ các trường hợp. (Delphi là một ngôn ngữ như vậy, C# là không, và tôi không nghĩ rằng bạn có thể làm điều đó trong Java hoặc). Lý do tại sao nếu bạn biết tại thời gian biên dịch chính xác các lớp bạn đang sử dụng (như trong ví dụ của bạn), thì không có lý do nào cho phương thức là abstract, bạn chỉ có thể có các phương thức static trong mỗi lớp có cùng tên nhiều thứ. Cách duy nhất mà bạn có thể không biết bạn đang sử dụng kiểu nào là nếu bạn có thể gán kiểu cho biến, khi đó bạn có thể truyền cho chúng xung quanh (giống như trường hợp lớp) và đột nhiên mọi thứ thực sự có ý nghĩa và hữu ích.Tôi nghĩ hầu hết các trình biên dịch/ngôn ngữ hỗ trợ các kiểu gán (cũng như các thể loại) cho các biến cũng quản lý các phương thức abstract staticvirtual abstract thông qua phép thuật biên dịch, vì vậy nếu chúng thực sự hữu ích trong ngôn ngữ bạn chọn thì chúng nên được hỗ trợ.

+0

Không chỉ các ngôn ngữ có các loại biến. Một trường hợp khác là khi phương thức tĩnh trừu tượng như vậy được gọi từ phương thức khác. Giả sử một trong các phương thức Animal gọi 'Animal.getNumberOfLegs()'. Sau đó, Dog có thể gọi một trong những phương pháp của nó phương pháp này. Vì 'getNumberOfLegs()' là trừu tượng 'Animal.getNumberOfLegs()' bên trong phần thân phương thức của Animal, có thể được gửi đến 'Dog.getNumberOfLegs()'. – doc

+0

Ví dụ đó không có ý nghĩa. Trình biên dịch phải biết gửi một cuộc gọi tĩnh đến một kiểu khác dựa trên chữ ký của cuộc gọi không tĩnh mà bạn đang ở trong đó như thế nào? Đó không phải là cách những thứ này hoạt động. – Donnie

+0

Tôi nghĩ rằng vtable bổ sung cho các cuộc gọi tĩnh nên thực hiện công việc. Dù sao, nó chỉ phụ thuộc vào RTTI. PHP làm một cái gì đó như thế (mặc dù không chính xác). – doc

0

một cách tiếp cận khác mà tôi có thể thấy ở đây là tạo một nhà máy trừu tượng: đây là C# bạn không cần phải tạo một cá thể Gà để biết một số chân. chỉ gọi phương thức nhà máy checken

abstract class AnimalFactory 
{ 
    public abstract Animal CreateAnimal(); 
    public abstract int GetLegs(); 

} 
abstract class Animal 
{ 

} 
internal class Chicken : Animal 
{ 

} 
class CreateChicken : AnimalFactory 
{ 
    public override Animal CreateAnimal() 
    { 
     return new Chicken(); 
    } 
    public override int GetLegs() 
    { 
     return 2; 
    } 

} 
0

Trong một khoảnh khắc, giả "phương pháp tĩnh trừu tượng" được phép.

Sau đó sử dụng mã của bạn, tôi thêm này:

Animal modelAnimal; 
int numLegs; 

modelAnimal = this.getModelAnimal(4); // Got a dog 

numLegs = modelAnimal.getNumberOfLegs(); 

tôi sẽ nhận được lỗi như modelAnimal mà là một Chó đối tượng sẽ cố gắng gọi getNumberOfLegs trong Animal lớp và không Chó lớp học. Không ghi đè các phương pháp tĩnh mà bạn biết. Để tránh tình trạng này, các nhà thiết kế chưa bao gồm các phương pháp tĩnh trừu tượng.

Các vấn đề liên quan