Когда перемещать общее поле в базовый класс?


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 - это действительное число колес. Однако, в конце концов, вы можете добавить roll()метод, и в этом случае идея подкласса выглядит предсказуемой.
user949300

11
Лодка имеет 0 колес. Что это ломает?
D Drmmr

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

10
Я хочу сказать, что пример плохой. В транспортном средстве (например, в лодке) нет ничего концептуального, утверждающего, что у него 0 колес.
D Drmmr

10
Тогда все это идет в ад, когда вам нужно хранить байдарку.
IllusiveBrian

13

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

Используя пример @ Pete, мы вводим (возможно, абстрактный) подкласс для Wheeled Vehicle, который происходит от исходного базового класса, а два подкласса происходят от него. Таким образом, исходный базовый класс не загрязнен колесами, но общность колес - СУХАЯ (не повторяется среди подклассов, имеющих колеса).

Это, конечно, может быть излишним для ваших целей, но это поддерживается механизмом иерархии классов.


На самом деле это то, что я решил сделать (A и B в основном пересекаются, поэтому B будет производным от A).
Самис

9

Я собираюсь сыграть адвоката дьявола здесь.

Прямо сейчас ты ничего не должен делать .

СУХОЙ? Нет. Но лучше иметь небольшое дублирование, чем преждевременную абстракцию, от которой потом трудно отказаться. Рефакторинг для перемещения свойства в общий базовый класс прост. Идти в другую сторону нет. Ждать и смотреть.

При принятии такого решения я склонен использовать «правило 3»: как только я повторил одну и ту же вещь, например, в трех разных местах, и только потом, могу ли я рассмотреть вопрос о его продвижении по цепочке. NB вы только в 2.


2
Мудрое наблюдение, которое, я бы сказал, необходимо для принятия решения.
radarbob

1
Я в основном согласен, но «Прямо сейчас» (без дополнительного кода, который мы видим) рефакторинг в обоих направлениях тривиален. Но если есть дополнительный код, который использует поле, рефакторинг в обоих направлениях может стать значительно сложнее.
Док Браун

2

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

Я обычно предпочитаю «тяжелые» базовые классы, которые содержат все, что может быть разделено. Это упрощает сериализацию в файлы, так как вам не нужны методы сериализации потомков в каждом производном классе. Но если у вас нет такой или подобной проблемы, или, возможно, вам нужно сделать все возможное, чтобы уменьшить использование памяти, тогда только в тех полях, где они вам нужны, должно быть хорошо.

Класс-посредник, который вводит общие поля, будет хорошо, если у вас очень ограниченное количество полей. Но имейте в виду, что такой подход может значительно увеличить сложность, если у вас есть десятки полей, используемых в разных комбинациях, что приводит ко многим промежуточным классам, каждый из которых представляет определенный набор полей. Это может стать проблемой обслуживания.


Мой пример тривиален, хотя это все еще рабочий код. Вы приводите хороший аргумент в пользу «компрометации» иерархии с «тяжелым» базовым классом, к которому я тоже склонялся бы в таком случае.
Самис

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