Поиск…


Вступление

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

замечания

Гораздо проще начать с маркировки переменной private и разоблачить ее, если необходимо, чтобы скрыть уже public переменную.

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

public class DumbData {
    public String name;
    public int timeStamp;
    public int value;
}

В этом случае интерфейс класса - это данные, которые он хранит.

Обратите внимание, что переменные, помеченные как final могут быть помечены как public не нарушая инкапсуляцию, поскольку они не могут быть изменены после установки.

Инкапсуляция для сохранения инвариантов

Есть две части класса: интерфейс и реализация.

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

Реализация - это внутренние действия класса. Другие классы не должны знать о реализации класса.

Инкапсуляция относится к практике скрытия реализации класса от всех пользователей этого класса. Это позволяет классу делать предположения о его внутреннем состоянии.

Например, возьмите этот класс, представляющий угол:

public class Angle {
    
    private double angleInDegrees;
    private double angleInRadians;
    
    public static Angle angleFromDegrees(double degrees){
        Angle a = new Angle();
        a.angleInDegrees = degrees;
        a.angleInRadians = Math.PI*degrees/180;
        return a;
    }
    
    public static Angle angleFromRadians(double radians){
        Angle a = new Angle();
        a.angleInRadians = radians;
        a.angleInDegrees = radians*180/Math.PI;
        return a;
    }
    
    public double getDegrees(){
        return angleInDegrees;
    }
    
    public double getRadians(){
        return angleInRadians;
    }
    
    public void setDegrees(double degrees){
        this.angleInDegrees = degrees;
        this.angleInRadians = Math.PI*degrees/180;
    }
    
    public void setRadians(double radians){
        this.angleInRadians = radians;
        this.angleInDegrees = radians*180/Math.PI;
    }
    private Angle(){}
}

Этот класс основан на базовом предположении (или инварианте ): angleInDegrees и angleInRadians всегда синхронизированы . Если бы члены класса были общедоступными, не было бы никаких гарантий того, что оба представления углов будут скоррелированы.

Инкапсуляция для уменьшения сцепления

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

Например, давайте изменим реализацию класса Angle из предыдущего примера:

public class Angle {
    
    private double angleInDegrees;
    
    public static Angle angleFromDegrees(double degrees){
        Angle a = new Angle();
        a.angleInDegrees = degrees;
        return a;
    }
    
    public static Angle angleFromRadians(double radians){
        Angle a = new Angle();
        a.angleInDegrees = radians*180/Math.PI;
        return a;
    }
    
    public double getDegrees(){
        return angleInDegrees;
    }
    
    public double getRadians(){
        return angleInDegrees*Math.PI / 180;
    }
    
    public void setDegrees(double degrees){
        this.angleInDegrees = degrees;
    }
    
    public void setRadians(double radians){
        this.angleInDegrees = radians*180/Math.PI;
    }

    private Angle(){}
}

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

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



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow