Чи повинні усі публічні методи в абстрактному класі позначатись віртуальними?


12

Нещодавно мені довелося оновити абстрактний базовий клас на деяких ОС, які я використовував, щоб він був більш перевіреним, зробивши їх віртуальними (я не міг використовувати інтерфейс, оскільки він поєднував два). Це змусило мене замислитись, чи слід відзначати всі методи, які мені потрібні віртуальні, або чи слід відзначати кожен публічний метод / властивість віртуальним. Я, як правило, погоджуюся з Роєм Ошеровим, що кожен метод повинен бути віртуальним, але я натрапив на цю статтю, яка змусила мене замислитися над тим, чи потрібно це чи ні . Я збираюся обмежити це абстрактними класами для простоти (проте, чи всі конкретні публічні методи мають бути віртуальними, це особливо дискусійно).

Я міг бачити, де ви можете дозволити підкласу використовувати метод, але не хочу, щоб він переосмислював реалізацію. Однак, якщо ви довіряєте, що Принцип заміщення Ліскова буде дотримуватися, то чому б ви не дозволили його скасувати? Позначивши його абстрактним, ви все одно змушуєте певну переоцінку, тому мені здається, що всі публічні методи всередині абстрактного класу дійсно повинні бути позначені віртуальними.

Однак я хотів запитати, якщо є щось, про що я, можливо, не думаю. Чи всі публічні методи в абстрактному класі повинні бути віртуальними?


Це може допомогти: stackoverflow.com/questions/391483 / ...
NoChance

1
Можливо, вам варто розібратися, чому в C # за замовчуванням не віртуально, але в Java це. Причина, чому, ймовірно, дасть вам деяке розуміння відповіді.
Даніель Літтл

Відповіді:


9

Однак, якщо ви довіряєте, що Принцип заміщення Ліскова буде дотримуватися, то чому б ви не дозволили його скасувати?

Наприклад, тому, що я хочу, щоб реалізація алгоритму в скелеті була виправлена, і я дозволяю лише певним частинам визначати (пере) визначати підкласи. Це широко відоме як шаблон методу шаблонів (акцент нижче)

Таким чином, шаблонний метод управляє більшою картиною семантики завдань та більш досконалими деталями реалізації вибору та послідовності методів. Ця велика картина називає абстрактні та не абстрактні методи для заданого завдання. Неабстрактні методи повністю контролюються методом шаблону, але абстрактні методи, реалізовані в підкласах, забезпечують виразну силу та ступінь свободи. Деякі або всі абстрактні методи можуть бути спеціалізовані на підкласі, дозволяючи автору підкласу надавати особливу поведінку з мінімальними модифікаціями більшої семантики. Метод шаблону (який не є абстрактним) залишається незмінним у цій схемі, гарантуючи, що підпорядковані не абстрактні методи та абстрактні методи називаються в спочатку призначеній послідовності.

Оновлення

Деякі конкретні приклади проектів, над якими я працював:

  1. спілкування зі застарілою системою мейнфрейму через різні "екрани". Кожен екран має купу полів із фіксованим іменем, положенням і довжиною, що містять конкретні біти даних. Запит заповнює певні поля конкретними даними. Відповідь повертає дані в одному або декількох інших полях. Кожна транзакція дотримується тієї ж основної логіки, але деталі різні на кожному екрані. Ми використовували метод шаблону в декількох різних проектах для реалізації фіксованого скелета логіки обробки екрана, дозволяючи підкласам визначати деталі, характерні для екрана.
  2. експорт / імпорт даних конфігурації в таблиці БД до / з файлів Excel. Знову ж таки, основна схема обробки файлу Excel та вставки / оновлення записів БД або скидання записів у Excel однакова для кожної таблиці, але деталі кожної таблиці різні. Тож метод шаблонів - це дуже очевидний вибір для усунення дублювання коду та полегшення коду для розуміння та обслуговування.
  3. Створення PDF-документів. Кожен документ має однакову структуру, але їх зміст щоразу різний, залежно від безлічі факторів. Знову ж таки, метод шаблону дозволяє легко відокремити нерухомий скелет алгоритму генерації від конкретних змінних деталей, що залежать від конкретного випадку. Насправді. він навіть застосовується на кількох рівнях тут, оскільки документ складається з декількох розділів , кожен з яких складається з нуля або більше полів . Тут застосовується метод шаблону на 3 різних рівнях.

У перших двох випадках оригінальна реалізована застаріла реалізація використовувала стратегію , в результаті чого багато дублювали код, який, звичайно, роками тут і там зростав непомітні відмінності, містив безліч дублюваних або трохи інших помилок, і було дуже важко підтримувати. Рефакторинг на метод шаблонів (та деякі інші вдосконалення, наприклад використання анотацій Java) зменшив розмір коду приблизно на 40-70%.

Це лише найсвіжіші приклади, які приходять мені на думку. Я міг би навести більше випадків майже з кожного проекту, над яким я працював до цього часу.


Просто цитування GoF - це не відповідь. Вам потрібно було б дати фактичну причину, щоб зробити таке.
DeadMG

@DeadMG, я регулярно використовував метод шаблонів протягом своєї кар’єри, тому я вважав очевидним, що це дуже практична і корисна модель (як і більшість зразків GoF ... це не теоретичні навчальні вправи, а зібрані з реального світового досвіду). Але, мабуть, не всі згодні з цим ... тому я додаю пару конкретних прикладів до своєї відповіді.
Петер Тьорк

2

Цілком розумно, а іноді бажано мати невіртуальні методи в абстрактному базовому класі; лише тому, що це абстрактний клас, не обов'язково означає, що кожна його частина повинна бути поліморфно застосована.

Наприклад, ви можете скористатися ідіомою "Невіртуальний поліморфізм", згідно з якою функція поліморфно викликається функцією невіртуального члена, щоб забезпечити виконання певних передумов або постумов до виклику віртуальної функції

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

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

Я заперечую, що поки загальна поведінка залишається такою ж, то це можна зробити віртуальним. Ви можете заповнити умови до / після публікації, використовуючи іншу реалізацію (база даних в пам'яті). Інакше це має бути приватна функція?
Джастін Піхоні

2

Класу достатньо містити ОДИН віртуальний метод, щоб клас став абстрактним. Ви можете звернути увагу на те, які методи ви хочете віртуальних, а які ні, відповідно до типу поліморфізму, який ви плануєте використовувати.


1

Запитайте себе, чим користується невіртуальний метод в абстрактному класі. Такий метод повинен мати реалізацію, щоб зробити його корисним. Але якщо клас має реалізацію, чи можна його ще назвати абстрактним класом? Навіть якщо мова / компілятор це дозволяє, чи має це сенс? Особисто я не думаю. У вас був би нормальний клас з абстрактними методами, які очікують впровадження нащадків.

Моя основна мова - Delphi, а не C #. У Delphi, якщо ви позначаєте абстрактний метод, ви також повинні позначати його віртуальним або компілятор скаржиться. Не надто уважно стежили за останніми змінами мови, але якщо абстрактні класи знаходяться у Delphi або приходять до них, я б очікував, що компілятор поскаржиться на будь-які невіртуальні методи, будь-які приватні методи та будь-які реалізації методів для класу, позначеного абстрактним на рівні класу.


2
Я думаю, що має сенс мати абстрактний клас, який має деякі абстрактні методи, деякі віртуальні методи та деякі невіртуальні методи. Я думаю, це завжди залежить від того, що саме ти хочеш робити.
svick

3
Спочатку я перейду ваші C # q. Абстрактний метод в C # неявно віртуальний і не може мати реалізацію. Що стосується вашого першого пункту, абстрактний клас в C # може і повинен мати якусь реалізацію (інакше вам просто слід використовувати інтерфейс). Суть абстрактного класу полягає в тому, що ОБОВ'ЯЗКОВО бути підкласовим, проте він містить логіку, яку (теоретично) використовуватимуть усі підкласи. Це скорочує дублювання коду. Я запитую, чи не слід перекривати будь-яку з цих реалізацій від перекриття (по суті кажучи, базовий спосіб - єдиний спосіб).
Джастін Піхоні

@JustinPihony: спасибі. У Delphi, коли ви позначаєте абстрактний метод, компілятор скаржиться, якщо ви надасте для нього реалізацію. Цікаво, як різні мови по-різному реалізують поняття і таким чином створюють різні очікування у своїх користувачів.
Мар'ян Венема

@svick: так, це має досконалий сенс, я б просто не називав це абстрактним класом, а класом з абстрактними методами ... Але я гадаю, що це може бути моєю інтерпретацією.
Мар'ян Венема

@MarjanVenema, але це не термінологія, яку використовує C #. Якщо ви позначаєте будь-які методи в класі abstract, ви також повинні позначити весь клас abstract.
svick

0

Однак, якщо ви довіряєте, що Принцип заміщення Ліскова буде дотримуватися, то> чому б ви не дозволили його скасувати?

Ви не робите певні методи віртуальними, оскільки не вважаєте, що це так. Крім того, роблячи певні методи не віртуальними, ви подаєте сигнал спадкоємцям, який метод (и) слід реалізувати.

Особисто я часто буду робити перевантаження методів, які існують для зручності, а не віртуальні, так що користувачі класу можуть мати послідовні за замовчуванням, а реалізатори навіть не в змозі зробити помилку порушення того, що передбачає поведінку.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.