Java нысан түрлерінде дұрыс инкапсуляция дегеніміз не?

Явадағы 2 сыныптан төмен дұрыс инкапсуляция дегеніміз не? Бұл кодты екі кодты да көрдім (негізінен 1-ші жақ). Бірақ 2-ші тәсіл дұрыс деп ойлаймын.

impнемесеt java.util.Date;

public class SomeClass
{
    private Date date;

    public Date getDate()
    {
        return date;
    }

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

немесе

impнемесеt 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();
    }

}
4
дәстүрлі, бірақ міндетті түрде жақсы емес. менің жазбамды төменде қарастырайық :)
қосылды автор mel3kings, көзі
Біріншісі - дәстүрлі.
қосылды автор vikingsteve, көзі

8 жауаптар

Ол сіз алатын/орнатқан өрістің түрін тәуелді: өзгермейтін немесе жоқ, яғни егер олар құрылысы аяқталғаннан кейін өзгертілсе.

Барлық Getter/Setter парадигмасының артында, мысалы, дананың жеке/қорғалған өрістері кез-келген сыртқы класқа еркін түрде (немесе кіруге рұқсат етілмеген) мүмкін емес.

Мәселен, бірінші мысалда, Класс жеке Дата-өрісіне сілтеме жасай алады, содан кейін ( Date өзгермейтіндіктен) Күннің setTime (long) әдісін қолдануға болады SomeClass әдісінің Setter әдісін айналып өтетін Күнді өзгертіңіз (және ол кез-келген жанама әсерлер болуы мүмкін, мысалы, тексеруді орындау, GUI элементін жаңарту және т.б.). Сіздің екінші мысалыңызда бұл мүмкін емес, себебі сыртқы класс нақты күннің клонын ғана сатып алады, сонан кейін кез-келген модификациялар түпнұсқа жеке күн-ай-күніне әсер етпейді SomeClass .

Bottom line:
It all depends on the type of your private/protected fields and what you are trying to achieve/prevent.


Ұстау керек:

  • clone() әрдайым Объектінің терең клонын қайтармайды (әсіресе, өріс сілтемесі басқа да өзгермейтін нысандар және т.б.). Сондықтан ол оны ұқыпты пайдалану керек (және оның ішкі жұмыс туралы хабардар болу).

  • Бастапқы деректер түрлері мен Strings өзгермейді, сондықтан осы түрлердің өрістерін алу/орнату кезінде clone() керек болмайды.

5
қосылды

clone ретінде жақсы идея емес -

  1. Объекттің жалпы clone әдісі бір сыныптың жаңа данасын жасайды және барлық өрістерді жаңа данаға көшіреді және оны = ұсақ көшірмені қайтарады. Object класы клон әдісін ұсынады және ұсақ көшірме үшін қолдау көрсетеді. Ол «Нысан» түрін қайтарады және сіз өзіңіздің бастапқы объектіңізге <�кодты қайтару сөзін қажет етеді.

  2. Сіз терең көшірмені мұқият орындағанда, циклдік тәуелділіктерге түсіп кетуіңіз мүмкін.

  3. Clone даналық және баптандыру үшін емес. Бұл жаңа нысан құру сияқты қабылданбауы керек. Себебі клондалған нысандардың конструкторы ешқашан процеске шақырылмауы мүмкін.

4.Осы кемшілігі (және көптеген басқа .. :)), клондау final өрістер.

5 . In a Singleton pattern, if the superclass implements a public clone() method, to prevent your subclass from using this class’s clone() method to obtain a copy overwrite it and throw a CloneNotSupportedException

So, Approach 1 is better than Approach 2

4
қосылды

Инкапсуляцияның екі негізгі ерекшелігі:

      
  • Мысал айнымалы мәндерді қорғаңыз (қатынас модификаторымен, жиі жеке).
  •   
  • Көпшілікке кіру әдістерін жасаңыз және қоңыраулар кодын мәжбүрлі айнымалы айнымалыға тікелей қатынауға емес, сол әдістерді қолдануға мәжбүрлеңіз
  •   

It is always a good practice to create defensive copy of the mutable object any time it is passed into Constructors and set methods or out of get methods in the class. If this is not done, then it is simple for the caller to break encapsulation, by changing the state of an object which is simultaneously visible to both the class and its caller. Also, Don't use the clone method to make defensive copy of a parameter(mutable object) whose type is subclassable by untrusted parties as this may lead to intentional or unintentional change in the internal state of the instance variable.
Going by all these rules, none of your approach is correct.

Демек, сіздің кодыңыздағы инкапсуляцияны дұрыс жолмен сақтау:

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());
    }

}
4
қосылды

This is a matter of security/encapsulation preference, the most basic encapsulation is the 1st approach you posted, the second however is a more advanced way of encapsulation. This protects also the objects being passed on to the class by cloning it.

Төмендегілерді қарастырайық:

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;
      }

to (similar to your cloning)

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

біз қайтадан негізгі әдісті шақырамыз:

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

position нысанына қарамастан, data </​​code> нысанына қарамастан, өзгеріссіз қалады. Деп үміттенемін, онда неге клондау бар.

3
қосылды
+1 Мысалсыздық үлгісін беру үшін. Қараңыз: ibm.com/developerworks/library/j-jtp02183
қосылды автор mabi, көзі
бұл қосымша ақпарат үшін рақмет.
қосылды автор mel3kings, көзі
Сондай-ақ, someBetterDate жеке нүктесіне сілтеме алу үшін getValue() дегенді қолдануға болады және одан кейін оны өздігінен өзгертуге болады (мысалы, data.getValue() .x = 22 ).
қосылды автор gkalpak, көзі

Екінші Қорғалған көшіру қолдануға тырысады. 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....

Осылайша, ішкі сыныптың ішкі құрамына қол жеткізуге ешқандай мүмкіндік жоқ.

2
қосылды

Біріншісі ! Екіншіден, әрбір getDate() қоңырауы үшін clone() деген жаңа нысан жасалады, ол сіздің қолданбаңызға байланысты ыңғайсыз болуы мүмкін. (мысалы, aSomeDate.getDate ()) Date әдісін шақырғыңыз келсе, 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);
  }
}
2
қосылды

Бұл 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); 
  }
}

Мәселен, IMHO, мен екінші нұсқаны қолданамын немесе Jodas-ге DateTime өтіңіз.

1
қосылды

Бұл нақты мысал үшін емес, жалпы көзқарас үшін:

Пайдаланушыға қайтарылған нысанды қалаған кез келген үйлесімді сыныпқа беру тәсілі әрқашан жақсы.

Сондықтан бірінші әдіс жақсы көрінеді, себебі сіз қайтаратын зат басқа бір сыныпта кеңейтілсе, тіркелген түрдегі объектіні беруден гөрі оны осы түрге айналдыру оңай болады.

Сонымен, сіз неғұрлым жалпы нысанды қамтамасыз етсеңіз немесе abstraction деп айта аламын және polymorphism деп үміттенемін.

0
қосылды