Có bất kỳ ngôn ngữ OO nào hỗ trợ một cơ chế để đảm bảo một phương thức overriden sẽ gọi cơ sở không?


12

Tôi nghĩ rằng đây có thể là một tính năng ngôn ngữ hữu ích và tự hỏi liệu có ngôn ngữ nào đã hỗ trợ nó không.

Ý tưởng là nếu bạn có:

class C
  virtual F
     statement1
     statement2

class D inherits C
  override F
     statement1
     statement2
     C.F()

Sẽ có một từ khóa được áp dụng cho CF () để loại bỏ dòng mã cuối cùng ở trên sẽ gây ra lỗi trình biên dịch bởi vì nó nói "Phương pháp này có thể bị ghi đè nhưng việc triển khai ở đây cần phải chạy bất kể là gì".


Câu trả lời:


14

Vâng, họ làm. Nó được gọi là mô hình OO của Scandinavia, nó được sử dụng ví dụ trong Simula (mô hình OO khác phổ biến và được sử dụng như được cấp bây giờ, là mô hình của Mỹ). Trong mô hình Scandinavia, bạn không ghi đè, nhưng cung cấp hành vi phụ.

trong phương thức của Superclass foo:

some-code-before
INNER // this is the actual Simula keyword
some-code-after

trong phương thức Subgroup 'foo:

some-code-in-subclass

Nếu bạn gọi phương thức của Superclass 'foo', chỉ some-code-beforesome-code-afterxảy ra ( INNERkhông có gì), nhưng nếu bạn gọi foo của lớp con 'foo, thì nó sẽ some-code-before, some-code-in-subclassvà sau đó some-code-after.


9

Không có ngôn ngữ nào tôi biết về việc thực thi gọi phương thức ghi đè. Thật vậy, một số ngôn ngữ cho phép ghi đè các phương thức không thể ghi đè (chẳng hạn như sử dụng newtừ khóa trong C #). Tuy nhiên, có hai cách tiếp cận điều này.

Đầu tiên là tạo ra một phương thức không thể rút gọn (ví dụ: một phương thức thiếu virtualtừ khóa trong C # hoặc một phương thức có finaltừ khóa trong Java) gọi một phương thức có thể ghi đè không thể gọi được từ bên ngoài lớp (ví dụ: protectedtrong C #, Java hoặc C ++).

class C
  A
     statement1
     F
     statement3

  protected virtual F
     statement2

class D inherits C

  protected override F
     statement4
     C.F()

Các lớp ghi đè Cđược tự do ghi đè Fvà sửa đổi hành vi của nó nhưng người gọi từ bên ngoài lớp chỉ truy cập thông qua A.

Chỉnh sửa: Như những người khác đã chỉ ra, đây được gọi là mẫu phương thức Mẫu .

Cách thứ hai là sử dụng một ngôn ngữ thực thi các điều kiện tiên quyết và hậu điều kiện được chỉ định trong lớp cơ sở, như Eiffel hoặc C # với Hợp đồng mã. Nó sẽ không buộc lớp cơ sở được gọi nhưng phương thức được ghi đè có thể bị buộc phải thực hiện các câu lệnh tương tự. Sử dụng các khía cạnh cũng có thể giúp nếu ngôn ngữ cho phép các khía cạnh được kế thừa.


2
Bạn thậm chí có thể làm cho phương thức được ghi đè privatetrong C ++ :) Herb Sutter giải thích chi tiết ở đây .
dòng chảy

Mẫu mẫu chỉ có một nhược điểm mà bạn cần thực hiện lại mỗi lần trong khi đi sâu hơn thông qua hệ thống phân cấp thừa kế. Ví dụ với Simula thanh lịch hơn và vẫn cho phép mẫu mẫu.
Pavel Voronin

7

Không thực sự là một phần của ngôn ngữ, nhưng trình phân tích mã tĩnh FindBugs cho Java có một chú thích OverrideMustInvokemà nhà phát triển có thể thêm vào một phương thức và điều này sẽ khiến FindBugs hiển thị lỗi nếu tìm thấy một phương thức ghi đè không gọi siêu thực hiện . Nó thậm chí còn cho phép xác định xem cuộc gọi phải là đầu tiên hay cuối cùng trong phương thức ghi đè.


6

Yêu cầu gọi một phương thức siêu lớp là một mô hình chống . Nếu nó không được thi hành tại thời điểm biên dịch thì nó dễ bị lỗi, đó là lý do tại sao bạn đang tìm kiếm một cấu trúc ngôn ngữ để kiểm tra nó.

Có một cách được hỗ trợ trong tất cả các ngôn ngữ OO: Mẫu phương thức mẫu . Ở đây, bạn làm cho phương thức siêu lớp không bị ghi đè, và trong đó bạn gọi một phương thức có thể ghi đè. Lớp con sau đó có thể ghi đè phương thức này để thêm chức năng:

class super {
  public final void doSomething() {
    doSpecialthing();
    doMore();
  }
  public void doSpecialthing() {
  }
}

Tùy thuộc vào vị trí của cuộc gọi đến phương thức được ghi đè, nó thậm chí còn cho phép xác định thứ tự thực hiện, với siêu cuộc gọi thông thường theo ý muốn của người thực hiện lớp con.


1

Mẫu gần nhất tôi có thể nghĩ đến là các sự kiện tự đăng ký. Đó là một chút rườm rà, và hoàn toàn không trực quan cho người viết mã, nhưng đạt được mục tiêu.

class C
{
    public void F()
    {
        ...
        OnF()
    }

    protected event OnF
}

class D : C
{
    public D()
    {
        base.OnF += this.F
    }

    private void F
    {
        ...
    }
}

1
Đây là một mẫu thiết kế khá phổ biến, đôi khi có cả phần ghi đè pro và post, vd. ViewWillAppear (), ViewDidAppear (). Lý tưởng khi bạn muốn cho phép các lớp con mở rộng (thay vì sửa đổi) hành vi mặc định.
Kris Van Bael

1

Máy Lisp "hương vị" cho phép các phương thức với loại "trước" "sau" và "xung quanh" phương thức chính được kế thừa.


0

Mặc dù về lý thuyết không phải là xấu, nhưng nó có tác dụng phụ tiêu cực trong việc hạn chế các lựa chọn của tôi khi thực hiện D. Ví dụ, nếu (vì một số lý do không thể hiểu được), sẽ thuận tiện hơn khi gọi triển khai siêu lớp Ftừ một số phương thức khác:

class D inherits C
    override F
        statement1
        statement2
        G()
    G
        statement3
        C.F()
        statement4

Theo kịch bản của bạn, tôi tưởng tượng trình biên dịch sẽ đánh dấu việc thực hiện Ftrong D, mặc dù nó thực hiện (gián tiếp) cuộc gọi C.F().

Về cơ bản, những gì bạn đã mô tả là một cơ chế có thể giúp trình biên dịch nhận ra khi hợp đồng trên các lớp kế thừa từ đó Cbị vi phạm. Quan điểm của tôi là, trong khi đó là một điều tuyệt vời, nó không nên trả giá bằng cách giới hạn cách tôi có thể thực hiện lớp con của mình.


1
-1: có thể nghĩ về một tình huống mà bạn không muốn sử dụng đó không phải là câu trả lời cho câu hỏi "có ngôn ngữ nào cho phép không".

@GrahamLee: rất đúng. Quan điểm của tôi là cố gắng giải thích một lý do tại sao không có ngôn ngữ nào (mà tôi biết) thực hiện một tính năng như vậy. Tôi đoán rằng tôi đã bị cuốn vào việc giải thích điều đó, đến nỗi tôi quên đề cập đến lý do tại sao tôi lại giải thích nó. -1 vui vẻ chấp nhận. :)
Mac
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.