Tại sao tôi nên khai báo một lớp là một lớp trừu tượng?


40

Tôi biết cú pháp, quy tắc áp dụng cho lớp trừu tượng và tôi muốn biết cách sử dụng lớp trừu tượng

Lớp trừu tượng không thể được khởi tạo trực tiếp nhưng có thể được mở rộng bởi lớp khác

Lợi thế của việc làm như vậy là gì?

Nó khác với Giao diện như thế nào?

Tôi biết rằng một lớp có thể thực hiện nhiều giao diện nhưng chỉ có thể mở rộng một lớp trừu tượng. Có phải đó chỉ là sự khác biệt giữa một giao diện và một lớp trừu tượng?

Tôi biết về việc sử dụng Giao diện. Tôi đã học được điều đó từ mô hình ủy nhiệm sự kiện của AWT trong Java.

Trong tình huống nào tôi nên khai báo lớp là một lớp trừu tượng? Lợi ích của việc đó là gì?


14
Điều này đã được hỏi, bạn biết. Một tìm kiếm sẽ bật lên các câu hỏi khác như câu hỏi này. Bạn nên bắt đầu trên Google, điều này sẽ dẫn bạn đến Stack Overflow. Tất cả những thứ này là trùng lặp: stackoverflow.com/search?q=abab+interface .
S.Lott

1
"Việc sử dụng lớp Trừu tượng họ chỉ thảo luận về ... quy tắc". Gì? Có gì khác nhau giữa "quy tắc" và "cách sử dụng"? Bằng cách này, câu hỏi chính xác của bạn đã được hỏi. Tôi biết. Tôi đã trả lời nó. Cứ nhìn. Điều quan trọng là học cách sử dụng tìm kiếm.
S.Lott

Ý tôi là "Trong tình huống nào tôi nên khai báo lớp là lớp trừu tượng? Lợi ích của việc đó là gì?"
Vaibhav Jani

Một giao diện là một lớp trừu tượng thuần túy, đó là tất cả. Họ là một trong cùng một. Tôi sử dụng lớp trừu tượng mọi lúc cho một hàm trong lớp cơ sở không thể thực hiện được vì nó cần dữ liệu mà chỉ các lớp con có, nhưng tôi muốn đảm bảo rằng mọi lớp con đều có hàm này và thực hiện nó theo.
AngryBird

Tôi thấy mẫu phương thức mẫu rất mạnh và trường hợp sử dụng tốt cho các lớp trừu tượng.
m3th0dman

Câu trả lời:


49

Đây câu trả lời làm một công việc tốt trong việc giải thích sự khác biệt giữa một lớp trừu tượng và một giao diện, nhưng nó không trả lời lý do tại sao bạn nên tuyên bố một.

Từ quan điểm thuần túy kỹ thuật, không bao giờ có yêu cầu khai báo một lớp là trừu tượng.

Hãy xem xét ba lớp sau:

class Database { 
    public String[] getTableNames() { return null; } //or throw an exception? who knows...
}

class SqlDatabase extends Database { } //TODO: override getTableNames

class OracleDatabase extends Database { }  //TODO: override getTableNames

Bạn không cần phải làm cho lớp Cơ sở dữ liệu trừu tượng, mặc dù có một vấn đề rõ ràng với việc triển khai: Khi bạn đang viết chương trình này, bạn có thể nhập new Database()và nó sẽ hợp lệ, nhưng nó sẽ không bao giờ hoạt động.

Bất kể, bạn vẫn sẽ có được đa hình, miễn là chương trình của bạn chỉ thực hiện SqlDatabasevà các OracleDatabasethể hiện, bạn có thể viết các phương thức như:

public void printTableNames(Database database) {
    String[] names = database.getTableNames();
}

Các lớp trừu tượng cải thiện tình hình bằng cách ngăn nhà phát triển khởi tạo lớp cơ sở, bởi vì nhà phát triển đã đánh dấu nó là thiếu chức năng . Nó cũng cung cấp sự an toàn trong thời gian biên dịch để bạn có thể đảm bảo rằng bất kỳ lớp nào mở rộng lớp trừu tượng của bạn đều cung cấp chức năng tối thiểu để hoạt động và bạn không cần phải lo lắng về việc đặt các phương thức sơ khai (như cách trên) mà người thừa kế bằng cách nào đó có để biết một cách kỳ diệu rằng họ phải ghi đè một phương thức để làm cho nó hoạt động.

Giao diện là một chủ đề hoàn toàn riêng biệt. Một giao diện cho phép bạn mô tả những hoạt động có thể được thực hiện trên một đối tượng. Thông thường bạn sẽ sử dụng các giao diện khi viết phương thức, thành phần, v.v ... sử dụng dịch vụ của các thành phần, đối tượng khác, nhưng bạn không quan tâm loại đối tượng thực tế mà bạn nhận được dịch vụ là gì.

Hãy xem xét phương pháp sau:

public void saveToDatabase(IProductDatabase database) {
     database.addProduct(this.getName(), this.getPrice());
}

Bạn không quan tâm đến việc databaseđối tượng có kế thừa từ bất kỳ đối tượng cụ thể nào không, bạn chỉ quan tâm rằng nó có một addProductphương thức. Vì vậy, trong trường hợp này, một giao diện phù hợp hơn là làm cho tất cả các lớp của bạn xảy ra kế thừa từ cùng một lớp cơ sở.

Đôi khi sự kết hợp của hai công việc rất độc đáo. Ví dụ:

abstract class RemoteDatabase implements IProductDatabase { 
    public abstract String[] connect();
    public abstract void writeRow(string col1, string col2);

    public void addProduct(String name, Double price) {
        connect();
        writeRow(name, price.toString());
    }
}

class SqlDatabase extends RemoteDatabase {
    //TODO override connect and writeRow
}

class OracleDatabase extends RemoteDatabase { 
    //TODO override connect and writeRow
}

class FileDatabase implements IProductDatabase {
    public void addProduct(String name, Double price) {
         //TODO: just write to file
    }
}

Lưu ý cách một số cơ sở dữ liệu kế thừa từ RemoteDatabase để chia sẻ một số chức năng (như kết nối trước khi viết một hàng), nhưng FileDatabase là lớp riêng biệt chỉ thực hiện IProductDatabase.


16

Điểm tương đồng

Các lớp và giao diện trừu tượng là cần thiết cho sự trừu tượng. Chúng không thể được khởi tạo bằng một cái mới , nhưng có thể được giải quyết để đảo ngược các thùng chứa điều khiển hoặc thông qua các mẫu của nhà máy.

Sự khác biệt

  1. Giao diện

    • Xác định hợp đồng công chúng nổi tiếng, khả năng của các loại
    • Áp dụng để hiển thị kế thừa theo chiều ngang, tức là phân nhánh ở cấp kế thừa đầu tiên (ví dụ ILog để xác định phương tiện ghi nhật ký cho cơ sở dữ liệu, tệp văn bản, XML, SOAP, v.v.)
    • Tất cả các thành viên là công khai
    • Không cho phép thực hiện
    • Con kế thừa có thể có nhiều giao diện để thực hiện
    • Hữu ích cho việc tích hợp bên thứ ba
    • Đặt tên thường bắt đầu bằng tôi
  2. Lớp trừu tượng

    • Xác định cấu trúc, danh tính và một số hành vi được hỗ trợ mặc định
    • Áp dụng để hiển thị kế thừa theo chiều dọc, tức là phân nhánh sâu trên nhiều cấp độ (ví dụ: lớp AbstractEntity trong phát triển theo hướng tên miền)
    • Thành viên có thể có tầm nhìn khác nhau (từ công khai đến riêng tư)
    • Bạn có thể triển khai một số thành viên (ví dụ: lớp Reader)
    • Con kế thừa có thể chỉ có một lớp trừu tượng cơ sở

Thật sự dễ dàng để tìm câu trả lời bằng truy vấn google đơn giản .


những gì bạn mena bằng cách thực hiện không được phép? lớp java có thể thực hiện các giao diện.
Sajuuk

@Sajuuk dòng đó đề cập đến một giao diện. Bạn không thể đặt việc thực hiện hợp đồng vào một giao diện. Bạn có thể có mặc định thực hiện hợp đồng trong một lớp trừu tượng.
oleksii

11

Nó khác với Giao diện như thế nào?

Trong một lớp trừu tượng, bạn có thể thực hiện một số phương thức và để lại (bắt buộc) phần còn lại sẽ được thực hiện bởi lớp mở rộng. Bạn không thể thực hiện các phương thức trong một giao diện. Bạn không thể ép buộc bất cứ ai ghi đè bất cứ điều gì khi mở rộng một lớp học bình thường. Với một lớp trừu tượng, bạn có thể.


Điều này thực sự cần một bản cập nhật cho Java 8, nơi các phương thức mặc định đã được giới thiệu.
Haakon Løtveit

8

Các lớp trừu tượng dành cho "là một" mối quan hệ và giao diện dành cho "có thể làm".

Các lớp trừu tượng cho phép bạn thêm hành vi cơ sở để lập trình viên không phải mã hóa mọi thứ, trong khi vẫn buộc họ phải tuân theo thiết kế của bạn.


3

Bên cạnh các chi tiết kỹ thuật sâu sắc - như triển khai một số phương thức cho các lớp trừu tượng, v.v., ý nghĩa là như sau:

Các giao diện xác định khả năng chung - IEnumerable định nghĩa rằng lớp thực hiện giao diện này có thể được liệt kê. Nó không nói gì về bản thân lớp học.

Các lớp trừu tượng (hoặc cơ sở) xác định hành vi - WebRequest định nghĩa một hành vi chung của tất cả các lớp con như HttpWebRequest, v.v. Nó xác định ý nghĩa cốt lõi của lớp và mục đích thực sự của nó - để truy cập tài nguyên web.


2

Wikipedia mục .

Sự khác biệt chính giữa một giao diện và một lớp trừu tượng là một lớp trừu tượng có thể cung cấp các phương thức được thực hiện. Với các giao diện bạn chỉ có thể khai báo các phương thức, viết chữ ký của chúng. Dưới đây là một ví dụ về một lớp mở rộng một lớp trừu tượng thực hiện hai giao diện: (java)

interface MyInterface1 {
  string getValue1();
}

interface MyInterface2 {
  string getValue2();
}

abstract class MyAbstractClass implements MyInterface1, MyInterface2{
  void printValues() {
    System.out.println("Value 1: " + getValue1() + ", Value 2: " + getValue2() + 
                       ", Value 3: " + getValue3());
  }

  protected abstract string getValue3();
}

class ImpClass extends MyAbstractClass {
  public string getValue1() {
    return "1";
  }

  public string getValue2() {
    return "2";
  }

  protected string getValue3() {
    return "3";
  }
}

Trong ví dụ này, MyAbTHERClass cung cấp một phương thức công khai in cả ba giá trị. Trong ImpClass, bạn cần triển khai getValue1 và getValue2 tương ứng từ MyInterface1MyInterface2 và getValue3 từ lớp trừu tượng.

Võngà.

Có nhiều khía cạnh hơn (giao diện: chỉ các phương thức công khai, lớp trừu tượng: các phương thức trừu tượng công khai và trừu tượng công khai) nhưng bạn có thể tự đọc nó.

Trên một lưu ý cuối cùng, một lớp trừu tượng chỉ cung cấp các phương thức trừu tượng là một lớp cơ sở trừu tượng "thuần túy", hay còn gọi là một giao diện.


2
  • Giao diện - khi một vài lớp chia sẻ API (tên phương thức và tham số)
  • Lớp trừu tượng - khi một vài lớp chia sẻ cùng một mã (thực hiện)

Nói cách khác, bạn nên bắt đầu bằng một câu hỏi: "các lớp này có nhất thiết phải chia sẻ việc thực hiện không , hay chúng chỉ có một giao diện chung ?"

Nếu câu trả lời là hỗn hợp, chẳng hạn như - ba lớp này phải chia sẻ việc triển khai, nhưng hai lớp kia chỉ chia sẻ API của chúng - thì bạn có thể tạo giao diện cho cả năm lớp và một lớp trừu tượng cho ba lớp đó với chung mã.

Ngoài ra còn có các cách khác để chia sẻ triển khai, ví dụ như gói gọn một đối tượng với triển khai đó (ví dụ: trong mẫu Chiến lược ).


1

Bạn sẽ khai báo một bản tóm tắt lớp khi bạn không muốn nhà phát triển (có thể là chính mình) được phép khởi tạo nó, bởi vì nó không hoạt động hoặc sẽ không có ý nghĩa.

Ví dụ, hãy xem xét một trò chơi trong đó có các loại thực thể trò chơi khác nhau. Tất cả đều được thừa hưởng từ GameEntitylớp cơ sở .

abstract class GameEntity{

    int lifePoint, speed, damage;

    public attack(GameEntity target){ target.damage(damage); }

    public damage(int damageInflicted){ lifePoint -= damageInflicted - speed; }

    // etc...

}

Lớp này được khai báo abstractvì sẽ không có ý nghĩa để khởi tạo nó. Nó tuyên bố một số hành động cho các thực thể trò chơi và một số thuộc tính, nhưng không nơi nào trong lớp này là các thuộc tính này được khởi tạo. Lớp này đóng vai trò là khuôn mẫu cho các thực thể trò chơi, nhưng không có nghĩa là được khởi tạo ngay trên chính nó và như đã tuyên bố abstract.

Về sự khác biệt trong cách sử dụng giữa một lớp trừu tượng và giao diện:

Như tôi thấy, giao diện là một cách để đạt được hành vi đa hình mà không bị giới hạn bởi cơ chế kế thừa đơn lẻ của một số ngôn ngữ.

Hãy trở lại trò chơi làm ví dụ. Hãy xem xét một lớp Enemycó nguồn gốc từ GameEntity. Lớp này có một phương thức attackMeFromDistance(RangedAttacker attacker). Phương pháp này có nghĩa là cho phép các thực thể tấn công kẻ thù từ xa.

Như bạn có thể thấy, phương thức này lấy một RangedAttackerkiểu làm tham số. Tuy nhiên, tất cả các thực thể trò chơi đã kế thừa từ GameEntity. Họ không thể mở rộng một lớp học khác.

Lấy các lớp MageArcherví dụ. Chúng tôi muốn cho phép cả hai được chấp nhận làm tham số trong attackMeFromDistance(RangedAttacker attacker)phương thức, nhưng chúng đã được bắt nguồn từ đó GameEntity.

Để giải quyết điều này, chúng tôi tạo ra một giao diện mới:

interface RangedAttacker{
    public void attackFromDistance();
}

Một lớp thực hiện giao diện này phải thực hiện attackFromDistance()phương thức, và do đó nó được đảm bảo rằng nó có khả năng tấn công tầm xa. Điều này có nghĩa là attackMeFromDistancephương thức này có thể chấp nhận một cách an toàn các lớp thực hiện giao diện này. Vì vậy, thực hiện MageArcherthực hiện giao diện đó giải quyết vấn đề của chúng tôi.

Đối với tôi, đây là sức mạnh của giao diện.

Vì vậy, để tóm tắt, bạn thường sử dụng một lớp trừu tượng khi bạn muốn có một lớp cơ sở cho một số lớp, nhưng sẽ không có ý nghĩa để tự khởi tạo nó (hoặc trong trường hợp nó có abstractcác phương thức, phải có được thực hiện bởi các lớp con và trong trường hợp này, trình biên dịch sẽ bắt buộc bạn tạo lớp abstract). Bạn sẽ sử dụng một giao diện để đạt được hành vi đa hình mà không bị giới hạn bởi cơ chế kế thừa đơn.


0
  1. Có thể có quá ít phương thức phổ biến đối với một số lớp (logic nghiệp vụ). Và các phương pháp còn lại là khác nhau. Trong loại kịch bản đó, bạn có thể thực hiện tất cả các phương thức phổ biến trong một lớp và khai báo phần còn lại là trừu tượng. Sau đó, bạn nên khai báo lớp là trừu tượng.
  2. Đôi khi bạn không cho phép tạo đối tượng trực tiếp đến lớp. Đó là loại lớp bạn cần khai báo lớp là trừu tượng.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.