Bir sınıfın kendi genel yöntemini kullanması uygun mudur?


23

Arka fon

Şu anda ben bir var bir durum var nesneyi hem bir cihaz tarafından iletilen ve alınır. Bu mesajın aşağıdaki gibi birkaç yapısı var:

public void ReverseData()
public void ScheduleTransmission()

ScheduleTransmissionYöntem ihtiyacı aramaya ReverseDatadenir her yöntemi. Ancak, nesnenin uygulamada başlatıldığı yerden ReverseDataharici olarak aramam gereken (ve ad alanının dışına tamamen eklemeliyim ) gereken zamanlar var.

"Alma" derken ReverseData, bunun object_receivedverileri tersine çevirmek için bir olay işleyicide harici olarak çağrılacağını kastediyorum .

Soru

Bir nesnenin kendi genel yöntemlerini çağırması genellikle kabul edilebilir mi?


5
Bir kamu yöntemini kendisinden aramakta yanlış bir şey yoktur. Ancak, yöntem adlarınıza bağlı olarak, ReverseData (), iç verileri tersine çevirirse, genel bir yöntem olmak için bana biraz tehlikeli geliyor. ReverseData () nesnenin dışını çağırdıysa ve tekrar ScheduleTransmission () ile çağrıldıysa.
Kaan

@Kaan Bunlar, yöntemlerimin gerçek adları değil, yakından ilişkili. Aslında "ters veri" sadece toplam kelimenin 8 bitini tersine çevirir ve aldığımız ve ilettiğimizde yapılır.
Snoop

Soru hala duruyor. Bu 8 bit, iletim programlanmadan önce iki kez ters çevrilirse? Bu, kamu arayüzünde büyük bir delik gibi geliyor. Cevabımda belirttiğim gibi ortak arayüzü düşünmek, bu sorunun ortaya çıkmasına yardımcı olabilir.
Daniel T.,

2
@StevieV Bunun sadece Kaan'ın yarattığı endişeyi arttırdığına inanıyorum. Nesnenin durumunu değiştiren bir yöntemi gösterdiğinizi düşündüğünüz gibi ses çıkarır ve durum, öncelikle yöntemin çağrılma sayısına bağlıdır. Bu, nesnenin durumunun kodunuzda ne olduğunu izlemeye çalışırken bir kabus yaratır. Bana bu kavramsal durumlardan ayrı veri türlerinden faydalanacağınız gibi geliyor , böylece endişelenmenize gerek kalmadan herhangi bir kod parçasında ne olduğunu anlayabilirsiniz.
jpmc26

2
@StevieV Data ve SerializedData gibi bir şey. Veri, kodun gördüğü ve SerializedData, ağ üzerinden gönderilen şeydir. Ardından, ters veri (veya daha çok veri serileştirme / seri hale getirme) bir türden diğerine dönüşüm yapın.
csiz

Yanıtlar:


33

Ben sadece kabul edilebilir değil, özellikle uzantılara izin vermeyi planlıyorsanız teşvik edildiğini söyleyebilirim. Sınıftaki C # uzantılarını desteklemek için, aşağıdaki yorumlara göre yöntemi sanal olarak işaretlemeniz gerekir. Ancak bunu belgelemek isteyebilirsiniz, böylece birileri ReverseData () komutunu geçersiz kılarken şaşırtmasın, ScheduleTransmission () işlevini değiştirir.

Bu gerçekten sınıfın tasarımına bağlı. ReverseData (), sınıfınızın temel davranışları gibi görünür. Bu davranışı başka yerlerde kullanmanız gerekiyorsa, muhtemelen başka sürümlerine sahip olmak istemezsiniz. Yalnızca ScheduleTransmission () özeline ait ayrıntıların ReverseData () içine sızmasına izin vermediğinizden emin olmanız gerekir. Bu problem yaratacak. Ancak bunu zaten sınıf dışında kullandığınız için muhtemelen bunu çoktan düşündünüz.


Vay, bana çok garip geldi ama bu yardımcı oluyor. Başkalarının ne dediğini görmek istersiniz. Teşekkür ederim.
Snoop

2
"ReverseData () geçersiz kılındığında birinin şaşırtmaması için ScheduleTransmission () 'in çalışma şeklini değiştirir" : hayır, gerçekten değil. En azından C # da değil (sorunun C # etiketi olduğunu unutmayın). ReverseData()Soyut olmadığı sürece , çocuk sınıfında ne yaparsanız yapın, her zaman çağrılacak olan orijinal yöntemdir.
Arseni Mourzenko

2
@MainMa Veya virtual... Ve eğer metodu geçersiz kılıyorsanız , tanım gereği virtualveya olması gerekir abstract.
Pokechu22

1
@ Pokechu22: gerçekten de virtualçalışıyor. Her durumda, OP'ye göre imza, public void ReverseData()yani işleri geçersiz kılma hakkındaki cevabın bir kısmı yanıltıcıdır.
Arseni Mourzenko, 14.06.2016

@MainMa, bir temel sınıf yönteminin sanal bir yöntem çağırması durumunda, normal olmadıkça davranmadığını mı söylüyorsunuz abstract? Ben abstractvs cevapları aradım virtualve bu sözü görmedim: oldukça soyut sadece innthis base verilen bir sürüm olmadığı anlamına gelir.
JDługosz

20

Kesinlikle.

Bir yöntemin görünürlüğü, sınıf dışında veya bir çocuk sınıfı içinde bir yönteme erişime izin vermek veya reddetmek için tek bir amacı vardır; Kamusal, korumalı ve özel yöntemler her zaman sınıfın içinde çağrılabilir.

Genel yöntemlerin çağrılmasında yanlış bir şey yoktur. Sorunuzdaki örnek, iki ortak yönteme sahip olmanız gereken tam olarak bir durumun mükemmel bir örneğidir.

Ancak, aşağıdaki desenleri dikkatle izleyebilirsiniz:

  1. Bir yöntem şöyle Hello()çağırır World(string, int, int):

    Hello()
    {
        this.World("Some magic value here", 0, 100);
    }

    Bu kalıptan kaçının. Bunun yerine, isteğe bağlı değişkenleri kullanın . İsteğe bağlı argümanlar keşfedilebilirliği kolaylaştırır: Yazan arayan kişi Hello(, yöntemi varsayılan değerlerle çağırmayı mümkün kılan bir yöntem olduğunu mutlaka bilmez. İsteğe bağlı argümanlar ayrıca kendi kendini belgeliyor. World()Arayan kişiye gerçek varsayılan değerlerin ne olduğunu göstermez.

  2. Bir yöntem şöyle Hello(ComplexEntity)çağırır World(string, int, int):

    Hello(ComplexEntity entity)
    {
        this.World(entity.Name, entity.Start, entity.Finish);
    }

    Bunun yerine, aşırı yük kullanın . Aynı sebep: daha iyi keşfedilebilirlik. Arayan kişi IntelliSense aracılığıyla tüm aşırı yüklenmeleri bir kerede görebilir ve doğru olanı seçebilir.

  3. Bir yöntem, basit bir şekilde herhangi bir önemli değer eklemeden diğer kamu yöntemlerini çağırır.

    İki kere düşün. Bu yönteme gerçekten ihtiyacın var mı? Yoksa onu çıkarmalı ve arayanı farklı yöntemleri çağırsın mı? Bir ipucu: yöntemin adı doğru görünmüyorsa veya bulunması zorsa, muhtemelen kaldırmalısınız.

  4. Bir yöntem girişi doğrular ve ardından diğer yöntemi çağırır:

    Hello(string name, int start, int end)
    {
        if (name == null) throw new ArgumentNullException(...);
        if (start < 0) throw new OutOfRangeException(...);
        ...
        if (end < start) throw new ArgumentException(...);
    
        this.World(name, start, end);
    }

    Bunun yerine, Worldparametrelerinin kendisini doğrulamalı veya özel olmalıdır.


ReverseData gerçekte dahili ise ve "object" örneklerini içeren ad alanı boyunca kullanılmışsa aynı düşünceler geçerli midir?
Snoop

1
@StevieV: evet, elbette.
Arseni Mourzenko

@MainMa 1. ve 2.
Kapol,

@Kapol: Geri bildiriminiz için teşekkür ederiz. İlk iki nokta hakkında açıklama ekleyerek cevabımı değiştirdim.
Arseni Mourzenko

Durum 1'de bile, isteğe bağlı argümanlardan ziyade aşırı yüklemeleri tercih ederim. Seçenekler aşırı yükleme kullanımının bir seçenek olmadığı veya özellikle çok sayıda aşırı yük üretecek şekilde olması durumunda, özel bir tür oluşturacağım ve bunu argüman olarak kullanacağım (öneri 2'deki gibi) veya kodu yeniden gözden geçireceğim.
Brian

8

Eğer bir şey halka açıksa, herhangi bir zamanda herhangi bir sistem tarafından aranabilir. Bu sistemlerden biri olamamak için hiçbir sebep yok!

Yüksek oranda optimize edilmiş kütüphanelerde, ortaya konulan deyimi kopyalamak istiyorsunuz java.util.ArrayList#ensureCapacity(int).

ensureCapacity

  • ensureCapacity halka açık
  • ensureCapacity gerekli tüm sınırların kontrolü ve varsayılan değerleri vb. vardır.
  • ensureCapacity aramalar ensureExplicitCapacity

ensureCapacityInternal

  • ensureCapacityInternal özel mi
  • ensureCapacityInternal Çok az hata kontrolü var, çünkü tüm girişler sınıfın içinden geliyor.
  • ensureCapacityInternal ALSO çağırır ensureExplicitCapacity

ensureExplicitCapacity

  • ensureExplicitCapacity ayrıca özel
  • ensureExplicitCapacitysahip hiçbir hata denetimi
  • ensureExplicitCapacity fiili iş yapar
  • ensureExplicitCapacityensureCapacityve dışındaki hiçbir yerden aranmazensureCapacityInternal

Bu şekilde, güvendiğiniz kodun, girişlerin iyi olduğunu bildiğiniz için, ayrıcalıklı (ve daha hızlı!) Erişim hakkı kazanır. Güvenmediğiniz kod, bütünlüğünü doğrulamak ve bombalamak veya varsayılanlar sağlamak veya başka kötü girdilerle başa çıkmak için belirli bir titizlikten geçer. Her ikisi de gerçekten işe yarayan yere kanal.

Ancak, bu JDK'da en çok kullanılan sınıflardan biri olan ArrayList'te kullanılır. Davanızın bu düzeyde bir karmaşıklık ve titizlik gerektirmemesi çok olası. Uzun lafın kısası, tüm ensureCapacityInternalçağrılar çağrılarla değiştirilseydi ensureCapacity, performans yine de gerçekten çok iyi olurdu. Bu, muhtemelen yalnızca kapsamlı bir değerlendirme yapıldıktan sonra yapılan bir mikrooptimizasyondur.


3

Birkaç iyi cevap zaten verilmiş ve onlarla aynı fikirde olurdum; evet, bir nesnenin genel yöntemlerini diğer yöntemlerinden çağırabilir. Ancak, dikkat etmeniz gereken hafif bir tasarım uyarısı vardır.

Genel yöntemlerin genellikle “nesneyi tutarlı bir durumda al, mantıklı bir şey yap, nesneyi (muhtemelen farklı) tutarlı bir durumda bırak” sözleşmesi vardır. O Burada, "tutarlı", örneğin, anlamına gelebilir Lengtha List<T>onun daha büyük değildir Capacityve gelen endeksleri de unsurları referans olduğu 0için Length-1atmaz.

Ancak, nesnenin yöntemlerinin içinde nesne tutarsız bir durumda olabilir, bu nedenle genel yöntemlerden birini çağırdığınızda çok yanlış bir şey yapabilir, çünkü böyle bir olasılık göz önünde bulundurularak yazılmamıştır. Bu nedenle, genel yöntemlerinizi diğer yöntemlerden aramayı planlıyorsanız, sözleşmelerinin “nesneyi bir durumda almak, mantıklı bir şey yapmak, nesneyi (muhtemelen farklı) (belki de tutarsız) bırakmak - ancak yalnızca başlangıçta olduğundan emin olun. devlet tutarsızdı).


1

Genel bir yöntemi başka bir genel yöntem içinde çağırırken başka bir örnek tamamen iyi bir CanExecute / Execute yaklaşımıdır. Hem doğrulama hem de değişmez korumaya ihtiyacım olduğunda kullanırım .

Ama genel olarak her zaman bu konuda temkinli oluyorum. Bir yöntem a()içinde yöntem olarak adlandırılıyorsa b(), bu yöntemin a()bir uygulama detayı olduğu anlamına gelir b(). Çok sık farklı soyutlama seviyelerine ait olduklarını gösterir. Her ikisinin de halka açık olması beni bir Tek sorumluluk ilkesi ihlali olup olmadığını merak ediyor .


-5

Üzgünüm ama diğer 'evet yapabilirsin' cevaplarının çoğuna katılmam gerekecek ve şunu söyleyeceğim:

Bir kamu yöntemini diğerinden çağıran bir sınıftan vazgeçirebilirim

Bu uygulamayla ilgili birkaç potansiyel sorun var.

1: Kalıtımsal sınıfta sonsuz döngü

Bu nedenle, temel sınıfınız method2'den method1'i çağırır, ancak o zaman siz veya başka biri ondan devralır ve method1'i metod2 olarak adlandırılan yeni bir yöntemle gizler.

2: Olaylar, Kayıt vb.

Örneğin, '1 eklendi!' etkinliğini başlatan bir Add1 yöntemi var. Muhtemelen Add10 yönteminin bu olayı arttırmasını, bir kayıt defterine yazmasını veya her neyse, on defa olmasını istemiyorum.

3: diş açma ve diğer kilitlenmeler

InsertComplexData bir db bağlantısı açar, bir işlem başlatır, bir tabloyu kilitler, Sonra InsertSimpleData'yı çağırır, bir bağlantı açar, bir işlemi başlatır, masanın kilidinin açılmasını bekler ....

Daha fazla neden olduğuna eminim, 'yöntem1'i düzenler ve yöntem 2'nin farklı davranmaya başlamasına şaşırırsın' konusuna değinen başka bir cevap daha vardır.

Genel olarak, kodu paylaşan iki genel yönteminiz varsa, ikisini de diğer birini çağırmak yerine özel bir yöntemle çağırmak daha iyidir.

Düzenle ----

OP'deki spesifik durum üzerinde genişleyelim.

Çok fazla ayrıntıya sahip değiliz, ancak ReverseData'nın ScheduleTransmission yönteminin yanı sıra bir tür olay işleyicisi tarafından çağrıldığını biliyoruz.

Tersine çevirilen verilerin aynı zamanda nesnenin iç durumunu da değiştirdiğini varsayıyorum.

Bu durum göz önüne alındığında, konu güvenliğinin önemli olacağını düşünüyorum ve bu nedenle uygulamaya karşı üçüncü itirazım geçerli.

ReverseData iş parçacığını güvenli hale getirmek için bir kilit ekleyebilirsiniz. Ancak ScheduleTransmission'ın aynı zamanda güvenli bir iş parçacığı olması gerekiyorsa, aynı kilidi paylaşmak isteyeceksiniz.

Bunu yapmanın en kolay yolu, ReverseData kodunu özel bir yönteme taşımak ve her iki genel yöntemin de çağırmasını sağlamaktır. Daha sonra lock deyimini Genel Yöntemler kısmına koyabilir ve bir kilit nesnesini paylaşabilirsiniz.

Belli ki "bunun asla olmayacağını" iddia edebilirsin. veya "Kilidi başka bir şekilde programlayabilirim" fakat iyi kodlama uygulamasının amacı kodunuzu ilk önce iyi yapılandırmaktır.

Akademik açıdan şunu söyleyebilirim ki, bu katı L'yi ihlal ediyor. Kamu yöntemleri sadece kamuya açık olmaktan daha fazlasıdır. Ayrıca mirasçıları tarafından değiştirilebilirler. Kodunuz değişiklik için kapatılmalıdır; bu, hem genel hem de korunan yöntemlerde ne iş yaptığınızı düşünmeniz gerektiği anlamına gelir.

Bir tane daha var: Siz de DDD'yi potansiyel olarak ihlal ediyorsunuz. Nesneniz bir etki alanı nesnesiyse, genel yöntemleri, işletme için bir anlam ifade eden Etki Alanı terimleri olmalıdır. Bu durumda, 'bir düzine yumurta al', bu şekilde başlasa bile, '12 kez 1 yumurta al' ile aynıdır.


3
Bu konular, tasarım prensibinin genel bir kınamasından ziyade, zayıf düşünülmüş mimariye benziyor. (Belki "1 eklenmiş" olarak adlandırmak her zaman iyidir. Olmazsa, farklı bir program yapmalıyız. Belki iki yöntem aynı kaynağı özel olarak kilitlemeye çalışıyorsa, birbirlerini çağırmamalılar, ya da zayıf yazdık .) Şimdiki yerine Kötü uygulamaları sunmaktan , kamu yöntemlerine evrensel bir ilke olarak erişmeyi ele alan daha sağlam tartışmalara odaklanmak isteyebilirsiniz. Örneğin, iyi düzgün kod verildiğinde , neden bu kadar kötü?
doppelgreener 15.03

Kesinlikle, değilse, etkinliği koyacak bir yeriniz yok. Benzer şekilde # 3 olan her şey yolunda, yöntemlerden biri zincir bir işleme ihtiyaç duyuncaya kadar, sonra sen batırılırsın
Ewan

Bu sorunların tümü, kodun kendisinin hatalı olan genel prensipten daha düşük kalitede olduğunu yansıtmaktadır. Her üç vaka da sadece kötü kod. "Bunu yapma, bunu yerine yap" bazı versiyonlarıyla çözülebilirler (belki de "sormakla" mi ? Tam olarak istiyoruz") ve kendi halkı çağırarak gündeme bir sınıfın genel ilkesini demiyorlar yöntemleri. Sonsuz bir döngü için belirsiz potansiyel, burada ilke olarak meseleye giren ve o zaman bile tam olarak ele alınamayan tek potansiyeldir.
doppelgreener

Örnek paylaşımlarımdaki 'kod' olan tek şey bir diğerini çağırmak için kullanılan genel bir yöntemdir. 'Kötü kod' iyi olduğunu düşünüyorsanız! Bu benim tartışmam
Ewan

Kendi elinizi kırmak için bir çekiç kullanmanız, çekiçin kötü olduğu anlamına gelmez. Elini kıracak şeyler yapman gerekmiyor demektir.
doppelgreener 15:16
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.