2010-11-25 2 views
4

Я могу понять, почему существует public и private модификатор доступа, эти два также доступны практически на любом языке. Я даже понимаю, почему может возникнуть модификатор , так как вы можете захотеть, чтобы ваши классы (те, которые тесно связаны друг с другом) взаимодействовали друг с другом таким образом, который не подходит для общественного взаимодействия (например, поскольку это зависит от знание внутренних дел класса, может быть, потому, что оно раскрыло бы секрет, или, может быть, потому, что оно может измениться в любое время, и полагаясь на это, сломает весь существующий код и т. д.). Однако почему я хочу иметь защищенный идентификатор? Не поймите меня неправильно, я знаю, что protected означает, но почему я хочу, чтобы подклассы моих классов обращались к некоторым переменным экземпляра или использовали определенные методы, просто потому, что они являются подклассами и даже если они являются частью другого пакета? Что такое реальный случай использования в мире для protected?Почему для языка OO действительно нужен PROTECTED-модификатор доступа?

(А выступление в качестве аргумента для переменных экземпляра не учитывается, так как JIT компилятор может всегда встроенные методы доступа, уменьшая затраты на вызов к нулю)

+2

Просто пролистать несколько классов из Javadoc, и вы увидите кучу «реальных случаев использования» :) – zneak

+0

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

ответ

15

Государственные методы являются частью открытого интерфейса. Частные методы - внутренние. Защищенные методы - это точки расширения.

С помощью protected вы можете переопределить функционирование класса, переопределив его, не сделав этот метод частью открытого интерфейса.

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

Например, в рамке коллекции java есть класс AbstractList. Он защищен modCount поля и защищенный removeRange метод:

  • используется modCount поля (увеличиваются) на все подклассы, чтобы подсчитать количество модификаций. Iterator возвращаемый AbstractList делает использование этого поля

  • метод removeRange могут быть повторно использованы подклассами, вместо того, чтобы их определить его снова.

See this Соответствующая презентация Джоша Блоха по дизайну API.

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

+0

Почему бы не подклассу не было позволено запрашивать количество изменений? Что такое «зло» в этой информации? И почему бы остальному миру не было позволено делать вызов removeRange? – Mecki

+0

количество модификаций имеет значение только для Итератора, созданного абстрактным списком. Остальной мир не должен называть removeRange - я не знаю. Кажется, я помню, как Блох упоминал что-то об этом в лекции, но не может его найти. – Bozho

+0

Тот факт, что часть информации не интересна остальному миру, не является аргументом в пользу того, что доступ к ней не позволяет остальному миру. На самом деле, поскольку это не интересно, остальной мир, вероятно, никогда не получит к нему доступа, даже если бы мог. И что мешает остальному миру просто подклассифицировать мой подкласс AbstractList, использовать этот под-подкласс и предлагать эту информацию с помощью другого метода? – Mecki

-1

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

+0

Но у вас нет причины, по которой я не хочу делиться ею с остальной частью приложения? И если я не хочу делиться этим с остальными, почему бы не использовать для этого «пакет»? (где пакет можно рассматривать как «семью», чтобы следовать вашему образцу) – Mecki

+0

Я даже не получаю аналогию. Это аналогия, не так ли? – Teekin

+0

@Helgi @Mecki аналогия заключается в следующем: «пакет» означает жить в одном и том же месте, «защищенный» означает семью. Есть что-то, что вы поделитесь с семьей, но не с вашим соседом. И «семья» не означает жить в одном месте (т. Е. В одном пакете). Надеюсь, на этот раз я хорошо объяснил :) – khachik

0

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

Это говорит, что я говорю, что мои ученики «защищены, это список дел». Потому что, если вы меняете что-либо защищенное, вам нужно искать, кто полагается на него. Поэтому будьте действительно уверены в чем-то, прежде чем вы откроете его неизвестным продлетелям будущего.

+0

Почему я разрешаю расширителям делать это, но остальной мир нет? Что делает экстендеры, которые являются частью остального мира, отличного от ... ну ..., «остального мира»? – Mecki

+0

Вы писали, скажем, BankAccount. Вы думаете, что кто-то может написать SavingsAccount или TaxProtectedAccount, и для этого кода в этих классах может потребоваться изменить лимит баланса или овердрафта или другие атрибуты из базового класса. –

3

Наиболее частое использование, которое я вижу, - это позволить суперклассам использовать внутренние подклассы. Рассмотрим это:

class Foo 
{ 
    private int[] array = new int[] { 4, 3, 2, 1 }; 

    public void processAllElements() 
    { 
     for (int i = 0; i < array.length; i++) 
      processElement(array[i]); 
    } 

    protected abstract void processElement(int i); 
} 

class Bar 
{ 
    protected void processElement(int element) 
    { 
     System.out.println(element); 
    } 
} 

В этом случае, это Foo, который необходимо использовать защищенный элемент Bar, а не наоборот. Если вы хотите, чтобы ваш суперкласс получил доступ к логике подкласса, но не хотите, чтобы его публично открывали, у вас нет выбора, кроме модификатора protected. Это называется шаблоном шаблона метода, и он часто используется. (К сожалению, не обеспечивающих реального мира пример. Head to Wikipedia if you want some.)

+0

В этом примере, что было бы так плохо в том, чтобы сделать processElement общедоступным? Это что-нибудь сломает? Выяснить любую секретную информацию? Если вы позволяете программистам из внешнего мира подклассифицировать ваш класс, информация все равно не является секретной (поскольку вы хотите, чтобы люди переопределили ее). – Mecki

+0

@Mecki Это тривиальный пример. Ничего плохого не случится, если оно будет публичным. Однако этот метод может иметь дело с внутренними системами таким образом, чтобы вы предпочли, чтобы он был непубличным. И поскольку он не может быть закрытым, потому что ему нужен суперкласс, вы остаетесь только с защитой. – zneak

+0

@Mecki Если вы понимаете необходимость «частных» методов, я уверен, что вы можете понять, почему методы шаблонов не всегда могут быть «публичными». Если бы не было наследования, такие методы были бы «частными». Но так как вам это нужно от суперкласса, вам нужно «защищать». – zneak

0

Тот факт, что языки существуют, которые не имеют модификатор protected доступа, и даже языки, которые не имеют модификаторы доступа на всех где все методы являются публичными, и эти языки, как правило, считаются одними из «самых чистых» языков OO, показывает, что вы действительно, не «действительно нужен» protected модификатор доступа.

0

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

Некоторые люди могут нарушить идею класса, не разрешающего доступ к защищенным членам, выставленным его родителем, но такое поведение никоим образом не является нарушением Принципа замещения Лискова. LSP заявляет, что если код может получить объект производного типа, когда он ожидает объект базового типа, объект производного типа должен иметь возможность делать что-либо, что может сделать объект базового типа. Таким образом, если Moe публично поддерживает функцию и тип Larry, который происходит от Moe, код, который принимает параметр типа Moe и пытается использовать эту функцию, потерпит неудачу, если ему было дано Larry; такой отказ будет представлять собой нарушение LSP.

Предположим, однако, что Moe содержит функцию protected, которую Larry не поддерживает. Единственными классами, которым будет разрешено использовать эту функцию, будут либо те, которые непосредственно производятся от Moe, либо от потомков Moe, которые поддерживают его. Если Curly происходит от Moe, он может использовать эту функцию, не беспокоясь о том, поддерживают ли все подтипы Moe, потому что базой Curly не будет какой-либо произвольный объект, который является либо экземпляром Moe, либо производным (и может или может не поддерживает функцию) - это будет Moe, период.

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

0

Я лично использую защищенный модификатор, когда хочу: а) устранить шум в публичном контракте, но б) также совместно использовать функциональные возможности с производными классами, в то время как в свою очередь в) писать код, который является СУХОЙ, а также помнить о единой ответственности принцип. Звучит типично, я уверен, но позвольте мне привести пример.

Здесь мы имеем базовый интерфейс обработчика запросов:

public interface IQueryHandler<TCommand, TEntity> 
{ 
    IEnumerable<TEntity> Execute(TCommand command); 
} 

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

public abstract class CachedQueryHandler<TCommand, TEntity> 
    : IQueryHandler<TCommand, TEntity> 
{ 
    public IEnumerable<TEntity> Execute(TCommand command) 
    { 
     IEnumerable<TEntity> resultSet = this.CacheManager 
      .GetCachedResults<TEntity>(command); 

     if (resultSet != null) 
      return resultSet; 

     resultSet = this.ExecuteCore(command); 
     this.CacheManager.SaveResultSet(command, resultSet); 

     return resultSet; 
    } 

    protected abstract IEnumerable<TEntity> ExecuteCore(TCommand command); 
} 

CachedQueryHandler не собирается ни для кого другого, чтобы вызывать метод ExecuteCore непосредственно. Также не важно, как выполняется запрос. Защищенный модификатор идеально подходит для такого сценария.

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

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

public class DatabaseWidgetQueryHandler : CachedQueryHandler<WidgetCommand, Widget> 
{ 
    protected override IEnumerable<Widget> ExecuteCore(WidgetCommand command) 
    { 
     return this.GetWidgetsFromDatabase(); 
    } 
} 

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

IQueryHandler<WidgetCommand, Widget> widgetQueryHandler; 
var widgets = widgetQueryHandler.Execute(myWidgetCommand); 
Смежные вопросы