частные или защищенные переменные? - PullRequest
8 голосов
/ 13 июля 2009

Я сейчас читаю Code Complete, где McConnell настоятельно рекомендует сделать все переменные закрытыми. По стечению обстоятельств я просто так работал над проектом, в котором мне нужно было изменить личную переменную.

У класса была закрытая переменная (String), указывающая, откуда загружать изображение для использования в системном Chrome. Мне нужно было изменить это изображение, я не знаю о других языках, но, насколько я знаю, во Flex / AIR нет способа переопределить личную переменную.

Если бы он был объявлен защищенным, я мог бы просто расширить класс и переопределить эту переменную. Но поскольку он был закрытым, мне пришлось скопировать весь код из класса и создать дублированный класс, с той лишь разницей, что эта строка.

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

Это заставляет меня думать, что защищенный лучше, чем частный. Тем не менее, я хочу сделать все как надо. Так что если личное лучше, я хочу понять, почему.

Если общее мнение таково, что частная жизнь лучше, кто-то может объяснить, почему?

Ответы [ 10 ]

26 голосов
/ 13 июля 2009

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

Вы должны оставить поле члена закрытым, но определить защищенное виртуальное свойство для предоставления его производным классам:

private const string _defaultImagePath = @"C:\whatever.bmp";

protected virtual string ImagePath {
    get {return _defaultImagePath;}
}

В производном классе, который хочет изменить его:

private const string _myImagePath = @"C:\other.bmp";
protected override string ImagePath {
    get {return _myImagePath;}
}

Вы также захотите изменить базовый класс, чтобы он использовал свойство, когда ему нужен путь к изображению, вместо использования поля. Это рефакторинг "Encapsulate Field".

4 голосов
/ 13 июля 2009

Создайте переменные private, если вы знаете только, что конкретный класс, в котором они определены, будет единственным, использующим эти переменные. Однако, если вы хотите расширить класс, вы должны использовать protected. Приватный действительно так, что вы не используете переменные глобально (поэтому вы используете геттеры и сеттеры в классах), что помогает ослабить связь. Защищено вполне приемлемо при расширении класса.

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

2 голосов
/ 15 июля 2009

Когда я только начинал работать в ОО, я делал все public или protected, придумывая: «Почему я должен ограничивать то, что другие люди хотели делать с моим кодом?»

Когда я получил больше опыта, произошли две вещи:

  1. Современные IDE пришли, делая это смехотворно легко изменить Код.
  2. Я должен был поддерживать много неприятный код, который был расширен в способы, которые его дизайнеры не намеревались.

Теперь мой подход: делайте это private до тех пор, пока вам не понадобится public (или protected) - и даже тогда задумайтесь, действительно ли это правильное решение.

Хороший protected метод - это преднамеренная точка расширения, поставленная там дизайнером. Я обнаружил, что проектирование для расширяемости на самом деле довольно сложно, и вы должны сделать это - если вы просто оставите это открытым и надеетесь, вы просите кошмары по обслуживанию в будущем. *

2 голосов
/ 13 июля 2009

Если он был объявлен защищенным, я мог бы просто расширить класс, и переопределить эту переменную. Но так как это было частным, мне пришлось скопировать весь код из класса и создать дубликат класса с единственным Разница в том, что строка.

И если бы существовал установщик для местоположения изображения, вам бы вообще не понадобился подкласс (или новый класс).

И если вместо того, чтобы менять образ, вам нужны другие незначительные изменения, то, возможно, может помочь создание какого-то другого защищенного / виртуального API. Или, возможно, нет. Единственное, что я могу сказать, это то, что трудно сразу взглянуть на туманный пример и извлечь из него общую мудрость.

Существует компромисс между степенью гибкости, которую дает вам класс с точки зрения его поведения и расширяемости, и того, сколько усилий требуется для разработки и тестирования класса (и дальнейшего взаимодействия между тем, насколько это понятно, хорошо задокументировано и т. Д.). класс есть). Эти компромиссные решения по проектам не могут быть оценены в вакууме, скорее они должны оцениваться в контексте. Насколько вероятны различные функции / поведения / расширения / изменения, которые класс может возможно поддерживать на самом деле ? Хорошо разработанный класс - это класс, который имеет расширения для распространенных необходимых сценариев и не содержит ненужных расширений и API. (А для имеющихся расширений есть выбор между простыми «установщиками» и более сложными расширяемыми защищенными / виртуальными / подклассами ... последнее не является компромиссом, к которому можно легко перейти.)

Я не знаю, что это отвечает на ваш конкретный вопрос хорошо (или вообще), но то, как я прочитал ваш вопрос, заставило меня почувствовать, что вы намекаете, что "классам лучше быть швейцарскими армейскими ножами потому что расширяемость / настраиваемость это хорошо ", и я хотел сказать, что эти" хорошие "вещи идут с компромиссами. Кроме того, ваш вопрос, казалось, подразумевал, что эта ситуация была о «частном» и «защищенном» и «подклассе», тогда как я думаю, что простой «метод установки» был бы достаточным изменением API.

1 голос
/ 13 июля 2009

Приватный, IMO, сначала всегда лучше, а затем обнародовать / защитить при необходимости. Создать новый метод гораздо проще, чем пытаться удалить функциональность, от которой кто-то зависел.

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

1 голос
/ 13 июля 2009

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

Объявление этой переменной как закрытой должно восприниматься как выражение намерений разработчика для переменной (то есть внутреннего использования), что означает, что вам нужно либо найти другую точку расширения, либо дублировать код.

С точки зрения разработки API следует использовать private, чтобы изолировать детали реализации от вызывающих и защищать, чтобы раскрывать вещи для наследников

0 голосов
/ 24 января 2010

Защищенные члены являются частью API, поскольку их могут видеть производные классы в других пакетах (то есть в пакетах, находящихся вне контроля вашей организации). Как таковые они представляют собой обязательство. Совет предпочитать частные суммы советам, чтобы избежать принятия ненужных обязательств. Вы всегда можете сделать менее видимый элемент более заметным, но не всегда можете сделать обратное.

0 голосов
/ 04 сентября 2009

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

Настоящая причина, по которой вы хотите сохранить приватность члена, заключается в том, что экземпляр подкласса не может иметь неожиданный доступ к членам экземпляра суперкласса, который должен оставаться заблокированным. К сожалению, побочным эффектом является отсутствие растяжимости. Решение с наибольшим количеством голосов Джона Сандерса, вероятно, является лучшей практикой, поскольку оно решает эту проблему.

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

Итак, в заключение я говорю:

  • Продолжайте кодирование, используя защищенный, но помните, что это открывает конфиденциальные данные для подклассов.
  • Наслаждайтесь преимуществами легкой расширяемости, но не забывайте поддерживать отношения «есть» между вашими супер- и подклассами, чтобы поведение каждого из них оставалось верным смыслу метода.
  • Используйте приватную переменную / защищенный метод получения, если вы хардкор или если вам нужна дополнительная уверенность.
0 голосов
/ 13 июля 2009

Сделайте его приватным, пока он не будет защищен. Это проблема абстракции в таких языках, как C #, C ++ и Java, где защищенный / приватный связан с реализацией. Частные члены могут быть автоматически включены автоматически, тогда как защищены не могут. Это похоже на проблему абстракции в C # и C ++ с мономорфизмом и полиморфизмом, требующим виртуального ключевого слова.

Это, по сути, делает хрупкую проблему базового класса. Определение всего защищенного / виртуального является самым безопасным с точки зрения повторного использования, но мешает оптимизации. Определение всего как частного / не виртуального является наиболее эффективным.

На таком языке, как Eiffel, компилятор автоматически определяет, могут ли функции быть встроенными, и автоматически определяет, является ли функция полиморфной или мономорфной. http://dev.eiffel.com

0 голосов
/ 13 июля 2009

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

В примере, который вы упомянули, возможно, интерфейс класса не был определен полностью, потому что вам нужно изменить частное свойство класса. Если у вас есть контроль над кодом, вы можете сделать свойство защищенным или добавить функции для получения и установки значения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...