Bir sınıf yöntemi, kendisini değiştirdikten sonra aynı örneği ne zaman döndürmelidir?


9

Ben üç yöntem vardır bir sınıf var A(), B()ve C(). Bu yöntemler kendi örneğini değiştirir.

Örnek ayrı bir kopya olduğunda (örneğin) bir örnek döndürmek zorunda olsa da, yöntemde aynı örneği değiştirirken ve başka bir değer Clone()döndürmüyorken, döndürmek için ücretsiz bir seçim voidveya aynı örneği ( return this;) var.

Aynı değiştirilmiş örneği döndürmeye karar verirken, gibi düzgün yöntem zincirleri yapabilirim obj.A().B().C();.

Bunu yapmanın tek nedeni bu mu?

Kendi örneğini değiştirmek ve geri vermek de uygun mu? Yoksa yalnızca bir kopyasını döndürmeli ve orijinal nesneyi eskisi gibi bırakmalı mı? Aynı değiştirilmiş örneği döndürürken, kullanıcı döndürülen değerin bir kopya olduğunu varsayabilir, aksi takdirde döndürülmez? Eğer uygunsa, yöntemde bu tür şeyleri netleştirmenin en iyi yolu nedir?

Yanıtlar:


7

Zincirleme ne zaman kullanılır?

İşlev zincirleme çoğunlukla otomatik tamamlamalı bir IDE'nin yaygın olduğu diller arasında popülerdir. Örneğin, hemen hemen tüm C # geliştiricileri Visual Studio'yu kullanır. Bu nedenle, C # ile geliştiriyorsanız yöntemlerinize zincirleme ekleme, Visual Studio zinciri oluşturmanıza yardımcı olacağından, bu sınıfın kullanıcıları için zaman tasarrufu sağlayabilir.

Öte yandan, PHP gibi doğada oldukça dinamik olan ve genellikle IDE'lerde otomatik tamamlama desteğine sahip olmayan diller, zincirlemeyi destekleyen daha az sınıf görecektir. Zincirleme sadece zincirlenebilir yöntemleri ortaya çıkarmak için doğru phpDoc'lar kullanıldığında uygun olacaktır.

Zincirleme nedir?

FooAşağıdaki iki adında bir sınıf verildiğinde , her iki yöntem de zincirlenebilir.

function what() { return this; }
function when() { return new Foo(this); }

Birinin geçerli örneğe bir başvuru olması ve yeni bir örnek oluşturması, bunların zincirlenebilir yöntemler olduğunu değiştirmez.

Zincirlenebilir bir yöntemin yalnızca geçerli nesneye başvurması gerektiğine dair altın bir kural yoktur. Etkili, zincirlenebilir yöntemler iki farklı sınıfta olabilir. Örneğin;

class B { function When() { return true; } };
class A { function What() { return new B(); } };

var a = new A();
var x = a.What().When();

thisYukarıdaki örneğin hiçbirinde referans yoktur . Kod a.What().When()bir zincirleme örneğidir. İlginç olan, sınıf türünün Bhiçbir zaman bir değişkene atanmamış olmasıdır.

Bir yöntem, dönüş değeri ifadenin sonraki bileşeni olarak kullanıldığında zincirlenir.

İşte biraz daha örnek

 // return value never assigned.
 myFile.Open("something.txt").Write("stuff").Close();

// two chains used in expression
int x = a.X().Y() * b.X().Y();

// a chain that creates new strings
string name = str.Substring(1,10).Trim().ToUpperCase();

Ne zaman kullanılır thisvenew(this)

Çoğu dilde dizeler değişmezdir. Bu nedenle zincirleme yöntemi çağrıları her zaman yeni dizelerin oluşturulmasına neden olur. Nerede StringBuilder gibi bir nesne olarak değiştirilebilir.

Tutarlılık en iyi uygulamadır.

Bir nesnenin durumunu değiştirip geri dönen thisyöntemleriniz varsa, yeni örnekleri döndüren yöntemlerle karıştırmayın. Bunun yerine, Clone()bunu açıkça yapacak denilen belirli bir yöntem oluşturun .

 var x  = a.Foo().Boo().Clone().Foo();

İçinde neler olduğu konusunda çok daha net a.

Dış ve Arka Adım

Buna adım ve geri hile diyorum, çünkü zincirleme ile ilgili birçok ortak sorunu çözüyor. Temel olarak orijinal sınıftan yeni bir geçici sınıfa adım atıp orijinal sınıfa geri dönmeniz anlamına gelir.

Geçici sınıf yalnızca orijinal sınıfa özel özellikler sağlamak için vardır, ancak yalnızca özel koşullar altında bulunur.

Bir zincirin durumu değiştirmek zorunda kaldığı zamanlar vardır , ancak sınıf Atüm bu olası durumları temsil edemez . Yani bir zincir sırasında geri referans içeren yeni bir sınıf tanıtılır A. Bu, programcının bir duruma geçmesine ve geri dönmesine izin verir A.

İşte benim örneğim, özel durum olarak bilinsin B.

class A {
    function Foo() { return this; }
    function Boo() { return this; }
    function Change() return new B(this); }
}

class B {
    var _a;
    function (A) { _a = A; }
    function What() { return this; }
    function When() { return this; }
    function End() { return _a; }
}

var a = new A();
a.Foo().Change().What().When().End().Boo();

Şimdi bu çok basit bir örnek. Daha fazla kontrole sahip olmak istiyorsanız , farklı yöntemlere sahip Byeni bir süper türe geri dönebilirsiniz A.


4
Yalnızca Intellisense benzeri otomatik tamamlama desteğine bağlı olarak neden yöntem zincirleme önerdiğinizi gerçekten anlamıyorum. Yöntem zincirleri veya akıcı arayüzler, herhangi bir OOP dili için bir API tasarım modelidir; otomatik tamamlamanın yaptığı tek şey yazım hatalarını ve yazım hatalarını önlemektir.
amon

@ amon söylediğimi yanlış okudun. Zeka dili bir dil için yaygın olduğunda daha popüler olduğunu söyledim. Bu özelliğe bağlı olduğunu hiç söylemedim.
Reactgular

3
Bu cevap, zincirleme olanaklarını anlamama yardımcı olsa da, zincirleme ne zaman kullanılacağına karar vermem hala eksik. Tabii, tutarlılık en iyisidir . Ancak, fonksiyon ve benim nesne değiştirdiğim zaman atıfta return this. Kütüphanelerimin kullanıcılarının bu durumu ele almalarına ve anlamalarına nasıl yardımcı olabilirim? (Hiç değiştirmeden, gerektiren bu gerçekten iyi olacağını, bu zorunlu değildir bile, zincir yöntemlerine onları izin Veya sadece tek bir şekilde tutması gerektiğini?)
Martin Braun

@modiX Cevabım doğruysa, lütfen cevap olarak kabul edin. Aradığınız diğer şeyler zincirlemenin nasıl kullanılacağına dair görüşlerdir ve bunun için doğru / yanlış cevap yoktur. Karar vermek size kalmış. Belki küçük detaylar için çok fazla endişeleniyorsun?
Reactgular

3

Bu yöntemler kendi örneğini değiştirir.

Dile bağlı olarak, örneklerini veya parametrelerini döndüren void/ unitdeğiştiren yöntemlere sahip olmak deyimsel değildir. Daha fazlasını yapan dillerde bile (C #, C ++), daha işlevsel stil programlamaya (değişmez nesneler, saf fonksiyonlar) doğru kayma ile modası geçiyor. Ama diyelim ki şimdilik iyi bir neden var.

Bunu yapmanın tek nedeni bu mu?

Belirli davranışlar için (düşün x++), değişkeni değiştirse bile işlemin sonucu döndürmesi beklenir. Ancak bunu kendi başınıza yapmanın tek nedeni budur.

Kendi örneğini değiştirmek ve geri vermek de uygun mu? Yoksa yalnızca bir kopyasını döndürmeli ve orijinal nesneyi eskisi gibi bırakmalı mı? Aynı değiştirilmiş örneği döndürdüğünde, kullanıcı döndürülen değerin bir kopya olduğunu kabul edebilir, aksi takdirde döndürülmez? Eğer uygunsa, yöntemde bu tür şeyleri netleştirmenin en iyi yolu nedir?

Değişir.

Kopyalama / yeni ve geri dönüşün yaygın olduğu dillerde (C # LINQ ve dizeler), aynı başvuruyu döndürmek kafa karıştırıcı olacaktır. Değişiklik ve geri dönüşün yaygın olduğu dillerde (bazı C ++ kütüphaneleri) kopyalamak kafa karıştırıcı olacaktır.

İmzayı açık hale getirmek ( voidözellikler gibi dil yapılarını döndürmek veya kullanmak) açıklığa kavuşturmanın en iyi yolu olacaktır. Bundan sonra, adı SetFoomevcut örneği değiştirdiğinizi göstermek gibi açık hale getirmek iyi olur. Ancak anahtar, üzerinde çalıştığınız dilin / kütüphanenin deyimlerini korumaktır.


Cevabınız için teşekkürler. Esas olarak .NET çerçevesi ile çalışıyorum ve cevabınız deyimsel olmayan şeylerle dolu. LINQ yöntemleri gibi şeyler, işlemleri vb. Ekledikten sonra orijinalin yeni bir kopyasını döndürürken, Clear()veya Add()herhangi bir koleksiyon türü gibi şeyler aynı örneği değiştirir ve geri döner void. Her iki durumda da kafa karıştırıcı olacağını söylüyorsunuz ...
Martin Braun

@modix - LINQ yok değil operasyonlarını ekleme sırasında bir kopyasını döndürür.
Telastyn

O zaman neden yapabilirim obj = myCollection.Where(x => x.MyProperty > 0).OrderBy(x => x.MyProperty);? Where()yeni bir koleksiyon nesnesi döndürür. OrderBy()içerik sıralandığı için farklı bir koleksiyon nesnesi bile döndürür.
Martin Braun

1
@modix - Onlar uygulamak yeni nesneleri döndüren IEnumerable, ancak net olmak gerekirse, bunlar kopya değildir ve onlar (hariç koleksiyonları değildir ToList, ToArrayvs.).
Telastyn

Bu doğru, kopya değil, dahası yeni nesneler. Ayrıca koleksiyon derken, sayabileceğiniz nesnelere atıfta bulunmuştum (herhangi bir dizide birden fazla nesneyi tutan nesneler), miras alınan sınıflara değil ICollection. Benim hatam.
Martin Braun

0

(C ++ 'ı programlama diliniz olarak kabul ettim)

Benim için bu çoğunlukla okunabilirlik unsurudur. A, B, C değiştiriciler ise, özellikle bu, bazı işlevlere parametre olarak geçirilen geçici bir nesne ise, ör.

do_something(
      CPerson('name').age(24).height(160).weight(70)
      );

nazaran

{
   CPerson person('name');
   person.set_age(24);
   person.set_height(160);
   person.set_weight(70);
   do_something(person);
}

Değiştirilen örneğe bir referans döndürmek uygun değilse, evet diyebilirim ve örneğin '>>' ve '<<' akış operatörlerine ( http://www.cprogramming.com/tutorial/ operator_overloading.html )


1
Yanlış kabul ettin, bu C #. Ancak sorumun daha yaygın olmasını istiyorum.
Martin Braun

Tabii, Java da olabilirdi, ama bu sadece operatörlerle örneğimi bağlam içine yerleştirmek için küçük bir noktaydı. Özü, diğer cevapların da belirttiği gibi, okuyucunun beklentileri ve arka planından etkilenen okunabilirliğe dayanır; bu, kendileriyle ilgili belirli dil / çerçevelerdeki olağan uygulamalardan etkilenir.
AdrianI

0

Ayrıca dönüşü değiştirmek yerine kopya döndürmeyle zincirleme yöntemini de yapabilirsiniz.

İyi bir C # örneği, çağrıldığı dizeyi değiştirmeyen ancak bunun yerine neşeyle zincirleme yapmanıza izin veren yeni bir dize döndüren string.Replace (a, b) 'dir.


Evet biliyorum. Ama yine de kendi vakasını düzenlemek istemediğimde bu vakayı kullanmak zorunda olduğum için bu davaya başvurmak istemedim. Ancak, bunu işaret ettiğiniz için teşekkürler.
Martin Braun

Diğer cevaplara göre anlambilimin tutarlılığı önemlidir. Kopya dönüşü ile tutarlı olabileceğiniz ve değiştirme dönüşü ile tutarlı olamayacağınız için (değişmez nesnelerle yapamadığınız için), sorunuzun cevabının kopya dönüşü kullanmaması olduğunu söyleyebilirim.
Peter Wone
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.