Зачем языку ОО действительно нужен ЗАЩИЩЕННЫЙ модификатор доступа? - PullRequest
4 голосов
/ 25 ноября 2010

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

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

Ответы [ 7 ]

15 голосов
/ 25 ноября 2010

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

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

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

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

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

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

См. Эту связанную презентацию Джоша Блоха по разработке API.

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

3 голосов
/ 25 ноября 2010

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

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.Это называется шаблоном метода шаблона, и его часто используют.(Извините, что не предоставил пример из реальной жизни. Если хотите, отправляйтесь в Википедию. )

0 голосов
/ 19 ноября 2015

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

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

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);
0 голосов
/ 03 ноября 2012

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

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

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

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

0 голосов
/ 25 ноября 2010

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

0 голосов
/ 25 ноября 2010

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

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

0 голосов
/ 25 ноября 2010

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

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