2013-06-20 3 views
4

Какая правильная инкапсуляция из ниже 2 классов в java?. Я видел оба этих элемента во многих кодах (в основном, 1-й вариант). Но похоже, что второй подход правильный.Какова правильная инкапсуляция в типы объектов java

import java.util.Date; 

public class SomeClass 
{ 
    private Date date; 

    public Date getDate() 
    { 
     return date; 
    } 

    public void setDate(Date date) 
    { 
     this.date = date; 
    } 
} 

или

import java.util.Date; 

public class SomeClass 
{ 
    private Date date; 

    public Date getDate() 
    { 
     return (Date) date.clone(); 
    } 

    public void setDate(Date date) 
    { 
     this.date = (Date) date.clone(); 
    } 

} 
+0

Первый более условный. – vikingsteve

+0

обычный, но не обязательно лучше. рассмотрите мое сообщение ниже :) – mel3kings

ответ

5

это зависит от того, является ли тип поля вы получаете/набор immutable или нет, то есть, если они могут быть изменены после их строительства.

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

Так, в первом примере, класс может получить ссылку на частную Дату-поля, а затем (с Date не неизменно) использовать setTime(long) метод Дейта для изменения даты, фактически минуя сеттер метода SomeClass (и любые побочные эффекты, которые могут иметь, например, выполнение проверки, обновление элемента GUI и т. д.).
В вашем втором примере это невозможно сделать, поскольку внешний класс будет получать только клон фактической даты, поэтому любые сделанные после этого изменения не повлияют на исходное личное поле даты SomeClass.

Нижняя линия:
Все зависит от типа ваших частных/защищенные поля и то, что вы пытаетесь достичь/предотвратить.


Очки иметь в виду:

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

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

1

Это зависит от кода, который использует SomeClass. Если вы планируете включить этот класс в библиотеку, используемую третьей стороной, или ваш код взаимодействует/запускает код, который вы не контролируете, вам абсолютно необходима инкапсуляция последней формы.

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

Хотя это, как правило, приводит к чему-то, как это для меня:

public class MyClass { 
    private Date myDate; 

    public Date getDate() { 
    return myDate != null ? new Date(myDate.getTime()) : null; 
    } 
    public void setDate(Date date) { 
    myDate = (date != null ? new Date(date.getTime()) : null); 
    } 
} 

Так ИМХО, я бы использовал второй вариант или переключиться на Jodas DateTime.

2

Первый! Второй будет создавать новый объект с clone() для каждого звонка getDate(), это может быть смущающим, в зависимости от вашего приложения. (То есть, если вы хотите вызвать Date метод с aSomeDate.getDate().aMethod().)

Немного образцов, чтобы понять мой бедный английский;)

public class Main { 
    public static void main(String[] args) { 
    SomeDate sd = new SomeDate(new Date(1991, 3, 3)); 
    System.out.println(sd); 
    sd.getDate().setYear(2012); 
    System.out.println(sd); 
    } 
} 
0

Не для этого конкретного примера, но для общего подхода:

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

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

Таким образом, вы предоставляете более общий объект, или я могу сказать abstraction и поощряю polymorphism.

3

Это вопрос предпочтения безопасности/инкапсуляции, самая простая инкапсуляция - это первый подход, который вы опубликовали, второй, однако, является более совершенным способом инкапсуляции. Это защищает также объекты, передаваемые классу путем клонирования.

Рассмотрим следующий пример:

public class SomeData { 
private final Point value; 
    public SomeData (final Point value) { 
    this.value = value; 
    } 
    public Point getValue() { 
    return value; 
    } 
} 

теперь выше фрагмент выглядит неизменны (аналогично вашему примеру) Однако есть зияющая дыра в этом. ознакомьтесь с приведенным ниже фрагментом.

public final static void main(final String[] args) { 
    Point position = new Point(25, 3); 
    SomeData data = new SomeData(position); 
    position.x = 22; 
    System.out.println(data.getValue()); 
    } 

Поскольку мы передаем только ссылку на переменную позиции, мы все еще можем изменить ее состояние.Клонирование это поможет защитить переменные позиции:

если мы изменим декларацию от этого:

public SomeData (final Point value) { 
     this.value = value; 
     } 

к (похожей на ваше клонирование)

public SomeBetterData(final Point value) { 
    this.value = new Point(value); 
    } 

, когда мы снова вызвать основной метод :

Point position = new Point(25, 3); 
     SomeData data = new SomeData(position); 
     position.x = 22; 

data объект останется неизменным независимо от того, что мы делаем с position. Надеюсь, вы поняли, почему там клонирование.

+1

+1 для того, чтобы дать пример изменчивости. Также обратите внимание: http://www.ibm.com/developerworks/library/j-jtp02183/ – mabi

+1

Также обратите внимание, что вы все равно можете использовать 'getValue()' для получения ссылки на частную точку 'SomeBetterDate', а затем снова модифицируйте его произвольно (например, используя 'data.getValue(). x = 22'). – gkalpak

+0

Это правильно, спасибо за дополнительную информацию. – mel3kings

4

Не очень хорошая идея clone, как -

  1. Вообще clone метод объекта, создает новый экземпляр того же класса и копирует все поля в новый экземпляр и возвращает его = неполную копию. Object класс обеспечивает метод клонирования и обеспечивает поддержку для shallow copy. Он возвращает «Object» как тип, и вам нужно явно указать cast back вашему исходному объекту.

  2. Когда вы выполняете глубокую копию, будьте осторожны, так как вы можете упасть на cyclic dependencies.

  3. Клон не предназначен для instantiation и initialization. Его нельзя воспринимать как создание нового объекта. Поскольку конструктор клонированных объектов никогда не может быть вызван в процессе.

4.One более недостаток (и многие другие .. :)), клон предотвращает использование final полей.

5. В шаблоне Singleton, если superclass реализует метод public clone(), чтобы ваш подкласс от использования clone() метода этого класса, чтобы получить копию переписать его и бросить CloneNotSupportedException

Итак, подхода 1 является лучше чем подхода 2

4

Две основная особенности инкапсуляции являются:

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

Это всегда хорошая практика, чтобы создать defensive copy в изменяемом объекте в любое время оно передается в Constructors и set методов или из get методов в классе. Если это не сделано, то для вызывающего может просто нарушить инкапсуляцию, изменив состояние объекта, который одновременно отображается как для класса, так и для его вызывающего. Кроме того, не используйте метод clone для создания защитной копии параметра (изменяемого объекта), тип которого является подклассом ненадежными сторонами, поскольку это может привести к преднамеренному или непреднамеренному изменению внутреннего состояния переменной экземпляра.
Исходя из всех этих правил, ни один из ваших подходов не является правильным.

Таким образом, правильный способ следовать инкапсуляции в вашем коде:

import java.util.Date; 

public class SomeClass 
{ 
    private Date date; 

    public Date getDate() 
    { 
     return new Date(date.getTime()); 
    } 

    public void setDate(Date date) 
    { 
     this.date = new Date(date.getTime()); 
    } 

} 
2

Вторые пытается обеспечить соблюдение оборонительного Копирования. Рассмотрим класс Period, который хранит две даты и первый должен быть перед вторым:

public class Period { 
    private Date first, second; 

    public Period(Date first, Date second) { 
     if(first.compareTo(second) > 0) 
      throw new IllegalArgumentException("first > second"); 

     this.first = first; 
     this.second = second; 
    } 

    public Date getFirst() { 
     return first; 
    } 

    public Date getSecond() { 
     return second; 
    } 
} 

На первый взгляд, это звучит невзламываемой, но посмотрите:

Date d1 = new Date(), d2 = new Date(); 
Period p = new Period(d1, d2) // no exception 

d1.setYear(98); // broken period precondition 

Чтобы решить эту проблему - происходит защитное копирование, то есть внутренний экземпляр не может быть изменен исходным параметром.Хотя ваш второй подход будет работать, он все еще взломать, как подкласс может переопределить clone() и сохранить созданы все новые экземпляры ...

Правильный путь будет:

this.first = new Date(first.getTime()); 

и возвращения заявления:

return new Date(first.getTime()); // here you may use clone.... 

Таким образом, подкласс не может получить руку на внутренности.

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