Коли перенести загальне поле в базовий клас?


16

Наразі у мене є два похідні класи, Aі в Bобох є спільне поле, і я намагаюся визначити, чи повинен він переходити до базового класу.

На нього ніколи не посилається базовий клас, і скажіть, якщо в якийсь момент по дорозі виведений інший клас C, який не має _field1, тоді не було б порушено принцип "найменш привілейованого" (або щось таке), якщо він був?

public abstract class Base
{
    // Should _field1 be brought up to Base?
    //protected int Field1 { get; set; }
}

public class A : Base
{
    private int _field1;
}

public class B : Base
{
    private int _field1;
}

public class C : Base
{
    // Doesn't have/reference _field1
}

18
Я думаю , це питання залишається неясним , тому що ви не дали нам жодного уявлення про те , що Base, A, B, Cі _field1є. Це важливі деталі, які не слід оминати; Я думаю, ви повинні відредагувати питання, щоб поговорити про те, що це таке.
Таннер

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

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

Для чого у вас базовий клас для початку?
jpmc26

Відповіді:


34

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

Розглянемо конкретний приклад: ваш абстрактний базовий клас є, Vehicleі ви зараз маєте конкретні реалізації Bicycleта Car. Ви розглядаєте можливість переміщення numberOfWheelsз Bicycleта Carдо транспортного засобу. Чи варто це робити? Ні! Тому що не всі транспортні засоби мають колеса. Ви вже можете сказати, що якщо ви спробуєте додати Boatклас, то він зламається.

Тепер, якщо ваш абстрактний базовий клас був, WheeledVehicleто логічно мати там numberOfWheelsзмінну члена.

Вам потрібно застосувати ту саму логіку до вашої проблеми, оскільки, як бачите, це не проста відповідь так чи ні.


3
Можна тимчасово прийняти, що 0 - це дійсне числоWfWels. Однак зрештою ви можете додати roll()метод, і тоді ідея підкласу виглядає давніше.
user949300

11
Човен має 0 колес. Що це ламає?
D Drmmr

14
@DDrmmr Справа не в тому, що човен має 0 колес, це те, що Колеса навіть не існують як концепція для човна - отже, ваші моделі не повинні цього допускати.
Пітер М

10
Моя думка, що приклад поганий. У транспортному засобі (що трапляється на човні) нічого концептуально не так, заявляючи, що у нього 0 колес.
D Drmmr

10
Тоді все йде в пекло, коли вам потрібно зберігати весловий човен.
IllusiveBrian

13

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

Використовуючи приклад @ Pete, ми запровадимо (можливо, абстрактний) підклас для Wheeled Vehicle, який походить від початкового базового класу - в той час як два підкласи спускаються з нього. Таким чином, оригінальний базовий клас не забруднений колесами, але спільність коліс - ДУХ (не повторюється серед підкласів, які мають колеса).

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


Це насправді те, що я вирішив зробити (в основному A і B перетинаються, тому B походить від A).
Саміс

9

Я зіграю тут захисника чорта.

Зараз ти нічого не повинен робити .

ДУХО? Ні. Але краще трохи дублювати, ніж передчасне абстрагування, від якого ви не зможете легко повернутися пізніше. Рефактор для переміщення властивості до загального базового класу легко. Йти іншим шляхом - це не. Зачекай і побачиш.

Приймаючи таке рішення, я, як правило, використовую "правило 3": раз я повторив одне і те ж, наприклад, у трьох різних місцях, і тільки потім, чи вважаю я його переміщенням по ланцюгу. Зверніть увагу: ви лише у 2.


2
Мудре спостереження, яке, я б сказав, необхідне для прийняття рішення.
radarbob

1
Я в основному погоджуюся, але "прямо зараз" (без зайвого коду, як той, який ми бачимо) рефакторинг в обох напрямках є тривіальним. Але якщо є додатковий код, який використовує поле, рефакторинг в обох напрямках може стати значно складнішим.
Док Браун

2

Загалом, я б перемістив його до базового класу. Я не думаю, що є об'єктивний так / ні, тому що тут є компроміс - перенесення невикористаних полів проти зниження складності.

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

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


Мій приклад банальний, хоча це все-таки виробничий код. Ви робите хороший аргумент для "компрометації" ієрархії з "важким" базовим класом, до якого я теж бачив би, що схиляється в такому випадку.
Саміс

1
@samis: ви використовуєте класи під назвою "A, B, C" та "Base" лише з одним полем і без методу у виробничому коді? Я сумніваюсь у цьому.
Док Браун
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.