Защищенные данные в абстрактном классе

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

Теперь я понимаю, что мы хотим защитить данные от непосредственных манипуляций со случайными пользователями класса, и что публичные данные в целом являются сомнительной практикой. Я посмотрел "Защищенные поля Java vs public getters" (защищенные Java-поля vs public getters), но я все же сомневаюсь, что:

protected int i;  

хуже в абстрактном классе, чем:

private int i;  
protected int geti();  
protected void seti(int j); 

Я просто не вижу нисходящую сторону, когда абстрактный класс существует именно для предоставления родительского/общего объекта для классов children, а защищенная область предназначена для обеспечения доступа к детям, одновременно защищая данные от случайных пользователей. Я отмечаю в упомянутом выше вопросе, что большинство ответов, по-видимому, затрагивают вопрос о том, почему данные в целом должны быть частными, а не публичными. Я пытаюсь сосредоточить свой вопрос конкретно на данных, существующих в абстрактном родителе, предназначенном для использования детьми. Единственный разумный комментарий, который я слышал до настоящего времени, заключается в том, что использование защищенных данных родителей (например, int я выше) оставляет вас с кодом в дочернем классе, который ссылается на переменную, не объявленную в дочернем классе. Менее убедительным является аргумент (см. Общий защищенный элемент данных в базовом классе?), который вы, возможно, захотите каким-то образом изменить доступ, и теперь вы должны соблюдать свой интерфейс. Это абстрактный класс и предназначен для продления в 100% случаев.

Спасибо! Специфические заголовки/страницы # ссылки на книги гораздо полезнее, чем ссылки на "..ано базовый текст программирования Java..."

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

Спасибо всем, кто внес вклад.

Ответы

Ответ 1

Если поле является частным, а доступ осуществляется через геттеры и сеттеры, вы сможете переопределить получатели и сеттеры (например, удаление поля и обновление/чтение значения из внешнего источника), и, таким образом, поле "работает, не касаясь каких-либо дочерних классов.

Стоит ли это того, что до вас.

Ответ 2

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

Предоставление доступа позволяет базовому классу поддерживать свое состояние: ни один из подклассов не будет повреждать его без преднамеренного трюка.

Ответ 3

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

Ответ 4

Если вам не нужен ваш ребенок для прямого доступа к нему, почему вы позволите им?

Незащищенная защита не защищена. Но если это не обязательно, возможно, лучше избегать этого и контролировать доступ к вашим полям.

Ответ 5

В Java защищенные члены доступны всем членам в одном пакете в дополнение к любым расширяющимся классам. Закрытие поля будет препятствовать прямому доступу к ним в одном пакете.

Кроме того, существует точка, которая была поднята ранее.

Ответ 6

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

Кроме этого, это действительно зависит от вас. Согласие состоит в том, что есть все, что есть, и убиратели.

Ответ 7

Информационное скрытие ценно даже среди классов, связанных с наследованием.

В дополнение к разрешению повторной реализации, как указано выше:

  • Вы можете установить точки останова в методах.
  • Вы можете добавить ограничения в одном месте.

Ответ 8

Вы хотите использовать геттеры/сеттеры, поскольку использование protected int i; позволяет переопределить поле (которое вы хотите избежать любой ценой).

Вы хотите запретить переопределение поля, потому что оно работает иначе, чем переопределение метода. Превышение поля не делает переопределенное поле недоступным (тип ссылки определяет, какой экземпляр поля, с которым вы работаете).

Доступные поля должны быть окончательными или в конечном классе.

public class OverridingFun {
    public static class Base {
        public int i = 1;
        public int getI(){ return i; }
    }
    public static class A extends Base {
        public int i = 2;
        public int getI(){ return i; }
    }
    public static class B extends A {
        public int i = 3;
        public int getI(){ return i; }
    }
    public static void main(String [] args){
        B b = new B();
        A bAsA = b;
        Base bAsBase = b;

        System.out.println(b.getI());//3
        System.out.println(bAsA.getI());//3
        System.out.println(bAsBase.getI());//3

        System.out.println(b.i);//3
        System.out.println(bAsA.i);//2
        System.out.println(bAsBase.i);//1

        b.i = 4;
        bAsA.i = 5;
        bAsBase.i = 6;

        System.out.println(b.i);//4
        System.out.println(bAsA.i);//5
        System.out.println(bAsBase.i);//6
    }
}

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

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

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