Sự khác biệt giữa 'mở rộng' và 'thực hiện' trong TypeScript


Câu trả lời:


153

Phiên bản ngắn

  • extends có nghĩa:

Lớp mới là trẻ em . Nó nhận được lợi ích đi kèm với thừa kế. Nó có tất cả các thuộc tính, phương thức làm cha mẹ của nó. Nó có thể ghi đè một số trong số này và triển khai mới, nhưng nội dung chính đã được bao gồm.

  • implements có nghĩa:

Lớp mới có thể được coi là "hình dạng" giống nhau , trong khi nó không phải là lớp con . Nó có thể được chuyển đến bất kỳ phương thức nào mà Personyêu cầu, bất kể có cha mẹ khác vớiPerson

Hơn ...

Trong OOP (các ngôn ngữ như C #, Java), chúng tôi sẽ sử dụng

extendsthu lợi nhuận từ thừa kế (xem wiki ). Trích dẫn nhỏ:

... Kế thừa trong hầu hết các ngôn ngữ hướng đối tượng dựa trên lớp là một cơ chế trong đó một đối tượng có được tất cả các thuộc tính và hành vi của đối tượng mẹ. Tính kế thừa cho phép lập trình viên: tạo các lớp được xây dựng dựa trên các lớp hiện có ...

implementssẽ nhiều hơn cho tính đa hình (xem wiki ). Trích dẫn nhỏ:

... đa hình là việc cung cấp một giao diện duy nhất cho các thực thể thuộc các loại khác nhau ...

Vì vậy, chúng ta có thể có cây thừa kế thực sự khác nhau của chúng ta class Man.

class Man extends Human ...

nhưng nếu chúng tôi cũng tuyên bố rằng chúng tôi có thể giả vờ là một kiểu khác - Person:

class Man extends Human 
          implements Person ...

.. thì chúng ta có thể sử dụng nó ở bất cứ đâu, nơi Personđược yêu cầu. Chúng ta chỉ cần thực hiện các Nhân viên "interface" (tức là triển khai tất cả các công cụ của nó) .

implementlớp khác? Đó là thứ thực sự thú vị

Mặt đẹp của Javascript (một trong những lợi ích) là sự hỗ trợ tích hợp của kiểu gõ Duck ( xem wiki ). Trích dẫn nhỏ:

"Nếu nó đi như một con vịt và nó lang thang như một con vịt, thì nó phải là một con vịt."

Vì vậy, trong Javascript, nếu hai đối tượng khác nhau ... sẽ có một phương thức tương tự (ví dụ render()), chúng có thể được chuyển đến một hàm mong đợi nó:

function(engine){
  engine.render() // any type implementing render() can be passed
}

Để không làm mất điều đó - chúng ta cũng có thể làm như vậy trong Typescript - với nhiều hỗ trợ đánh máy hơn. Và đó là nơi

class implements class

có vai trò của nó, nơi nó có ý nghĩa

Trong các ngôn ngữ OOP như C#... không có cách nào để làm điều đó ...

Ngoài ra, tài liệu sẽ giúp ở đây:

Các lớp mở rộng giao diện

Khi một kiểu giao diện mở rộng một kiểu lớp, nó kế thừa các thành viên của lớp nhưng không kế thừa các phần triển khai của chúng. Nó giống như thể giao diện đã khai báo tất cả các thành viên của lớp mà không cung cấp triển khai. Các giao diện kế thừa ngay cả các thành viên riêng tư và được bảo vệ của một lớp cơ sở. Điều này có nghĩa là khi bạn tạo một giao diện mở rộng một lớp với các thành viên riêng tư hoặc được bảo vệ, kiểu giao diện đó chỉ có thể được thực hiện bởi lớp đó hoặc một lớp con của nó.

Điều này hữu ích khi bạn có hệ thống phân cấp kế thừa lớn, nhưng muốn chỉ định rằng mã của bạn chỉ hoạt động với các lớp con có các thuộc tính nhất định. Các lớp con không cần phải liên quan ngoài việc kế thừa từ lớp cơ sở. Ví dụ:

class Control {
    private state: any;
}

interface SelectableControl extends Control {
    select(): void;
}

class Button extends Control implements SelectableControl {
    select() { }
}

class TextBox extends Control {
    select() { }
}

// Error: Property 'state' is missing in type 'Image'.
class Image implements SelectableControl {
    private state: any;
    select() { }
}

class Location {

}

Vì vậy, trong khi

  • extends có nghĩa là - nó nhận tất cả từ cha mẹ của nó
  • implementstrong trường hợp này gần giống như thực hiện một giao diện. Đối tượng con có thể giả vờ rằng nó là cha mẹ ... nhưng nó không nhận được bất kỳ triển khai nào

khi bạn nói " extends-it get all from your parent", nó có áp dụng cho các thành viên riêng tư không? Ví dụ, class Person {private name: string} class man extends Person{gender: string;}không mancó tên tài sản?
davejoem

Tư nhân cũng có. Chỉ không thể truy cập được bởi TS. Làm cho chúng được bảo vệ và bạn có thể sử dụng chúng. Trong trường hợp "thực hiện" chỉ là phần công cộng có ý nghĩa. Hy vọng nó sẽ giúp một chút
Radim Köhler

Câu trả lời xuất sắc. Chỉ không chắc chắn bởi bình luận của bạn cho "riêng tư có ở đó nhưng không thể truy cập bởi TS". Ý bạn là thuộc tính private được sao chép trong đối tượng con mới được tạo đó? Và trong trường hợp thực hiện, chỉ tài sản công được sao chép?
kushalvm

Ngoài ra, tôi còn có một điểm nữa. Nếu đây là định nghĩa của expand. Sau đó, vui lòng nếu bạn có thể giải thích điều này stackoverflow.com/questions/60390454/…
kushalvm

97

Trong bảng chữ (và một số ngôn ngữ OO khác), bạn có các lớp và giao diện.

Một giao diện không có triển khai, nó chỉ là một "hợp đồng" của những thành viên / phương thức mà loại này có.
Ví dụ:

interface Point {
    x: number;
    y: number;
    distance(other: Point): number;
}

Các cá thể triển khai Pointgiao diện này phải có hai thành viên kiểu số: xy, và một phương thức distancenhận một Pointcá thể khác và trả về a number.
Giao diện không triển khai bất kỳ cái nào trong số đó.

Các lớp là triển khai:

class PointImplementation implements Point {
    public x: number;
    public y: number;

    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }

    public distance(other: Point): number {
        return Math.sqrt(Math.pow(this.x - other.x, 2) + Math.pow(this.y - other.y, 2));
    }
}

( mã trong sân chơi )

Trong ví dụ của bạn, bạn coi Personlớp của mình một lần như một lớp khi bạn mở rộng nó và một lần như một giao diện khi bạn triển khai nó.
Ma cua ban:

class Person {
    name: string;
    age: number;
}
class Child  extends Person {}

class Man implements Person {}

Có lỗi biên dịch cho biết:

Class 'Man' triển khai sai giao diện 'Person'.
Loại "Tên" thuộc tính bị thiếu trong loại "Người đàn ông".

Và đó là bởi vì các giao diện thiếu sự triển khai.
Vì vậy, nếu bạn implementlà một lớp thì bạn chỉ lấy "hợp đồng" của nó mà không cần triển khai, vì vậy bạn sẽ cần phải làm điều này:

class NoErrorMan implements Person {
    name: string;
    age: number;
}

( mã trong sân chơi )

Điểm mấu chốt là trong hầu hết các trường hợp, bạn muốn extendhọc một lớp khác chứ không phải lớp implementđó.


7
Câu trả lời này dễ hiểu hơn.
Akshay Raut

6

Câu trả lời tuyệt vời từ @ nitzan-tomer! Đã giúp tôi rất nhiều ... Tôi đã mở rộng bản demo của anh ấy một chút với:

IPoint interface;
Point implements IPoint;
Point3D extends Point;

Và cách chúng hoạt động trong các hàm mong đợi một IPointkiểu.

Vì vậy, những gì tôi đã học được cho đến nay và đang sử dụng như một quy tắc ngón tay cái: Nếu bạn đang sử dụng các lớp và phương thức mong đợi các kiểu chung, hãy sử dụng các giao diện làm các kiểu mong đợi. Và đảm bảo rằng lớp cha hoặc lớp cơ sở sử dụng giao diện đó. Bằng cách đó, bạn có thể sử dụng tất cả các lớp con trong những lớp đó trong chừng mực chúng triển khai giao diện.

Đây là bản demo mở rộng


Điều này không cung cấp câu trả lời cho câu hỏi. Để phê bình hoặc yêu cầu làm rõ từ tác giả, hãy để lại bình luận bên dưới bài đăng của họ. - Từ xét
aronisstav

1
@aronisstav Tôi chỉ đăng một bản demo mở rộng về những gì tôi đã tìm thấy một câu trả lời hay đã giúp tôi. Nhưng có thể ai đó sẽ thấy công việc tôi đã làm trong việc mở rộng bản demo hữu ích. Đó là tất cả. Nhận xét không thực sự dành cho việc đặt một khối mã, vì vậy đó là lý do tại sao tôi thấy nó dễ hiểu hơn trong Bài trả lời. Vậy vấn đề với nó là gì?
andzep

Câu trả lời của bạn (tự động?) Được gắn cờ do độ dài và nội dung, xuất hiện trong hàng đợi đánh giá của tôi và tôi đã khen ngợi những lý do được trình bày trong cờ. Đóng góp chính của nó (giải thích rằng bạn đã mở rộng bản demo) sẽ tốt hơn dưới dạng một nhận xét. Với đoạn được thêm vào có lẽ nó thực sự hữu ích hơn.
aronisstav

@andzep ví dụ demo mở rộng của bạn thực sự hữu ích.
namit

3
  1. Giao diện mở rộng giao diện với hình dạng
  2. Giao diện mở rộng lớp với hình dạng
  3. Giao diện triển khai lớp nên triển khai tất cả các trường do giao diện cung cấp
  4. Lớp thực hiện lớp với hình dạng
  5. Class mở rộng lớp với tất cả các lĩnh vực

extendstập trung vào kế thừa và implementstập trung vào ràng buộc cho dù giao diện hoặc lớp.


0

Mở rộng công cụ VS

  • extends: Lớp con (được mở rộng) sẽ kế thừa tất cả các thuộc tính và phương thức của lớp được mở rộng
  • implements: Lớp sử dụng implementstừ khóa sẽ cần triển khai tất cả các thuộc tính và phương thức của lớp mà nóimplements

Nói một cách đơn giản hơn:

  • extends: Ở đây bạn nhận được tất cả các phương thức / thuộc tính này từ lớp cha, do đó bạn không cần phải tự mình triển khai
  • implements: Đây là một hợp đồng mà lớp phải tuân theo. Lớp phải triển khai ít nhất các phương thức / thuộc tính sau

Thí dụ:

class Person {
  name: string;
  age: number;

  walk(): void {
    console.log('Walking (person Class)')
  }

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}
class child extends Person { }

// Man has to implements at least all the properties
// and methods of the Person class
class man implements Person {
  name: string;
  age: number

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  walk(): void {
    console.log('Walking (man class)')
  }

}

(new child('Mike', 12)).walk();
// logs: Walking(person Class)

(new man('Tom', 12)).walk();
// logs: Walking(man class)

Trong ví dụ, chúng ta có thể thấy rằng lớp con kế thừa mọi thứ từ Person trong khi lớp man phải thực thi mọi thứ từ chính Person.

Nếu chúng ta xóa một cái gì đó khỏi lớp người đàn ông, chẳng hạn như phương thức walk, chúng ta sẽ gặp lỗi thời gian biên dịch sau :

Class 'man' triển khai sai class 'Person'. Ý của bạn là mở rộng 'Person' và kế thừa các thành viên của nó như một lớp con? Thuộc tính "walk" bị thiếu trong loại "man" nhưng được yêu cầu trong loại "Person". (2720)

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.