Bir sınıfın yöntemleri kendi alıcılarını ve ayarlayıcılarını mı çağırmalı?


53

Çalıştığım yerde böyle şeyler yapan birçok sınıf görüyorum:

public class ClassThatCallsItsOwnGettersAndSetters {

    private String field;

    public String getField() {
        return field;
    }

    public void setField(String field) {
        this.field = field;
    }

    public void methodWithLogic() {
        setField("value");
        //do stuff
        String localField = getField();
        //do stuff with "localField"
    }
}

Bunu sıfırdan yazsaydım methodWithLogic()bunun yerine böyle yazmış olurdum :

public class ClassThatUsesItsOwnFields {

    private String field;

    public String getField() {
        return field;
    }

    public void setField(String field) {
        this.field = field;
    }

    public void methodWithLogic() {
        field = "value";
        //do stuff            
        //do stuff with "field"
    }
}

Sınıf kendi alıcılarını ve ayarlayıcılarını aradığında, kodu okumayı zorlaştırdığını hissediyorum. Bana göre bu, neredeyse bizim asla olmadığımız halde, bu yöntem çağrısında karmaşık bir mantığın gerçekleştiğini ima ediyor. Bilmediğim bir kodun hatalarını ayıklarken, hatanın bu yöntemde bazı yan etkiler olmadığını kim söyleyebilir? Başka bir deyişle, bu kodu anlama yolculuğumda birçok yan seyahatime çıkmamı sağlıyor.

İlk yöntemin faydaları var mı? İlk yöntem gerçekten daha mı iyi?


Bilmediğim bir kodun hatalarını ayıklarken, hatanın bu yöntemde bazı yan etkiler olmadığını kim söyleyebilir? Biriminiz test ediyor. :)
Joshua Taylor

1
Dahili kodunuzda alıcı / ayarlayıcı kullanmak, bir hata ayıklayıcıyla geçiyorsanız kodunuzda bir kesme noktası oluşturmanıza olanak sağlar. Ayrıca, ayar / değer almayla ilgili herhangi bir sorun çıkarırsa, bunun geçersiz olduğundan değil, bu yöntemden kaynaklandığından emin olmanızı sağlar. Genel olarak, daha fazla tutarlılık kodu sağlar.
callyalater, 13.06.2016

Yanıtlar:


46

Hangisinin daha iyi ya da daha kötü olduğunu söylemeyeceğim, çünkü bu kısmen sizin durumunuza bağlı (bence). Ancak alıcılarınızın ve belirleyicilerinizin daha sonra uygulamayı değiştirebileceğini ve bunları atlamanın bu mantığı atlayacağını düşünün.

Örneğin, bazı ayarlayıcı alanlara daha sonra "kirli" bayrak eklerseniz ne olur? Ayarlayıcılarınızı dahili kodunuzda arayarak kirli bayrağı başka bir kod değiştirmeden ayarlayabilirsiniz. Birçok durumda bu iyi bir şey olurdu.


Ancak, arka uçtaki verileri başlatırken ve bunun kirli olarak yorumlanmasını istemezseniz ne olur? Aradığınız Eğer setField()baştan sadece bir hata tanıttı.
Daniel Kaplan

6
Tamam, bu yüzden kısmen duruma bağlı olduğunu söylüyorum. Belki veri başlatma ayarlayıcıları atlamalı, fakat diğer mantıksal kodun atlamaması gerekiyor. Uygun olan kuralları seç ve ona uy.
Matt S

3
@DanielKaplan: Önemli olan, alıcıları ve ayarlayıcıları çağırmanın çoğu durumda yapılacak doğru şey olduğu ve yalnızca belirli durumlarda yapmadığıdır. Bununla birlikte, gerçek dünya kodunda, bazı kasıtlı yan etkileri ortaya çıkarmak için daha sonra mevcut bir alıcı / alıcı uygulamasını değiştirdiğinizde, muhtemelen sınıf içindeki alıcı veya alıcıya yapılan her çağrıyı ve ayrıca telefona doğrudan her erişimi kontrol etmeniz gerekir. alan. Bu yüzden sınıflarınızı mümkün olduğunca küçük tutmaya çalışmalısınız.
Doktor Brown

33

Ayarlayıcıyı doğrudan çağırmak kendi başına bir sorun değildir. Asıl soru şu olmalı: Neden kodumuzda her yerde belirleyiciler var?

Değişken sınıflar, özellikle sınıfın kendi durumunu yönettiği durumlarda tehlikeli bir oyundur. Bu belirleyicilerin kaç tanesinin gerçekten olması gerektiğini düşünün. Kurucuda kaç tane belirlenebilir ve daha sonra tamamen sınıfın kendisi tarafından kapsüllenebilir?

Alan harici olarak ayarlanabiliyorsa, kendinize mantıklı bir yöntemin orada olup olmadığını sorun. Sınıfınızın kendisi gerçekten sadece bir veri / durum sınıfı mı? Bu sınıfın birden fazla sorumluluğu var mı?

Beni yanlış anlama, bunun iyi olduğu durumlar olacak. Tamamen mantıklı olan sınıflardaki belirleyicileri ortadan kaldırmanız gerektiğini söylemiyorum. Ancak bunlar kural değil istisna olmalı. Ve bu birkaç durumda, kimin doğrudan erişebileceği açık olmalıdır.


1
Bu düşünce biçimine tamamen katılıyorum / pratik yapıyorum. Ayrıca benim sorumdan daha önemli bir noktaya geldiğinizi düşünüyorum. Bu, asıl soruma doğrudan cevap verdiğini sanmıyorum. Söylemediğiniz sürece, “sorunuzun önemi olmayan şeylerin büyük şemasında”. Bu da doğru olabilir :)
Daniel Kaplan

@DanielKaplan: Ben de aynısını söylüyorum. :) Bununla cevap yazıp yorum arasında kaldım, ama gerçekten büyük soruyu sormayan doğru bir cevap olduğunu hissetmiyorum.
pdr

9

Evet, sınıfınızın yöntemleri alıcıları ve ayarlayıcıları çağırmalıdır. Alıcıları ve belirleyicileri yazmadaki asıl nokta gelecek kanıtıdır. Her özelliği bir alan haline getirebilir ve verileri sınıfın kullanıcılarına doğrudan gösterebilirsiniz. Alıcıları ve ayarlayıcıları oluşturma nedeniniz zorunlu değil, çünkü şimdi karmaşık bir mantık var, ancak eklemek zorunda kalırsanız arayüzün gelecekte kırılmaması için.


1
İlk cümleniz ile paragrafınızın kalanı arasındaki ilişkiyi görmüyorum. İlk cümle bir sınıfın kendi alıcıları ve belirleyicilerini çağırması gerektiğini söylüyor . Paragrafın geri kalanında, müşteri kodunun (yani: sınıfı kullanan kod) alanlara doğrudan erişmek yerine alıcıları ve ayarlayıcıları kullanması gerektiğini açıklar . Ancak bunun neden sınıfın kendi alanlarına doğrudan erişememesi gerektiğini anlamıyorum .
Daniel Kaplan,

1
@DanielKaplan Demek istediğim sebepler aynı ve aynı. Setter mantığını daha sonra eklerseniz, dahili kodu dışsal olduğu kadar (potansiyel olarak) etkiler.
Michael

2
@Michael: Bir sınıfın kendi alıcılarını / ayarlayıcılarını kullanmaması için genellikle iyi sebepler olabilir. Yaygın bir senaryo, formun birçok ayarlayıcısının bulunduğu yerdir. someField=newValue; refresh(); Yöntem birden fazla alanın ayarlanmasına izin veriyorsa, ayarlayıcıları bu alanları yazmak için çağırmak gereksiz "yenileme" işlemlerine neden olur. Tüm alanları yazmak ve bir refresh()kez çağırmak daha verimli ve daha düzgün görünümlü bir işlem sağlayabilir.
supercat,

6

Sorularınızı tek kelimeyle cevaplamak için, evet.

Bir sınıfa sahip olmak kendi alıcıları ve belirleyicilerini çağırır, genişletilebilirlik ekler ve gelecekteki kodlar için daha iyi bir temel sağlar.

Diyelim ki böyle bir şeyiniz var:

public class Vehicle
{
    private int year;
    private String make;

    public Vehicle(int year, String make)
    {
        setYear(year);
        setMake(make);
    }

    public void setYear(int year)
    {
        this.year = year;
    }

    public void setMake(String make)
    {
        this.make = make;
    }
}

Ayarlayıcıları yıllarca aramak ve şu anda yapmak herhangi bir işlevsellik ekleyemez, ancak ayarlayıcılara giriş doğrulama gibi bir şey eklemek isterseniz?

public class Vehicle
{
    private int year;
    private String make;

    public Vehicle(int year, String make)
    {
        setYear(year);
        setMake(make);
    }

    public void setYear(int year)
    {
        if(year > 0)
        {
            this.year = year;
        }
        else
        {
            System.out.println(year + " is not a valid year!");
        }
    }

    public void setMake(String make)
    {
        this.make = make;
    }
}

5

Bahsetilmeyen bir şey, alıcı ve ayarlayıcıların (tüm yöntemlerde olduğu gibi) Java'da sanal olmasıdır. Bu her zaman kodunuzda kullanmak için başka bir özellik ekler. Başka bir kullanıcı sınıfınızı genişletip, alıcılarınızın ve ayarlayıcılarınızın üzerine yazabilir. Temel sınıfınız, alt sınıfın verilerini kendi yerine kullanırdı. Sanal fonksiyonları açıkça işaretleyeceğiniz bir dilde, bunun hangi işlevlerle yapılabileceğini tahmin edip beyan edebileceğinizden bu çok daha kullanışlıdır. Java'da, bu her zaman farkında olmanız gereken bir şeydir. Arzu edilen davranış ise, bunları kodunuzda kullanmak iyi bir şeydir, aksi halde çok değil.


3

Genel arabirim tarafından sağlanan bir şeyi başarmaya çalışıyorsanız, alıcıları / ayarlayıcıları kullanın.

Bununla birlikte, sınıfın sahibi / geliştiricisi olarak, kodunuzun özel bölümlerine erişmenize izin verilir (kurs sınıfının içinden), ancak tehlikeleri azaltmaktan da siz sorumlusunuz.

Belki de bazı dahili listelerde yinelenen bir alıcıya sahipsiniz ve yineleyiciyi arttırmadan mevcut değeri elde etmek istersiniz. Bu durumda, özel değişkeni kullanın.

public class MyClass
{
    private int i;
    private List<string> list;
    public string getNextString()
    {
        i++;
        return list[i];
    }

    private void getString()
    {
        // Do not increment
        string currentString = list[i];

        // Increment
        string nextString = getNextString();
    }
}

Bunun nedenini verebilir misiniz?
Daniel Kaplan,

1

Evet. Alıcılar ve belirleyiciler durumu temsil eder, bu yüzden bu soruyu tersine çevirin - mecbur olmadıkça bir nesnenin durumunu değiştirmenin birden fazla yolunu izlemek zorunda kalmak ister misiniz?

İzlemeniz gereken şey sayısı ne kadar azsa o kadar iyidir - bu yüzden değişmez nesnelerin üstesinden gelmek daha kolaydır.

Bir kamu alanı ve kamu alıcı / belirleyicisine sahip olmanızı önleyen hiçbir şey olmadığını düşünün - peki ne kazanırsınız?

Durum , bir veya daha fazla nedenden dolayı doğrudan alana erişmek için arzu edilebilir veya hatta gerekli olabilir, eğer olursa, bundan uzak durmamanız gerekir. Ancak, bunu yalnızca tanımlanmış bir yararı varsa, gerçekten yapmalısınız.


Sorumun amacı, sadece aynı sınıftaki alanların doğrudan erişimini sormak olduğum . Müşteri kodunun alıcı ve belirleyicilerin amacını zaten anlıyorum.
Daniel Kaplan,

@DanielKaplan: Bunu anlıyorum ve söylediğim şey pratik olduğu kadarıyla onlara aynı şekilde davranmanız gerektiği.
jmoreno

@DanielKaplan: En azından bir an için tam olarak aynı sonuca sahip (tüm yanlar aynı etkide) olan bir özel pasaportunuz ve bir kamu pasaportunuz olabilir, ama bu şeylerin farklılaşma potansiyeli dışında size ne kazandırır? Bu senaryoda ve ne anlatmak arasındaki fark olan önlemek değil ki mümkün getters / belirleyiciler dışında durumuna erişmek için, ama aslında bunu yaparken önleyebilirsiniz. Zorunda olmadıkça kodunuzu karmaşıklaştırma.
jmoreno

1

Her iki yöntem de kendi kullanım durumlarına sahiptir. Genel ayarlayıcı alan değerini (ve / veya sınır değerleri) tutarlı tuttuğundan, yöntem mantığınız bu tutarlılık mantığına müdahale etmediğinde ayarlayıcıyı kullanmanız gerekir. Eğer "mülkünüzü" yeni ayarladıysanız, ayarlayıcıyı kullanın. Diğer taraftan, bazı alanlara doğrudan erişime ihtiyaç duyduğunuz durumlar, örneğin ağır ayarlayıcı ile toplu işlemler veya ayarlayıcı konseptinin çok basit olduğu işlemler vardır.

İşleri tutarlı tutmak sizin sorumluluğunuzdadır. Setter bunu tanım gereği yapar, ancak karmaşık durumları kapsamaz.


1

Hayır derdim ..

Alıcılarınız / ayarlayıcılarınız sadece alıp ayarladılarsa (tembel başlatma, kontrol yok, vb.) Sonra değişkenlerinizi kullanın. Gelecekte alıcılarınızı / ayarlayıcılarınızı değiştirirseniz methodWithLogic(), sınıfınızı çok iyi değiştirebilirsiniz (çünkü sınıfınız değişir) ve doğrudan atama yerine bir alıcı / ayarlayıcıyı arayabilirsiniz. Alıcı / ayarlayıcı için bu çağrıyı belgeleyebilirsiniz (sınıf kodunuzun geri kalanı doğrudan değişkeni kullanırken doğrudan alıcı / ayarlayıcıyı çağırmak garip olacaktır).

JVM, alıcılara / ayarlayıcılara sık sık yapılan aramaları satır içi yapar. Yani, asla performans sorunu değil. Değişken kullanmanın kazancı okunabilirlik ve IMHO, bunun için giderdim.

Bu yardımcı olur umarım..

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.