2014-12-31 4 views
1

«Создание поля класса/свойства private» - это одна из лучших практик в ООП. Чтобы получить доступ к полю private, мы можем использовать общедоступный метод getter. Но только для записи метода getter может быть недостаточно в соответствии с this blog(Please see Quote 2). Он предлагает вернуть клон поля/свойства (если он является ссылочным типом) из метода getter. Следуя этому подходу, мы можем сделать поле/свойство немодифицируемым. Пожалуйста, смотрите ниже код -Наилучшее приближение возвращаемого значения от метода «getter»

public class Department{ 
Employee admin; 
... 
... 
... 
public Employee getAdmin(){ 
    return admin.clone(); 
} 
} 

Так что теперь в 'admin' является неизменяемым внешним миром, если мы не используем сеттер. Этот подход выглядит довольно неплохо. Но я почти не нашел кода, который реализует этот подход. Существуют ли какие-либо недостатки в использовании этого подхода, которые могут испортить преимущество использования этого подхода?
Спасибо заранее.

+1

Лучшая практика, чтобы избежать поглотитель вообще, если это возможно;) в 99% случаев, это возможно в отношении закона Деметры при работе с объектами предметной области. – Mik378

+2

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

+1

@ Mik378: Я думаю, что это слишком широко, лично. Существует много случаев, когда тип * не должен * знать все, что может быть сделано с его составными частями, а обнародование данных упрощает жизнь. Разве разоблачение администратором ведомства каким-либо образом нарушает инкапсуляцию? Для меня это не похоже. –

ответ

5

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

Есть два подхода, которые достигают подобный эффект без клонирования:

  • Making Employee неизменен и
  • Давать Employee неизменного интерфейс

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

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

public interface Employee { 
    String getFirstName(); 
    String getLastName(); 
} 
public class EmployeeImpl implements Employee { 
    private String firstName; 
    private String lastName; 

    public String getFirstName() {return firstName;} 
    public String getLastName() {return lastName;} 
    public void setFirstName(String name) {firstName = name;} 
    public void setLastName(String name) {lastName = name;} 
} 

Этот подход может быть побежден путем литья в класс, но большую часть времени он в порядке.

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

3

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

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

Альтернатива для вашего конкретного случая - иметь интерфейс под названием Employee, который предлагает только геттеры. Затем вы также можете определить класс MutableEmployee, который реализует Employee, а также предлагает сеттеры.

interface Employee { 
    public String getName(); 
    public float getSalary(); 
} 

class MutableEmployee implements Employee { 
    private String name; 
    private float salary; 

    @Override 
    public String getName() { 
     return name; 
    } 

    @Override 
    public float getSalary() { 
     return salary; 
    } 

    public void setSalary(float salary) { 
     super.salary = salary; 
    } 

    public void setName(String name) { 
     super.name = name; 
    } 
} 

Ваш Department класс будет модифицируется следующим образом:

class Department { 
    private MutableEmployee admin; 

    public Employee getAdmin() { 
     return (Employee) admin; 
    } 

    public MutableEmployee getMutableAdmin() { 
     return admin; 
    } 
} 

Определение подклассов MutableEmployee

Если вы хотите создать подкласс MutableEmployee, как MutableGraphicDesigner, вам нужно определить следующее:

  1. interface GraphicDesigner extends Employee
  2. class MutableGraphicDesigner extends MutableEmployee implements GraphicDesigner
+1

@dasblinkenlight Я изменил ответ, потому что понял, что наследование было проблематичным с решением, которое я предлагал. Теперь это в основном то же самое, что и ваш собственный ответ. –

Смежные вопросы