it-swarm.dev

Harfler ve Ayarlayıcılar Ne Zaman Haklı?

Mektuplar ve ayarlayıcılar genellikle uygun OO olmadığı için eleştirilir. Öte yandan çoğu OO kod gördüğüm kapsamlı alıcılar ve ayarlayıcılar vardır.

Alıcılar ve ayarlayıcılar ne zaman gerekçelendirilir? Bunları kullanmaktan kaçınmaya mı çalışıyorsunuz? Genel olarak aşırı mı kullanılıyorlar?

En sevdiğiniz dilin özellikleri varsa (benimki) bu tür şeyler de bu soru için alıcılar ve ayarlayıcılar olarak kabul edilir. Aynı şey bir OO metodoloji bakış açısından).

Getter/Setter Eleştirisi Kaynakları (bazıları daha iyi görünürlük sağlamak için yorumlardan alınmıştır):

Eleştiriyi basitçe ifade etmek için: Harfler ve Ayarlayıcılar, nesnelerin iç durumunu nesnenin dışından değiştirmenize izin verir. Bu kapsüllemeyi ihlal ediyor. Sadece nesnenin kendisi iç durumunu önemsemelidir.

Ve bir örnek Kodun yordamsal sürümü.

struct Fridge
{
    int cheese;
}

void go_shopping(Fridge fridge)
{
     fridge.cheese += 5;
}

Kodun mutator sürümü:

class Fridge
{
     int cheese;

     void set_cheese(int _cheese) { cheese = _cheese; }
     int get_cheese() { return cheese; }
 }

void go_shopping(Fridge fridge)
{
     fridge.set_cheese(fridge.get_cheese() + 5);        
}

Alıcılar ve ayarlayıcılar, uygun bir kapsülleme sağlamadan kodu çok daha karmaşık hale getirdi. Dahili duruma diğer nesneler tarafından erişilebildiğinden, bu alıcıları ve ayarlayıcıları ekleyerek çok fazla kazanmıyoruz.

Soru daha önce Yığın Taşması üzerinde tartışıldı:

179
Winston Ewert

Alıcılara ve pasörlere sahip olmak başlı başına kapsüllenmeyi bozmaz. Enkapsülasyonu bölen şey, her veri üyesi için (her alan, Java lingo), hiç düşünmeden otomatik olarak bir alıcı ve ayarlayıcı eklemektir. tüm veri üyelerini herkese açık hale getirmekten daha iyi, sadece küçük bir adım uzaklıktadır.

Kapsülleme noktası, nesnenin durumunu nesnenin dışından bilmemeniz veya değiştirmemeniz değil, bunu yapmak için makul bir ilke olması gerektiğidir.

  • Bazı veri üyeleri tamamen nesnenin içinde olabilir ve ne alıcıları ne de ayarlayıcıları olmalıdır.

  • Bazı veri üyelerinin salt okunur olması gerekir, bu nedenle alıcılara ihtiyaç duyabilirler, ancak ayarlayıcılara ihtiyaç duymazlar.

  • Bazı veri üyelerinin birbiriyle tutarlı kalması gerekebilir. Böyle bir durumda, her biri için bir ayarlayıcı değil, aynı anda ayarlamak için tek bir yöntem sağlarsınız, böylece değerleri tutarlılık açısından kontrol edebilirsiniz.

  • Bazı veri üyelerinin yalnızca belirli bir şekilde değiştirilmesi gerekebilir, örneğin sabit bir miktar kadar arttırmak veya azaltmak. Bu durumda, ayarlayıcı yerine increment() ve/veya decrement() yöntemi sağlarsınız.

  • Yine de başkalarının aslında okuma-yazma ihtiyacı olabilir ve hem alıcı hem de ayarlayıcıya sahip olabilir.

Bir class Person Örneğini ele alalım. Diyelim ki bir kişinin adı, sosyal güvenlik numarası ve yaşı var. İnsanların adlarını veya sosyal güvenlik numaralarını değiştirmelerine izin vermediğimizi varsayalım. Bununla birlikte, kişinin yaşı her yıl 1 arttırılmalıdır. Bu durumda, adı ve SSN'yi verilen değerlere başlatacak ve yaşı 0 olarak başlatacak bir yapıcı sağlarsınız. Ayrıca, incrementAge() yöntemini de sağlarsınız. 1. Ayrıca üçü için alıcılar sağlarsınız. Bu durumda ayarlayıcı gerekmez.

Bu tasarımda, nesnenin durumunun sınıfın dışından denetlenmesine ve sınıfın dışından değiştirilmesine izin verirsiniz. Ancak, durumun keyfi olarak değiştirilmesine izin vermezsiniz. Adın ve SSN'nin hiçbir zaman değiştirilemeyeceğini ve yaşın bir seferde 1 yıl artırılabileceğini etkili bir şekilde belirten bir politika vardır.

Şimdi diyelim ki bir kişinin de maaşı var. İnsanlar istedikleri işi değiştirebilirler, bu da maaşlarının da değişeceği anlamına gelir. Bu durumu modellemek için setSalary() yöntemi sağlamaktan başka çaremiz yok! Maaşın istenildiği zaman değiştirilmesine izin vermek bu durumda son derece makul bir politikadır.

Bu arada, örneğinizde, putCheese() ve takeCheese() yerine Fridge sınıfını get_cheese() ve set_cheese() yöntemlerini veririm . O zaman hala kapsüllemeniz olur.


public class Fridge {
  private List objects;
  private Date warranty;

  /** How the warranty is stored internally is a detail. */
  public Fridge( Date warranty ) {
    // The Fridge can set its internal warranty, but it is not re-exposed.
    setWarranty( warranty );
  }

  /** Doesn't expose how the fridge knows it is empty. */
  public boolean isEmpty() {
    return getObjects().isEmpty();
  }

  /** When the fridge has no more room... */
  public boolean isFull() {
  }

  /** Answers whether the given object will fit. */
  public boolean canStore( Object o ) {
    boolean result = false;

    // Clients may not ask how much room remains in the fridge.
    if( o instanceof PhysicalObject ) {
      PhysicalObject po = (PhysicalObject)o;

      // How the fridge determines its remaining usable volume is a detail.
      // How a physical object determines whether it fits within a specified
      // volume is also a detail.
      result = po.isEnclosedBy( getUsableVolume() );
    }

     return result;
  }

  /** Doesn't expose how the fridge knows its warranty has expired. */
  public boolean isPastWarranty() {
    return getWarranty().before( new Date() );
  }

  /** Doesn't expose how objects are stored in the fridge. */
  public synchronized void store( Object o ) {
    validateExpiration( o );

    // Can the object fit?
    if( canStore( o ) ) {
      getObjects().add( o );
    }
    else {
      throw FridgeFullException( o );
    }
  }

  /** Doesn't expose how objects are removed from the fridge. */
  public synchronized void remove( Object o ) {
    if( !getObjects().contains( o ) ) {
      throw new ObjectNotFoundException( o );
    }

    getObjects().remove( o );

    validateExpiration( o );
  }

  /** Lazily initialized list, an implementation detail. */
  private synchronized List getObjects() {
    if( this.list == null ) { this.list = new List(); }
    return this.list;
  }

  /** How object expiration is determined is also a detail. */
  private void validateExpiration( Object o ) {
    // Objects can answer whether they have gone past a given
    // expiration date. How each object "knows" it has expired
    // is a detail. The Fridge might use a scanner and
    // items might have embedded RFID chips. It's a detail hidden
    // by proper encapsulation.
    if( o implements Expires && ((Expires)o).expiresBefore( today ) ) {
      throw new ExpiredObjectException( o );
    }
  }

  /** This creates a copy of the warranty for immutability purposes. */
  private void setWarranty( Date warranty ) {
    assert warranty != null;
    this.warranty = new Date( warranty.getTime() )
  }
}
163
Dima

Java) içindeki alıcı ve ayarlayıcıların temel nedeni çok basit:

  • Bir arayüzde alanları değil, yalnızca yöntemler belirtebilirsiniz.

Bu nedenle, bir alanın arayüzden geçmesine izin vermek istiyorsanız, bir okuyucuya ve bir yazar yöntemine ihtiyacınız olacaktır. Bunlar geleneksel olarak x alanı için getX ve setX olarak adlandırılır.

42
user1249

Gönderen http://www.adam-bien.com/roller/abien/entry/encapsulation_violation_with_getters_and

JavaBean stili:

connection.setUser("dukie");
connection.setPwd("duke");
connection.initialize();

OO tarzı:

connection.connect("dukie","duke");

Açıkça ikinci yaklaşımı tercih ediyorum; uygulama ayrıntılarını akıtmaz, daha basit ve daha özlüdür ve gerekli tüm bilgiler yöntem çağrısına dahil edilir, bu yüzden doğru yapmak daha kolaydır. Ayrıca mümkün olduğunda yapıcıdaki parametreleri kullanarak özel üyeler ayarlamayı tercih ederim.

Sorunuz, bir alıcı/ayarlayıcı ne zaman haklı? Belki bir mod değişikliği gerektiğinde veya bazı bilgiler için bir nesneyi sorgulamanız gerektiğinde.

myObject.GetStatus();
myObject.SomeCapabilitySwitch = true;

Bunu düşünürken, C # 'da kodlamaya ilk başladığımda, yukarıda gösterilen Javabeans tarzında bir sürü kod yazdım. Ancak dilde deneyim kazandıkça, yapıcıda daha fazla üye ayarı yapmaya ve yukarıdaki OO stili gibi görünen yöntemleri kullanmaya başladım).

20
Robert Harvey

Alıcılar ve ayarlayıcılar ne zaman gerekçelendirilir?

"Get" ve "set" davranışları aslında modelinizdeki davranışla eşleştiğinde pratikte asla değildir.

Diğer her kullanım sadece bir hile çünkü iş etki alanının davranışı net değil.

Düzen

Bu cevap yanıltıcı olabilirdi, bu yüzden genişleyeyim. Yukarıdaki cevaplar çoğunlukla doğrudur, ancak OO tasarım ve büyük resmi özleyen şeylerden bazıları) programlama paradigmalarına odaklanır. Benim tecrübelerime göre, insanlar gettings ve setterlerden kaçınmanın OO programlama dilleri (örneğin, alıcıları mülklerle değiştirmenin harika olduğunu düşünüyorlar)

Aslında bu tasarım sürecinin bir sonucudur. Arayüzlere, kapsüllemeye ve kalıplara girmek zorunda değilsiniz, bu paradigmaları bozup kırmadığını ve neyin iyi olup olmadığını tartışmak = OO programlama. nihayetinde önemli olan, eğer alanınızda bu şekilde çalışan hiçbir şey yoksa, alanınızı modellememenizdir.

Gerçek şu ki, alan alanınızdaki herhangi bir sistemin alıcıları ve ayarlayıcıları olması pek olası değildir. Bordrodan sorumlu olan erkek ya da kadına gidemez ve sadece "Bu maaşını X olarak ayarla" diyemezsin ya da "Bana bu maaşı al" . Böyle bir davranış yok

Bunu kodunuza koyuyorsanız, sisteminizi alan adınızın modeliyle eşleşecek şekilde tasarlamıyorsunuzdur. Evet, arayüzleri ve kapsüllemeyi bozuyor, ama mesele bu değil. Mesele şu ki, var olmayan bir şeyi modelliyorsunuz.

Muhtemelen önemli bir adım veya süreci kaçırıyorsunuz, çünkü muhtemelen sadece ödeme yapmak için yürüyemem ve bu maaşını X'e ayarlayamamamın bir nedeni var.

İnsanlar getters ve setters kullandıklarında, bu süreç için kuralları yanlış yere itme eğilimindedirler. Bu, alan adınızdan daha da uzaklaşıyor. Gerçek dünya örneğini kullanarak, içeri giren rasgele kişinin bu değerleri alma iznine sahip olduğu varsayılırsa, aksi takdirde onlardan istemeyecekti. Sadece alanın nasıl olduğu değil, aslında alanın nasıl olduğu hakkında da yalan söylüyor.

14
Cormac Mulhall

Genel bir kural olarak, alıcılar ve pasifler kötü bir fikirdir. Bir alan mantıksal olarak arayüzün bir parçası değilse ve onu özel yaparsanız, sorun değil. Mantıksal olarak arayüzün bir parçasıysa ve herkese açık hale getirirseniz, sorun değil. Ancak özel hale getirir ve sonra bir alıcı ve ayarlayıcı sağlayarak tekrar dönüp etkili bir şekilde herkese açık hale getirirseniz, kodunuzun artık daha ayrıntılı ve gizlenmiş olması dışında başladığınız yere geri dönersiniz.

Açıkçası istisnalar var. Java'da arayüzler kullanmanız gerekebilir. Java standart kütüphane, normal kod kalitesi ölçülerinden daha ağır basacak kadar geriye dönük uyumluluk gereksinimlerine sahiptir. daha sonra başka bir şekilde arabirimi bozmadan, kaydedilmiş bir alanı anında hesaplama ile değiştirin.Ama bunlar istisnalar.Karakterler ve ayarlayıcılar, özel gerekçe gerektiren bir anti-kalıptır.

11
rwallace

alanın doğrudan veya yöntemle erişilebilir olup olmadığı gerçekten önemli değildir.

Sınıf değişmezleri (yararlı olanlar) önemlidir. Ve onları korumak için bazen dışarıdan bir şey değiştiremeyiz. Örneğin. Eğer ayrı genişlik ve yükseklikte sınıf Meydanı varsa, bunlardan birini değiştirmek onu kareden başka bir şey haline getirir. Bu durumda yöntem değişikliğine ihtiyacımız varSide Rectangle ise, setters/public field olabilir. Ancak ayarlayıcı sıfırdan büyük daha iyi olup olmadığını test edecek.

Somut dillerde (örneğin Java) bu yöntemlere (arayüzlere) ihtiyacımızın nedenleri. Yöntemin başka bir nedeni de uyumluluktur (kaynak ve ikili). Bu yüzden onları eklemek daha kolay, daha sonra kamusal alanın yeterli olup olmayacağını düşünün.

btw. Herkese açık final alanları olan basit değişmez değer tutma sınıflarını kullanmayı seviyorum.

3
user470365

Arabirimleri aynı tutarken dahili bileşenlerinizi istediğiniz gibi değiştirmek isteyebilirsiniz. Arabirimleriniz farklı değilse, kodladığınız kodlar bozulmaz. İç kısımlarınızı dilediğiniz gibi değiştirebilirsiniz.

2
George Silva

Genişlik ve yüksekliği kapsayan bir Size sınıfı düşünün. Yapıcıları kullanarak ayarlayıcıları ortadan kaldırabilirim ama bu Size ile bir dikdörtgen çizmeme nasıl yardımcı olur? Genişlik ve yükseklik sınıfa ait dahili veriler değildir; bunlar Boyut tüketicileri tarafından kullanılabilir olması gereken paylaşılan verilerdir.

Nesneler, davranışlar ve durum veya niteliklerden oluşur. Açıkta bir durum yoksa, yalnızca davranışlar herkese açıktır.

Devlet olmadan, bir nesne koleksiyonunu nasıl sıralarsınız? Bir nesnenin belirli bir örneğini nasıl ararsınız? Yalnızca kurucuları kullanırsanız, nesnenizde uzun bir öznitelik listesi varsa ne olur?

Doğrulama olmadan hiçbir yöntem parametresi kullanılmamalıdır. Bu nedenle yazmak tembelliktir:

setMyField(int myField){
    this.myField = myField;
}

Bu şekilde yazarsanız, en azından ayarlayıcıyı doğrulama için kullanmaya hazırsınız demektir; halka açık bir alandan daha iyi - ama zar zor. Ancak en azından müşterilerinizin kodunu kırmadan geri dönüp doğrulama kurallarını koyabileceğiniz tutarlı bir genel arayüzünüz var.

Harfler, ayarlayıcılar, özellikler, mutasyoncular, onlara ne istediğinizi söyleyin, ancak bunlar gerekli.

0
Dale

Benim yaklaşımım bu -

Daha sonra veri ile demen yapmayı beklediğimde, bir alıcı/ayarlayıcı haklı. Ayrıca, eğer değişiklik oluyorsa, sık sık verileri bir alıcıya/ayarlayıcıya iterim.

Bir POD yapısı ise, yuvaları kullanılabilir durumda bırakıyorum.

Daha soyut bir düzeyde, soru "verileri kim yönetir" dir ve bu projeye bağlıdır.

0
Paul Nathan

Alıcılar ve ayarlayıcılar kapsülleme ve gerçek OO'yu ihlal ederse, o zaman ciddi bir sorun yaşıyorum.

Bir nesnenin aklınıza en iyi gelen şeyi temsil ettiğini her zaman hissettim.

Java'da Mazes üreten bir program yazmayı bitirdim ve "Labirent Kareler" i temsil eden bir sınıfım var. Bu sınıfta koordinatları, duvarları ve booleanları vb. Temsil eden verilerim var.

Bu verileri değiştirmek/değiştirmek/erişmek için bir yolum var! Alıcılar ve ayarlayıcılar olmadan ne yapabilirim? Java özellikleri kullanmıyor ve bu sınıfa ait tüm verilerimi herkese açık olarak ayarlamak kesinlikle bir kapsülleme ve OO ihlalidir.

0
Bryan Harrington

Alıcıları ve ayarlayıcıları kullanmak karmaşık geliyorsa, sorun kavramın kendisi değil, dil olabilir.

Ruby'de yazılmış second örneğinden kod:

class Fridge
  attr_accessor :cheese
end

def go_shopping fridge
  fridge.cheese += 5
end

Java'daki first örneğine çok benzediğine dikkat edin? Alıcılar ve ayarlayıcılar birinci sınıf vatandaş olarak kabul edildiğinde, bunlar bir angarya değildir ve ek esneklik bazen gerçek bir nimet olabilir - örneğin, yeni bir buzdolabında peynir için varsayılan bir değer döndürmeye karar verebiliriz:

class Fridge
  attr_accessor :cheese

  def cheese
    @cheese || 0
  end
end

Tabii ki halka açık olarak maruz kalmamaları gereken birçok değişken olacaktır. İç değişkenleri gereksiz yere ortaya koymak, kodunuzu daha da kötüleştirecektir, ancak bunu alıcılarda ve ayarlayıcılarda zorlukla suçlayabilirsiniz.

0
Tobias Cohen