2011-08-12 45 views
9

Nếu tôi có dự án có chứa các lớp tương tự và một số có thể sử dụng cùng một triển khai, nhưng trong hầu hết các trường hợp, chúng thực hiện cách xử lý các phương thức được xác định trong giao diện hoặc lớp trừu tượng. Tôi đang cố gắng tìm ra nếu một giao diện/lớp trừu tượng là tốt hơn hay không. Tôi không nhận được điểm của một giao diện nếu bạn chỉ có thể sử dụng một lớp trừu tượng với các phương thức trừu tượng ảo.Giao diện và lớp trừu tượng chỉ với các phương thức trừu tượng ảo có giống nhau không?

Dưới đây là một giao diện:

public interface IAthlete 
{ 
    void Run(); 
} 

Dưới đây là một lớp trừu tượng:

public abstract class Athlete 
{ 
    public abstract void Run(); 
} 

Đây là một thực hiện của giao diện:

public class Sprinter : IAthlete 
{ 
    public void Run() 
    { 
     Console.WriteLine("Running Fast...."); 
    } 
} 

Đây là một phần mở rộng của lớp trừu tượng:

public class MarathonRunner : Athlete 
{ 
    public override void Run() 
    { 
     Console.Write("Jogging...."); 
    } 
} 

Bây giờ nếu tôi quyết định thêm một phương thức gọi là Dừng hoặc giao diện hoặc phương pháp trừu tượng, Sprinter và MarathonRunner đều bị ngắt và vì tôi có thể cung cấp một số triển khai mặc định để trừu tượng, nó có vẻ là một lựa chọn tốt hơn. Tui bỏ lỡ điều gì vậy?

+3

Tôi cho rằng chúng ta đang nói về Java ở đây. Sự khác biệt chính là trong Java, một lớp có thể thực hiện một số giao diện, nhưng chỉ có thể mở rộng một siêu lớp. Giao diện do đó linh hoạt hơn một chút. –

+0

xin lỗi C#, tôi sẽ cập nhật thẻ. – Xaisoft

+0

http://stackoverflow.com/questions/2308786/abstract-classes-and-interfaces-in-c/2308801#2308801 –

Trả lời

6

Có 2 sự khác biệt chính giữa giao diện và trừu tượng siêu lớp:

Tóm tắt Lớp học

  • tái sử dụng mã là có thể bằng cách sử dụng một bản tóm tắt super-class
  • bạn chỉ có thể kế thừa một siêu lớp

Giao diện

  • mọi phương pháp này phải được thực hiện trong mỗi lớp phụ
  • một lớp có thể kế thừa nhiều hơn 1 giao diện (đa kế thừa)
+0

Nếu bạn khai báo một lớp trừu tượng với tất cả các phương thức trừu tượng, không phải lớp mở rộng lớp trừu tượng phải thực hiện tất cả các phương thức giống như một giao diện, vì vậy đây là những gì tôi đang bối rối. – Xaisoft

+1

+1 - Mặc dù, về mặt kỹ thuật bạn có thể thêm 'triển khai' vào các giao diện với các phương pháp mở rộng. = D – Tejs

+2

@Xaisoft - Có, lớp dẫn xuất sẽ cần ghi đè lên tất cả các phương thức trừu tượng. Hãy xem xét một lớp trừu tượng như là một superset của chức năng giao diện mà không có khả năng sử dụng nhiều thừa kế. – Tejs

-1

Giao diện là một cách btter đi như sự đồng thuận hiện tại giữa các NET Phát triển comunity là bạn nên ưu tiên thành phần trên thừa kế, do đó, giao diện là một chiến lược tốt hơn nhiều (nghĩ về Injection Containers và làm thế nào hữu ích họ là tốt, và cho phép không bắt đầu thử nghiệm đơn vị). cũng có, các lớp có thể thực hiện nhiều giao diện nhưng chỉ có thể kế thừa từ một lớp (trừu tượng hoặc cách khác). các cấu trúc cũng có thể thực hiện các giao diện nhưng không kế thừa từ một lớp khác. Ở cấp độ thời gian chạy, các giao diện hiệu quả hơn vì thời gian chạy không phải đi bộ ngăn xếp kế thừa để tìm ra các hàm ý đa hình của việc gọi một thành viên cụ thể.

+0

Câu trả lời của bạn là rambling – Atreys

1

Trong trường hợp tất cả những gì bạn có là phần phổ biến để trích xuất, bạn hoàn toàn đúng là không có sự khác biệt đáng kể giữa hai loại. Nhưng điều này giống như nói "trong trường hợp thêm 1 vào 2, không có sự khác biệt giữa một số intdouble" - đó là về mặt kỹ thuật đúng, nhưng không phải là hướng dẫn đặc biệt hữu ích về cách suy nghĩ.

Trong trường hợp có độ phức tạp hơn mức này (nghĩa là, trong hầu hết các trường hợp) sẽ có nhiều lớp hơn và các phần baheaviour phổ biến để trích xuất. Sau đó, bạn phải bắt đầu thực hiện một ý nghĩa lựa chọn giữa thừa kế lớp và thực hiện giao diện, có tính đến điều khoản như:

  • bạn chỉ nhận được một shot tại lựa chọn một lớp cơ sở, nhưng bạn có thể thực hiện nhiều giao diện như bạn như
  • nếu bạn muốn 'mẹ' của bạn để làm bất cứ công việc, nó cần phải được một lớp học không phải là một giao diện

và vân vân.

Nói chung, các ngữ nghĩa ngữ nghĩa trong lớp học của bạn sẽ hướng dẫn bạn - nơi 'những thứ' có mối quan hệ "IS A" (như MarathonRunner đến Vận động viên), kế thừa được đề xuất; trong đó 'những thứ' có một "Tôi CÓ THỂ THỰC HIỆN HỢP ĐỒNG CỦA MỘT" (ví dụ, Person to Runner), thực hiện giao diện được đề xuất.

0

Giao diện là một tính năng rất hữu ích, và rất giống với lớp trừu tượng, và trong một số trường hợp, exchangable với lớp trừu tượng.

Tuy nhiên, đừng nhảy thẳng đến giao diện, hãy bỏ phiếu bạn phải (antipattern rất phổ biến trong các nhà phát triển Java). Tôi đề nghị, bằng cách đọc ví dụ của bạn, để dính vào các lớp trừu tượng.

Hầu hết các lần tôi chỉ sử dụng giao diện, khi tôi có một số lớp không liên quan và tôi cần chúng để có các thành viên chung, như Nếu các lớp này đến từ cùng một lớp cơ sở.

Trong ví dụ của bạn, bạn đang cố gắng tìm điều gì xảy ra nếu bạn cần phương thức stop mới, khi thêm phương thức ảo cơ bản. Đây có thể được giải quyết theo một cách tiếp cận khác, đó không phải là Lớp trừu tượng so với giao diện.

Có 3 lựa chọn:

(1) Thêm một phương pháp trừu tượng mà ép buộc các lập trình viên để ghi đè lên nó, để nhanh chóng đối tượng.

(2) Thêm một phương pháp ảo mới thực hiện điều gì đó, nhưng không cần phải được ghi đè lên.

(3) Thêm phương pháp mới không thực hiện được gì, có thể áp dụng cho trường hợp của bạn.

// cannot instantiate an abstract class 
public abstract class Athlete 
{ 
    // helper method: 
    public /* non-abstract */ void DoNothing() 
    { 
     // does nothing on purpouse !!! 
    } 

    // (1) virtual & abstract method, must be overriden 
    public abstract void Run(); 


    // (2) new virtual method, doesn't need to be overriden, 
    // but, maybe you dont like what it does 
    public virtual void Stop() 
    { 
     Message.Show("Stop !!!"); 
    } 

    // (3) new virtual method, doesn't need to be overriden, 
    // its safe to be called 
    public virtual void TakeBreak() 
    { 
     // works like an abstract virtual method, but, you don't need to override 
     DoNothing(); 
    } 
} // class Athlete 

// in a non abstract class, you must override all abstract methods 
public /* non-abstract */ class Runner: Athlete 
{ 
    public override void Run() 
    { 
     DoNothing(); 
    } 

    public override void Stop() 
    { 
     DoNothing(); 
    } 

    // don't need to override this method 
    // public virtual void TakeBreak(); 

} // class Trekker 

// ... 

Runner ARunner = new Runner(); 
ARunner.Run(); 
ARunner.Stop(); 
ARunner.TakeBreak(); 

Loại thứ ba của phương pháp ảo, có thể áp dụng đối với ví dụ của bạn, doesnt' có một cái tên đặc biệt, tôi đã gửi một câu hỏi về nó trên stackoverflow, nhưng, không ai biết một cái tên đặc biệt cho trường hợp này.

Chúc mừng.

0

Sự khác biệt quan trọng giữa giao diện và lớp trừu tượng là cách các thành viên của họ xử lý thừa kế đa thế hệ.Giả sử có một lớp trừu tượng BaseFoo với phương thức trừu tượng Bar và giao diện IFoo với phương thức Boz; lớp Foo được thừa kế BaseFoo và triển khai IFoo và lớp DerivedFoo được kế thừa từ Foo.

Nếu DerivedFoo cần ghi đè BaseFoo.Bar, có thể làm như vậy và ghi đè có thể gọi base.Bar() nếu cần sử dụng triển khai của cha mẹ. Nếu Foo cụ Boz ngầm sử dụng một phương pháp ảo, sau đó DerivedFoo có thể ghi đè phương pháp đó và gọi base.Boz() (ghi đè là một chức năng của lớp chứ không phải giao diện) nhưng nếu Foo thực hiện một cách rõ ràng IFoo.Boz, thì cách duy nhất cho DerivedFoo để thay đổi hành vi của IFoo.Boz sẽ phải triển khai lại. Nếu có, thì việc thực hiện Foo của IFoo.Boz sẽ không thể truy cập được, ngay cả trong việc thực hiện DerivedFoo của cùng một thành viên giao diện.

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