Tất cả các phương thức công khai trong một lớp trừu tượng có thể được đánh dấu ảo?


12

Gần đây tôi đã phải cập nhật một lớp cơ sở trừu tượng trên một số OSS mà tôi đang sử dụng để nó dễ kiểm tra hơn bằng cách biến chúng thành ảo (tôi không thể sử dụng giao diện khi kết hợp hai giao diện). Điều này khiến tôi suy nghĩ liệu tôi nên đánh dấu tất cả các phương thức mà tôi cần ảo hay liệu tôi nên đánh dấu mọi phương thức / thuộc tính ảo công khai. Tôi thường đồng ý với Roy Osherove rằng mọi phương pháp nên được làm ảo, nhưng tôi đã xem qua bài viết này khiến tôi suy nghĩ về việc liệu điều này có cần thiết hay không . Tuy nhiên, tôi sẽ giới hạn điều này xuống các lớp trừu tượng để đơn giản (dù tất cả các phương thức công khai cụ thể có phải là ảo hay không đều gây tranh cãi, tôi chắc chắn).

Tôi có thể thấy nơi bạn có thể muốn cho phép một lớp con sử dụng một phương thức, nhưng không muốn nó ghi đè lên việc thực hiện. Tuy nhiên, miễn là bạn tin tưởng rằng Nguyên tắc thay thế của Liskov sẽ được tuân theo, vậy tại sao bạn không cho phép nó bị ghi đè? Bằng cách đánh dấu nó là trừu tượng, dù sao bạn cũng đang buộc một ghi đè nào đó, vì vậy, dường như tất cả các phương thức công khai bên trong một lớp trừu tượng thực sự phải được đánh dấu ảo.

Tuy nhiên, tôi muốn hỏi trong trường hợp có điều gì đó tôi có thể không nghĩ tới. Tất cả các phương thức công khai trong một lớp trừu tượng có nên được tạo ảo không?


Điều này có thể giúp: stackoverflow.com/questions/391483/ trên
NoChance

1
Có lẽ bạn nên xem tại sao trong C # mặc định không phải là ảo nhưng trong Java thì có. Lý do tại sao có thể sẽ cung cấp cho bạn một số cái nhìn sâu sắc về câu trả lời.
Daniel Little

Câu trả lời:


9

Tuy nhiên, miễn là bạn tin tưởng rằng Nguyên tắc thay thế của Liskov sẽ được tuân theo, thì tại sao bạn không cho phép nó bị ghi đè?

Ví dụ, vì tôi muốn triển khai bộ xương của thuật toán được sửa lỗi và chỉ cho phép các phần cụ thể được xác định lại bởi các lớp con. Điều này được biết đến rộng rãi là mẫu Phương thức mẫu (nhấn mạnh bên dưới bởi tôi):

Do đó, phương thức mẫu quản lý bức tranh lớn hơn về ngữ nghĩa tác vụ và các chi tiết triển khai được tinh chỉnh hơn về lựa chọn và trình tự các phương thức. Bức tranh lớn hơn này gọi các phương thức trừu tượng và không trừu tượng cho nhiệm vụ trong tầm tay. Các phương thức không trừu tượng được kiểm soát hoàn toàn bởi phương thức mẫu nhưng các phương thức trừu tượng, được triển khai trong các lớp con, cung cấp sức mạnh biểu cảm và mức độ tự do của mẫu. Một số hoặc tất cả các phương thức trừu tượng có thể được chuyên môn hóa trong một lớp con, cho phép người viết của lớp con cung cấp hành vi cụ thể với các sửa đổi tối thiểu cho ngữ nghĩa lớn hơn. Phương thức mẫu (không trừu tượng) vẫn không thay đổi trong mẫu này, đảm bảo rằng các phương thức không trừu tượng phụ và các phương thức trừu tượng được gọi trong chuỗi dự định ban đầu.

Cập nhật

Một số ví dụ cụ thể về các dự án tôi đang thực hiện:

  1. giao tiếp với hệ thống máy tính lớn kế thừa qua nhiều "màn hình" khác nhau. Mỗi màn hình có một loạt các trường, tên, vị trí và độ dài cố định, chứa các bit dữ liệu cụ thể. Một yêu cầu điền vào các trường nhất định với dữ liệu cụ thể. Một phản hồi trả về dữ liệu trong một hoặc nhiều lĩnh vực khác. Mỗi giao dịch tuân theo logic cơ bản giống nhau, nhưng các chi tiết khác nhau trên mỗi màn hình. Chúng tôi đã sử dụng Phương thức mẫu trong một số dự án khác nhau để triển khai khung cố định của logic xử lý màn hình, đồng thời cho phép các lớp con xác định chi tiết cụ thể của màn hình.
  2. xuất / nhập dữ liệu cấu hình trong các bảng DB đến / từ các tệp Excel. Một lần nữa, lược đồ cơ bản xử lý tệp Excel và chèn / cập nhật các bản ghi DB hoặc kết xuất các bản ghi vào Excel là giống nhau cho mỗi bảng, nhưng chi tiết của mỗi bảng là khác nhau. Vì vậy, Phương thức mẫu là một lựa chọn rất rõ ràng để loại bỏ trùng lặp mã và làm cho mã dễ hiểu và duy trì hơn.
  3. Tạo tài liệu PDF. Mỗi tài liệu có cùng cấu trúc, nhưng nội dung của chúng mỗi lần khác nhau, tùy thuộc vào rất nhiều yếu tố. Một lần nữa, Phương thức mẫu giúp dễ dàng tách bộ xương cố định của thuật toán tạo ra khỏi các chi tiết cụ thể, có thể thay đổi theo từng trường hợp. Trong thực tế. nó thậm chí còn áp dụng trên nhiều cấp độ ở đây, vì tài liệu bao gồm một số phần , mỗi phần bao gồm 0 hoặc nhiều trường . Phương pháp mẫu được áp dụng trên 3 cấp độ khác nhau ở đây.

Trong hai trường hợp đầu tiên, việc triển khai di sản ban đầu đã sử dụng Chiến lược , dẫn đến rất nhiều mã trùng lặp, tất nhiên qua nhiều năm đã có sự khác biệt tinh tế ở đây và ở đó, chứa rất nhiều lỗi trùng lặp hoặc hơi khác nhau và rất khó duy trì. Tái cấu trúc thành Phương thức mẫu (và một số cải tiến khác, như sử dụng chú thích Java) đã giảm kích thước mã khoảng 40-70%.

Đây chỉ là những ví dụ gần đây nhất trong tâm trí tôi. Tôi có thể trích dẫn nhiều trường hợp hơn từ hầu hết các dự án tôi đã làm cho đến nay.


Đơn giản chỉ cần trích dẫn GoF không phải là một câu trả lời. Bạn cần đưa ra một lý do thực tế để làm một việc như vậy.
DeadMG

@DeadMG, tôi đã sử dụng Phương thức Mẫu thường xuyên trong suốt sự nghiệp của mình, vì vậy tôi nghĩ rõ ràng đó là một mẫu rất thực tế và hữu ích (vì hầu hết các mẫu của GoF là ... những bài tập lý thuyết không phải là lý thuyết này, nhưng được thu thập từ kinh nghiệm thực tế). Nhưng rõ ràng không phải ai cũng đồng ý với nó ... vì vậy tôi thêm một vài ví dụ cụ thể vào câu trả lời của mình.
Péter Török

2

Hoàn toàn hợp lý và đôi khi mong muốn có các phương thức không ảo trong một lớp cơ sở trừu tượng; chỉ vì nó là một lớp trừu tượng không nhất thiết có nghĩa là mọi phần của nó sẽ có thể được sử dụng đa hình.

Ví dụ: bạn có thể muốn sử dụng thành ngữ 'Không đa hình ảo', theo đó một hàm được gọi là đa hình từ một hàm thành viên không ảo, để đảm bảo rằng các điều kiện tiên quyết hoặc hậu điều kiện được đáp ứng trước khi hàm ảo được gọi

class MyAbstractBaseClass
{
protected:
    virtual void OverrideMe() = 0;
public:
    void CallMeFirst();

    void CallMe()
    {
        CallMeFirst();
        OverrideMe();
    }
};

Tôi sẽ lập luận rằng, miễn là hành vi tổng thể vẫn như cũ, thì điều này có thể được biến thành ảo. Bạn có thể điền vào các điều kiện trước / sau bằng cách sử dụng một triển khai khác (cơ sở dữ liệu so với trong bộ nhớ). Nếu không, nó phải là một chức năng riêng tư?
Justin Pihony

2

Nó là đủ để một lớp chứa MỘT phương thức ảo, để lớp trở nên trừu tượng. Bạn có thể muốn chú ý đến phương pháp nào bạn muốn ảo và phương pháp nào không, theo loại đa hình mà bạn dự định sử dụng.


1

Hãy tự hỏi những gì sử dụng của một phương pháp không ảo trong một lớp trừu tượng. Một phương pháp như vậy sẽ phải có một triển khai để làm cho nó hữu ích. Nhưng nếu lớp có một triển khai, nó vẫn có thể được gọi là một lớp trừu tượng? Ngay cả khi ngôn ngữ / trình biên dịch cho phép nó, nó có ý nghĩa không? Cá nhân, tôi không nghĩ vậy. Bạn sẽ có một lớp học bình thường với các phương thức trừu tượng mà con cháu dự kiến ​​sẽ thực hiện.

Ngôn ngữ chính của tôi là Delphi, không phải C #. Trong Delphi, nếu bạn đánh dấu một phương thức trừu tượng, bạn cũng phải đánh dấu nó ảo hoặc trình biên dịch phàn nàn. Không theo dõi các thay đổi ngôn ngữ mới nhất quá chặt chẽ, nhưng nếu các lớp trừu tượng đang ở hoặc sẽ đến Delphi, tôi sẽ mong trình biên dịch phàn nàn về bất kỳ phương thức không ảo nào, bất kỳ phương thức riêng tư nào và về bất kỳ phương thức triển khai phương thức nào cho một lớp được đánh dấu trừu tượng ở cấp lớp.


2
Tôi nghĩ thật hợp lý khi có một lớp trừu tượng có một số phương thức trừu tượng, một số phương thức ảo và một số phương thức không ảo. Tôi nghĩ nó luôn phụ thuộc vào chính xác những gì bạn muốn làm.
Svick

3
Đầu tiên tôi sẽ đi qua C # q của bạn. Một phương thức trừu tượng trong C # là hoàn toàn ảo và không thể thực hiện được. Theo điểm đầu tiên của bạn, một lớp trừu tượng trong C # có thể, và nên có một triển khai thực hiện một số loại (nếu không bạn chỉ nên sử dụng một giao diện). Điểm của một lớp là trừu tượng là nó PHẢI được phân lớp, tuy nhiên nó chứa logic mà (về mặt lý thuyết) tất cả các lớp con sẽ sử dụng. Điều này cắt giảm sao chép mã. Những gì tôi đang hỏi là liệu có nên ngừng hoạt động trong số những triển khai đó hay không (nói về cách cơ bản là cách duy nhất).
Justin Pihony

@JustinPihony: cảm ơn. Trong Delphi, khi bạn đánh dấu một phương thức trừu tượng, trình biên dịch sẽ khiếu nại nếu bạn cung cấp một triển khai cho nó. Thật thú vị khi các ngôn ngữ khác nhau thực hiện các khái niệm khác nhau và do đó tạo ra những kỳ vọng khác nhau ở người dùng của họ.
Marjan Venema

@svick: vâng, nó có ý nghĩa hoàn hảo, tôi sẽ không gọi nó là một lớp trừu tượng, mà là một lớp với các phương thức trừu tượng ... Nhưng tôi đoán đó có thể chỉ là cách giải thích của tôi.
Marjan Venema

@MarjanVenema, nhưng đó không phải là thuật ngữ C # sử dụng. Nếu bạn đánh dấu bất kỳ phương thức nào trong một lớp abstract, bạn cũng phải đánh dấu cả lớp abstract.
Svick

0

Tuy nhiên, miễn là bạn tin tưởng rằng Nguyên tắc thay thế của Liskov sẽ được tuân theo, thì> tại sao bạn không cho phép nó bị ghi đè?

Bạn không biến một số phương thức thành ảo vì bạn không tin đây là trường hợp. Hơn nữa, bằng cách làm cho các phương thức nhất định không phải là ảo, bạn đang báo hiệu cho những người thừa kế nên thực hiện phương thức nào.

Cá nhân, tôi sẽ thường làm quá tải phương thức tồn tại vì tiện lợi không phải là ảo để người dùng của lớp có thể có các mặc định nhất quán và người triển khai thậm chí không thể tạo ra lỗi phá vỡ hành vi 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.