Tüm statik yöntemleri kullanamaz mıyım?


64

Aşağıdaki iki UpdateSubject yöntemi arasındaki fark nedir? Sadece varlıklarla çalışmak istiyorsanız statik yöntemler kullanmanın daha iyi olduğunu hissettim. Statik olmayan yöntemlerle hangi durumlarda gitmeliyim?

public class Subject
{
    public int Id {get; set;}
    public string Name { get; set; }

    public static bool UpdateSubject(Subject subject)
    {
        //Do something and return result
        return true;
    }
    public bool UpdateSubject()
    {
        //Do something on 'this' and return result
        return true;
    }
}

Bu gerçekten sinir bozucu bir soru için topluluktan çok fazla vuruş yapacağımı biliyorum ama sormayı kendimi durduramadım.

Miras ile uğraşırken bu pratik olmaz mı?

Güncelleme:
Şimdi bizim işyerinde oluyor. 5 geliştiriciyle 6 aylık bir asp.net web uygulaması üzerinde çalışıyoruz. Mimarımız, tüm API'ler için tüm statik yöntemleri kullanmaya karar verdi. Statik yöntemler olmasının gerekçesi hafif olup, sunucu yükünü düşük tutarak web uygulamalarına yarar sağlar.


9
Zengin Hickey, böyle aptalca olmayan bir şeyi teşvik eder (tüm statik yöntemler). İşlevsel bir tarzı seviyorsanız, işlevsel bir dil de kullanabilirsiniz;) Yazılımdaki her şey bir nesne olarak modellenemez.
İş

13
@Job: Bunun ne kadar işlevsel olduğunu gerçekten göremiyorum - prosedürel.
Aaron,

2
@Job: Bu sınıf değiştirilemez.
Aaron

3
@ DonalFellows: Tabii ki, C # ve F # her ikisi de karma paradigmadır. Fakat gerçek şu ki statik yöntemler! = Fonksiyonel programlama. FP, geçiş / zincirleme fonksiyonları, değişmez veri tipleri, yan etkilerden kaçınma vb. Anlamına gelir. Bu, OP'deki kod snippet'inin tam tersidir.
Aaron,

4
"Onun mantıksal metotlar olduğu gerekçesi hafif ve sunucu yükünü azaltarak web uygulamalarına fayda sağlıyor". Bu yanlış. Sunucunun yüklenmesini engellemek?
mamcx

Yanıtlar:


87

Açık bir şekilde "this" parametreleri olan statik yöntemlerin en belirgin problemlerine gideceğim:

  1. Sanal gönderimi ve ardından polimorfizmi kaybedersiniz. Türetilmiş bir sınıfta bu yöntemi hiçbir zaman geçersiz kılamazsınız. Elbette türetilmiş bir sınıfta bir new( static) yöntemi tanımlayabilirsiniz, ancak erişen herhangi bir kod tüm sınıf hiyerarşisinin farkında olmalı ve açık bir kontrol ve döküm yapmalıdır, ki bu tam olarak OO'dan kaçınması gereken şeydir .

  2. # 1 uzantısına göre sınıfın örneklerini bir arabirimle değiştiremezsiniz , çünkü arabirimler (çoğu dilde) staticyöntemleri bildiremez .

  3. Gereksiz ayrıntı. Hangisi daha okunaklı: Subject.Update(subject)ya da sadece subject.Update()?

  4. Argüman kontrolü. Yine dile bağlı, ancak birçoğu, thisbağımsız nullbir referans hatasının güvenli olmayan çalışma zamanı koşulları (türden bir arabellek taşması) oluşturmasını engellemek için argüman olmadığından emin olmak için gizli bir denetim derleyecek . Örnek yöntemleri kullanmıyorsanız, bu çeki, her yöntemin başında açıkça eklemeniz gerekir.

  5. Kafa karıştırıcı. Normal, makul programcı bir gördüğünde staticyöntemi, o doğal olarak o varsaymak gidiyor gelmez (bir karşılaştırma veya eşitlik yöntemiyle gibi birden çok örneğini alır veya üzerinde işlem yapabilmek için beklenen sürece geçerli örneği gerektirir nullreferanslar) . Bu şekilde kullanılan statik yöntemleri görmek bize iki ya da üçlü bir çekim yapmamızı sağlayacak ve 4. ya da 5. zamandan sonra stresli ve öfkeli olacağız ve ev adresinizi bildiğimizde Tanrı size yardımcı olacaktır.

  6. Bu bir çoğaltma şekli. Bir örnek yöntemi çağırdığınızda gerçekte olan şey, derleyici veya çalışma zamanının, türün yöntem tablosundaki yöntemi araştırması ve thisbağımsız değişken olarak kullanmaya çağırmasıdır . Temel olarak, derleyicinin zaten ne yaptığını yeniden uyguluyorsunuz. Sen ihlal ettiğinize KURU İhtiyaç olmadığında zaman farklı yöntemlerde tekrar tekrar aynı parametreyi tekrarlayarak.

Örnek yöntemleri statik yöntemlerle değiştirmek için iyi bir neden düşünmek zor . Lütfen yapma


5
Sadece final hattınız için +1. Keşke daha fazla insan "statik yöntem" i düşünmeden önce "örnek yöntem" i düşünmeli.
Stuart Leyland-Cole,

4
+1. Statik metotların test yazmayı imkansız hale getirdiğini eklerdim, çoğu durumda onlarla alay edemezsiniz: kodunuzun herhangi bir kısmı statik bir metoda bağlı olduğunda, onu bir no ile değiştiremezsiniz. testlerinde op. .NET dünyada bir ilk örnek sınıfları gibi olurdu File, FileInfove DirectoryInfo(ve aslında, gerekli ve testlerde dışarı alay olarak daha sonra enjekte edilebilir arayüzleri arkasına gizlemek kütüphaneler vardır).
sm

Polimorfizm / kalıtım konusundaki puanlarınızın çok fazla olduğunu düşünüyorum. kişi kod yeniden kullanımı için devralma kullanmamalıdır, bu yüzden konu dışı. miras kısmı da şüpheli. çoğu modern dilde, yöntem imzaları kendi başlarına polimorfiktir. İşlevsel bir yaklaşım izlerseniz, yöntemlerin etrafından dolaşmaya başlarsınız ve arayüzlerine bağlı olarak değiştirilebilir basit yöntemlerden karmaşık yöntemler kullanırsınız. OOP'de olduğu gibi, sadece düşünülerek tasarlanarak dengelenmeyen statik yöntemlerin dezavantajı yoktur.
sara,

@sm bu doğru değil. HERHANGİ BİR bağımlılığa sahipseniz, statik ya da değil, bu bir birim testinde olamaz, o zaman denenemez. statik buna neden olmaz. statik bir yöntem, db bağlantısını temsil eden bir sınıfın yapabildiği gibi enjekte edilebilir. "statik" in "durağan ve küresel kaynaklara ve dış kaynaklara dokunan katı ve gizli gizli bağımlılıklar" anlamına geldiğini varsayıyorsunuz. adil değil.
sara,

@kai, X ve Y ne olursa olsun X yerine Y yapan statik bir yöntem enjekte etmeye çalışır. Vidalanacak harici herhangi bir şeye bağımlı olmanıza gerek yok. Belki de, uzun X hesaplaması yapılmasını gerektirmeyen bir yönü test etmekle ilgileniyorsunuzdur. Bir fantezi oluşturmadan fantezi statik yönteminizi nasıl enjekte edersiniz? İşlevleri dolaşmak her dil tarafından desteklenmiyor. Statik yöntemlerin kullanım durumları vardır ve bunları mantıklı geldiğinde kullanırım . Bu, her zaman olduğu gibi, "tam olarak yapman gerektiği anlamına gelmez çünkü" durumu.
sm

13

Sadece statik yöntemlerin mevcut olduğu C'deki OOP'yi tam olarak nasıl yaptığınızı ve "this" nin bir yapı göstericisi olduğunu hemen hemen anlattınız. Ve evet, yapı sırasında bir eleman olarak depolanacak bir işlev gösterici belirterek C cinsinden çalışma zamanı polimorfizmini yapabilirsiniz. Diğer dillerde mevcut olan örnek metotlar, esasen tam olarak aynı şeyi kaputun altında yapan sözdizimsel şekerdir. Python gibi bazı diller, "self" parametresinin açıkça listelendiği yerlerin arasındadır, ancak onu çağırmak için özel bir sözdizimi kalıtım ve polimorfizmi sizin için işler.


FWIW, C ile sanal gönderimi şu şekilde yapabilirsiniz: obj->type->mthd(obj, ...);ve bu, diğer dillerde (ancak sahnelerin arkasında) sanal gönderimle olanlara büyük ölçüde benzer.
Donal Üyeleri,

@Karl Daha derine inmeye başlamak için bir tür yazılı başvuru yapabilir misiniz?
Jorge Lavín

GObject'e iyi bir referans uygulaması olarak bakmak isteyebilirsiniz .
Karl Bielefeldt

10

Statik yöntemle ilgili sorun, bir alt sınıfa ihtiyaç duyduğunuz anda ortaya çıkar.

Statik yöntemler alt sınıflarda geçersiz kılınamaz, bu nedenle yeni sınıflarınız metotların yeni uygulamalarını sağlayamaz, bu da onları daha az kullanışlı yapar.


2
Bu mutlaka bir sorun değil . Bazı diller kasıtlı olarak aynı şeyi başarmak için başka mekanizmalar (C ++ ve C # 'da sanal olmayan yöntemler) sağlar - C # bu fikri daha da genişletmek için "mühürlü" yöntemler bile sağlar! Açıkçası, bunu sadece halt etmek için
yapmazsın

Her iki yöntemi de doğrudan doğrudan çağırabileceğiniz için @Steve, bu değişmiyor.

@ Steve, Java'da statik yöntemler geçersiz kılınamaz.

@ Thorbjørn - ilk önce soru Java ile ya da başka herhangi bir spesifik OOP dili ile etiketlenmemiş. Daha da önemlisi, statik bir üye işlevini geçersiz kılabileceğinizi söylemedim. Bir şey ifade etmek için bir benzetme kullanmaya çalışıyordum. Açıkça başarısız olduğum için yorumlar silindi.
Steve314

2
Bir anlamda, bunlar hala "yöntemlerin yeni uygulamaları" dır. Sadece standart OOP anlamında değil. Bu, "
İfadeleriniz

7

Bir yöntem bildirirseniz, yöntemi yürütmek için staticsınıftan ( newanahtar sözcük kullanarak ) bir nesneyi başlatmanız gerekmez . Bununla birlikte, statik olmadıkça, üye değişkenlere başvuramazsınız ; bu durumda, bu üye değişkenler sınıfa ait belirli bir nesneyi değil sınıfa aittir .

Başka bir ifadeyle, staticanahtar kelime, bildirimin sınıf tanımının bir parçası olmasına neden olur, bu nedenle sınıfın tüm örneklerinde (sınıftan başlatılan nesneler) tek bir referans noktası haline gelir .

Ardından, sınıfınızın birden fazla çalışan kopyası istiyorsanız ve durumun (üye değişkenleri) çalışan tüm kopyalarınız arasında paylaşılmasını istemiyorsanız (yani, her nesne kendi benzersiz durumunu tutuyorsa), o zaman izlersiniz. Bu üye değişkenlerini statik olarak ilan edemiyorum.

Genel olarak, staticsınıfları ve yöntemleri yalnızca bir veya daha fazla parametre kabul eden yardımcı yöntem oluştururken kullanmalısınız ve herhangi bir yan etki olmadan bir tür nesne döndürün (örn. Sınıftaki durum değişkenlerinde yapılan değişiklikler)


1
OP'nin ne staticanlama geldiğini anladığını düşünüyorum , neden her şeyi statik olarak ilan etmediğini soruyor.
Ed S.

1
O edilir örneğine geçen Subjectyerine örtük ki, ama açık bir parametresi olarak - thisparametresi.
Steve314

Evet, evet ... ama demek istediğimi anlayamıyorum.
Ed S.

@Ed - yalnızca, üstü kapalı bir thisparametre ile yapabileceğiniz açık bir parametre ile hemen hemen her şeyi yapabilirsiniz . Beklentilerinde farklılıklar vardır, ama mirasımızın dışında, ne de gerçek bir fark var olabilir yapmak. Örneğin, bu, statik olmayan üye olabilir açık parametresi ile - erişilebilir.
Steve314

Varsayımın altında, C # 'daki argümanın özel üyelerine, aynı sınıfta (C ++' da yapabildiğiniz gibi) bildirilen statik bir yöntemle bile ulaşamayacağınız varsayımı altında çalışıyordum. Bunu neden düşündüğümden emin değilim, ama yanılmışım.
Ed S.

3

İnsanların “statik yöntemlerin kötü tasarım gösterdiğini” iddia etmelerinin sebebi, mutlaka yöntemler değil, aslında statik verilerdir, örtük veya açık. Örtük olarak, programda, dönüş değerleri veya parametrelerinde bulunmayan HERHANGİ bir durum anlamına gelir. Statik bir fonksiyonda durumun değiştirilmesi, prosedürel programlamanın bir gerilemesidir. Ancak, işlev durumu değiştirmezse veya durum değiştirmeyi bileşen nesne işlevlerine devrettiğinde, yordamdan ziyade işlevsel bir paradigma ortaya çıkar.

Durum değiştirmiyorsanız, statik yöntemlerin ve sınıfların birçok yararı olabilir. Bir yöntemin saf olduğundan ve bu nedenle yan etkilerden ve herhangi bir hatadan tamamen çoğaltılabildiğinden emin olmayı çok kolaylaştırır. Ayrıca, paylaşılan bir kütüphane kullanan birden fazla uygulamadaki farklı yöntemlerin aynı girdiyi vererek aynı sonuçları elde etmelerini sağlayarak hem hata izlemeyi hem de birim testini çok daha kolay hale getirmelerini sağlar.

Sonuçta, 'StateManager' gibi yaygın olarak görülen büyük nesneler ile statik veya global veriler arasında pratikte fark yoktur. Doğru kullanılan statik yöntemlerin faydası, yazarın durumu daha sonra revize edenlere değiştirmeme niyetini gösterebilir.


1
"... yordamsal programlamaya geri dönüş olduğunu ..." ifadeniz, beni çok kötü gördüğünüzü düşündürmemi sağlıyor, sanırım bir anlamda OO'nun bir parçası.
NoChance

making ... unit testing much easier.Hayır olmaz. Statik yöntemleri çağıran herhangi bir kod yapar , çünkü statik yöntemden ayrıştıramazsınız, çünkü üniteyi test etmek imkansızdır . Statik bir yönteme yapılan bir çağrı, o sınıfa katı bir kodlanmış bağımlılık haline gelir.
StuperUser

2

Dile ve ne yapmaya çalıştığınıza bağlı olarak, cevabı çok fazla fark olmadığı, ancak staticsürümün biraz daha fazla dağınıklığı olduğu olabilir.

Örnek sözdiziminizin Java veya C # 'yu önerdiği gibi, Thorbjørn Ravn Andersen' ın geçersiz kılma (geç bağlanma ile) sorununu belirtmekte haklı olduğunu düşünüyorum. Statik bir yöntemle, geç bağlanmanın temellendirilmesi için "özel" bir parametre yoktur, bu nedenle geç bağlanmaya sahip olamazsınız.

Aslında, statik bir yöntem, bir modülde sadece bir işlevdir, bu modül sınıfla aynı ada sahip - gerçekten bir OOP yöntemi değil.


2

Bunu yaparken temelde nesnelerin tüm noktasını yeniyorsun. Prosedürel koddan nesne yönelimli koda geçiş yapmamızın iyi bir nedeni var.

Ben ediyorum ASLA bir parametre olarak sınıf türünü aldı statik bir yöntem bildirmek. Bu sadece kazancı olmayan kodları daha karmaşık hale getirir.


3
objectBir staticmetoda geçip yeni bir yöntemle geri döndüyseniz, bunu yapabilirsiniz object. İşlevsel programcılar bunu her zaman yaparlar.
Robert Harvey,

Soru: MathStatik sınıfı "kazançsız için karmaşık" olarak mı düşünüyorsunuz, yoksa tüm sayı tiplerinin mutlak değer, olumsuzlama, ekleme, kareleme, rasgele kökler vb. tanımlayan sanal yöntemleri olsaydı tercih eder miydiniz? Sanmıyorum Bazı değişmezleri zorlamanız gereken gizli değişken durumu kaydetmeniz gerektiğinde nesneler temizdir. Bir tür üzerinde çalışan her olası yöntemi bir türün içinde bir araya getirerek, BU karmaşık ve sürdürülemez kodlara yol açan bir şeydir. Robert ile aynı fikirdeyim, işlevsel programcıların nasıl çalıştığını görmek isteyebilirsiniz, gerçek bir göz açıcı olabilir!
sara,

@kai Dediğim gibi, hemen hemen. Matematik sınıfı makul bir istisnadır, ancak sayısal türlere eklemek kötü bir fikir olmaz.
Loren Pechtel

2

Burada bir sürü harika cevap var ve onlar bir cevap olarak bulabileceğim herhangi bir şeyden çok daha anlayışlı ve bilgili, ama adress edilmeyen küçük bir şey olduğunu hissediyorum:

"Mimarımız, tüm API'ler için tüm statik yöntemleri kullanmaya karar verdi. Statik olmasının gerekçesi hafif ve sunucu uygulamalarını düşük tutarak web uygulamalarına fayda sağlıyor ."

(kalın vurgu benimdir)

Sorunun bu bölümünde benim 2c bu:

Teorik olarak söylenen doğru olanı: Statik bir metot çağırmak, aslında bu metodu çağırmak için yeterli olmaz. Statik olmayan (örnek) bir yöntem çağırmak, ilk önce nesneyi somutlaştırmanın ek yükünü ve bir noktada örneği yok etmeyi sağlar (manuel olarak veya kullanılan platforma bağlı olarak otomatik çöp toplama biçimi aracılığıyla).

Oyun biraz şeytanın savunucusu: Bu daha ileri gidebiliriz ve şöyle şeyler söyleyebiliriz:

  • (örnek) yönteminin her çağrılması için bir örnek oluşturulduğunda bu gerçekten kötü hale gelebilir (yalnızca durağan bırakmak ve buna benzer şekilde adlandırmak yerine)

  • Yapıcının karmaşıklığına, tip hiyerarşisine, diğer örnek üyelere ve diğer bilinmeyen faktörlere bağlı olarak, statik olmayan yöntem çağrısının ek yükü değişebilir ve gerçekten büyük olabilir

Gerçekte, IMHO, yukarıdaki hususlar (ve “daha ​​hızlı oldukları için statik kullanalım.” Genel yaklaşımı) saman adam argümanları / değerlendirmeleri:

  • eğer kod iyi ise ve bu argümana daha özelse, örnekler yalnızca gerektiğinde oluşturulduysa (ve optimal bir şekilde imha edildiyse), uygun olduğunda delirik yöntemler kullanmaya fazladan bir ek yük alamazsınız (çünkü sadece ihtiyaç duyulan nesneleri yarat, bu yöntem statik olarak ilan edilse bile yaratılmış olacaktı;

  • Statik yöntemler bildirimini bu şekilde kötüye kullanarak, örneklerin nasıl oluşturulduğuna ilişkin olarak kodunuzla ilgili bazı sorunları gizlemek mümkündür (yöntem statik olduğundan, yanlış bir deneme kodu daha sonra farkedilmeden geçebilir ve bu asla iyi olmaz).


2

Bu, sormakta olduğunuz topluluğa bağlı olarak çok farklı cevaplar alma eğiliminde olan çok ilginç bir sorudur. Diğer cevaplar C # veya java önyargısı gibi görünmektedir: (çoğunlukla) saf nesne yönelimli ve nesne yöneliminin ne olduğuna dair bir tür deyimsel bakış açısına sahip bir programlama dili.

C ++ gibi diğer topluluklarda, nesne yönelimi daha serbest bir şekilde yorumlanmaktadır. Bazıları uzmanların konuyu araştırdıklarını ve aslında, serbest işlevlerin kapsüllemeyi iyileştirdiği sonucuna vardıklarını bilir (vurgu benimdir):

Bir sınıftaki kapsülleme miktarını ölçmenin makul bir yolunun, sınıfın uygulaması değiştiğinde kırılabilecek fonksiyonların sayısını saymak olduğunu gördük. Bu durumda, n üye işlevli bir sınıfın, n + 1 üye işlevli bir sınıftan daha kapsüllenmiş olduğu anlaşılmaktadır. Ve bu gözlem, üye olmayan arkadaş olmayan işlevleri üye işlevlerine tercih etme argümanımı haklı çıkarıyor.

Scott Meyers

C ++ terminolojisine aşina olmayanlar için:

  • üye işlevi = yöntem
  • üye olmayan işlev = statik yöntem
  • arkadaş olmayan = yalnızca herkese açık API kullan
  • üye olmayan arkadaş olmayan işlev = yalnızca genel API kullanan statik yöntem

Şimdi, size soruyu cevaplamak için:

Tüm statik yöntemleri kullanamaz mıyım?

Hayır, her zaman statik yöntemler kullanmamalısınız.

Ancak, yalnızca bir sınıfın genel API'sini kullanmanız gerektiğinde bunları kullanmalısınız .


-3

Java’da ...

Statik yöntemlerin üzerine yazılmaz, bunun yerine gizlenir ve bu nedenle sınıfı genişletme durumunda büyük bir fark olur (sorun?).

Diğer taraftan, Java programcılarının gerçekten nedenini anlamadan, nesnenin KADAR OLMALI olduğunu düşünme eğiliminde olduğunu fark ettim ve katılmıyorum.

Statik sınıfa özel kod için kullanılmalıdır. Statik miras ile iyi çalışmıyor.

statik yöntemlerin kullanımına ilişkin bazı iyi örnekler, yan etkisi olmayan bağımsız işlevlerdir.

Ayrıca, senkronize edilmiş anahtar kelimeyi de görün ...


2
Bu statik yöntem nasıl saklanıyor ve sınıfı genişletirken bu farklılık ve problem nerede ortaya çıkıyor? Senkronizasyon buna nasıl uyuyor? Statik ve kalıtım problemleri nerede yaşanır? Temel Java kütüphanelerinde bazı iyi ve kötü statik yöntemler sağlayabilir ve neden her birinin böyle olduğunu açıklayabilir misiniz?
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.