İnit () yöntemleri bir kod kokusu mu?


20

init()Bir tür için yöntem bildirmenin herhangi bir amacı var mı ?

Bence gerekip gerekmediğini sormuyorum tercih init()yapıcı üzerinde veya nasıl ilan önlemek içininit() .

Ben (ne kadar yaygın olduğunu görmek) bir yöntem bildirme arkasında herhangi bir mantık olup olmadığını init()veya bir kod kokusu ve kaçınılması gerektiğini soruyorum .


init()Deyim oldukça yaygındır, ancak herhangi bir gerçek yararı henüz görmedik.

Bir yöntemle başlatmayı teşvik eden türlerden bahsediyorum:

class Demo {
    public void init() {
        //...
    }
}

Bu ne zaman üretim kodunda kullanılacaktır?


Kısmen oluşturulan bir nesne ile sonuçlanan yapıcı tamamen nesneyi başlatmıyor önerdiğinden bu bir kod kokusu olabilir hissediyorum. Durum ayarlanmadıysa nesne mevcut olmamalıdır.

Bu, kurumsal uygulamalar anlamında üretimi hızlandırmak için kullanılan bir tür tekniğin parçası olabileceğine inanıyor. Böyle bir deyime sahip olmayı düşünebileceğim tek mantıklı sebep, eğer öyleyse bunun nasıl faydalı olacağından emin değilim.


1
"... ne kadar yaygın olduğunu görmek için ...": yaygın mıdır? Bazı örnekler verebilir misiniz? Belki de başlatma ve inşaatı ayırmayı gerektiren bir çerçeve ile uğraşıyorsunuz.
GELMEKTEDİR

Yöntem bir temel sınıfta mı, türetilmiş bir sınıfta mı yoksa her ikisinde mi? (Veya: yöntem miras hiyerarşisine ait bir sınıfta mıdır? Temel sınıf init()türetilmiş sınıfa çağırıyor mu , yoksa tersi mi?) Öyleyse, temel sınıfın bir "post-yapıcı çalıştırmasına izin vermenin bir örneği "ancak en çok türetilmiş sınıf inşaatı bitirdikten sonra yürütülebilir. Çok fazlı başlatma örneğidir.
rwong

Örnekleme noktasında başlangıç ​​yapmak istemiyorsanız, ikisini ayırmak mantıklı olacaktır.
JᴀʏMᴇᴇ


Yanıtlar:


39

Evet, bir kod kokusu. Kod kokusu her zaman kaldırılması gereken bir şey değildir. Bu ikinci bir bakış atmanızı sağlayan bir şey.

Burada temelde farklı iki durumda bir nesneniz var: başlangıç ​​öncesi ve başlangıç ​​sonrası. Bu devletlerin farklı sorumlulukları, çağrılmasına izin verilen farklı yöntemleri ve farklı davranışları vardır. Etkili iki farklı sınıf.

Fiziksel olarak iki ayrı sınıf yaparsanız, belki de modelinizin "gerçek dünya modeli" ile çok yakından eşleşmemesi pahasına, tüm potansiyel hata sınıfını statik olarak kaldırırsınız. Genellikle ilkini isim Configveya Setupböyle falan.

Bir dahaki sefere, yapı init deyimlerinizi iki sınıflı modellere dönüştürmeyi deneyin ve sizin için nasıl ortaya çıktığını görün.


6
İki sınıflı modeli denemeniz iyi. Kod kokusunu gidermek için somut bir adım önermek faydalıdır.
Ivan

1
Bu şimdiye kadarki en iyi cevap. Bir şey beni rahatsız ediyor: " Bu devletlerin farklı sorumlulukları, çağrılmasına izin verilen farklı yöntemleri ve farklı davranışları var " - Bu sorumlulukları ayırmamak SRP'yi ihlal ediyor. Yazılımın amacı her açıdan gerçek bir dünya senaryosunu çoğaltmak olsaydı, bu mantıklı olurdu. Ancak üretimde, devs çoğunlukla kolay yönetilebilirliği teşvik eden bir kod yazıyor, yazılım tabanlı bir
çevreye

1
Gerçek dünya farklı bir çevre. Çoğaltmaya çalışan programlar, çoğu projede hesaba katılmayacağınız / etmeyeceğiniz gibi, alana özgü görünüyor. Alanına özgü projeler söz konusu olduğunda kaşlarını çatmış bir sürü şey kabul edilir, bu yüzden bunu mümkün olduğunca genel tutmaya çalışıyorum (kurucuda çağırmanın nasıl thisbir kod kokusu olduğu ve hataya açık kodla sonuçlanabileceği gibi, ve bundan kaçınmak, projenizin hangi alan adı altında olduğuna bakılmaksızın önerilir).
Vince Emigh

14

Değişir.

Bir inityöntem, yapıcı ayrılan nesne başlatma için gerekli olmadığı zaman bir kod kokusudur. Bazen bu adımları ayırmanın mantıklı olduğu durumlar vardır.

Hızlı bir Google araması bana bu örneği verdi. Kolayca nerede nesne ayırma (yapıcı) sırasında yürütülen kod başlatma daha iyi ayrılabilir daha fazla durum düşünebilirsiniz. Seviyelendirilmiş bir sisteminiz vardır ve tahsis / inşaat X seviyesinde gerçekleşir, ancak başlatma sadece Y seviyesinde olur, çünkü sadece Y gerekli parametreleri sağlayabilir. Belki "init" maliyetlidir ve yalnızca tahsis edilen nesnelerin bir alt kümesi için çalıştırılmalıdır ve bu alt kümenin belirlenmesi yalnızca Y düzeyinde yapılabilir. Veya türetilmiş bir (sanal) "init" yöntemini geçersiz kılmak istiyorsunuz yapıcı ile yapılamayacak bir sınıf. Belki X seviyesi size miras ağacından tahsis edilmiş nesneler sağlar, ancak Y seviyesi somut türetmenin farkında değildir, sadece ortak arayüz (init olabilir).

Tabii ki, tecrübelerime göre, bu vakalar, tüm başlatmanın doğrudan yapıcıda yapılabileceği standart durumun sadece küçük bir yüzdesidir ve ayrı bir inityöntem gördüğünüzde , gerekliliğini sorgulamak iyi bir fikir olabilir.


2
Bu bağlantıdaki cevap, yapıcıdaki iş miktarını azaltmak için yararlı olduğunu söylüyor, ancak kısmen oluşturulan nesneleri teşvik ediyor. Daha küçük kurucular ayrıştırma yoluyla elde edilebilir, bu yüzden bana göre cevaplar yeni bir sorun yaratıyor (gerekli tüm başlatma yöntemlerini çağırmayı unutma potansiyeli, nesneyi hataya eğilimli bırakıyor) ve kod kokusu kategorisine
giriyor

@VinceEmigh: Tamam, ben, SE platfform burada belki değil en uygun olanı bulabiliriz ilk örnek oldu, ama orada olan ayrı kullanmak için geçerli durumlar inityöntemle. Ancak, böyle bir yöntem gördüğünüzde, gerekliliğini sorgulamaktan çekinmeyin.
Doc Brown

Bunun için her kullanım durumunu sorguluyorum / meydan okuyorum, çünkü bunun bir zorunluluk olacağı bir durum olmadığını hissediyorum. Bana göre, nesne yaratma zamanlaması zayıf ve kaçınılmalıdır, çünkü uygun tasarımla önlenebilecek hatalara adaydır. Bir init()yöntemin doğru kullanımı olsaydı, eminim ki amacı hakkında bilgi edinmekten yararlanırdım. Cehaletimi affedin, bunun için ne kadar zorlandığımı ve kaçınılması gereken bir şey olduğunu
düşünmemi engellediğim için şaşkınım

1
@VinceEmigh: Böyle bir durumu düşünemediğinizde, hayal gücünüz üzerinde çalışmanız gerekir ;-). Ya da cevabımı tekrar okuyun, sadece "tahsis" e indirmeyin. Veya farklı satıcılardan daha fazla çerçeveyle çalışın.
Doc Brown

1
@DocBrown Kanıtlanmış uygulamalar yerine hayal gücünü kullanmak, çift ayraç başlatma gibi bazı korkak kodlara yol açar: zeki, ancak verimsiz ve kaçınılmalıdır. Satıcıların kullandığını biliyorum, ancak bu kullanımın haklı olduğu anlamına gelmez. Öyle olduğunu düşünüyorsanız, lütfen nedenini bana bildirin, çünkü anlamaya çalıştığım şey bu. BAZI faydalı bir amaca sahip çıkmış gibi görünüyordunuz, ancak iyi tasarımı teşvik eden bir örnek vermekte zorlandığınız anlaşılıyor. Ben hangi koşullar altında biliyor olabilir kullanılmalıdır, ancak gerektiği kullanılabilmesi?
Vince Emigh

5

Deneyimlerim iki gruba ayrılıyor:

  1. İnit () öğesinin gerçekten gerekli olduğu kod. Bu, bir üst sınıf veya çerçeve sınıfınızın yapıcısının inşaat sırasında tüm bağımlılıklarını almasını engellediğinde ortaya çıkabilir.
  2. İnit () işlevinin kullanıldığı ancak önlenebileceği kod.

Kişisel deneyimlerime göre (1) 'in birkaç örneğini gördüm, ancak (2)' nin daha birçok örneğini gördüm. Sonuç olarak, genellikle bir init () kod kokusu olduğunu varsayıyorum, ancak bu her zaman böyle değildir. Bazen bunun üstesinden gelemezsiniz.

Oluşturucu Desen kullanarak sık sık bir init () için ihtiyaç / arzu kaldırmak yardımcı olabilir bulduk .


1
Bir üst sınıf veya çerçeve, bir türün yapıcı aracılığıyla ihtiyaç duyduğu bağımlılıkları kazanmasına izin vermezse, bir init()yöntem eklemek bunu nasıl çözer? init()Yöntem ya bağımlılıkları kabul etmek parametreleri gerekir, yoksa bağımlılıkları örneğini olurdu içindeinit() ayrıca bir kurucu ile yapabilirdi yöntemi. Bir örnek verebilir misiniz?
Vince Emigh

1
@VinceEmigh: init () bazen bir yapılandırma dosyasını harici bir kaynaktan yüklemek, bir veritabanı bağlantısı açmak veya bu ilkden bir şey yapmak için kullanılabilir. DoFn.initialize () yöntemi (apache Crunch çerçevesinden) bu şekilde kullanılır. Serileştirilemeyen dahili alanları yüklemek için de kullanılabilir (DoFns serileştirilebilir olmalıdır). Buradaki iki sorun, (1) başlatma yönteminin çağrıldığından emin olmak için bir şeylere ihtiyaç duyması ve (2) nesnenin bu kaynakları nereden alacağını (veya nasıl oluşturacağını) bilmesi gerektiğidir.
Ivan

1

Init yöntemi işe yaradığında tipik bir senaryo, değiştirmek istediğiniz bir yapılandırma dosyanızın olması ve uygulamanın yeniden başlatılmadan değişikliğin hesaba katılmasıdır. Bu, elbette, bir Init yönteminin bir kurucudan ayrı olarak çağrılması gerektiği anlamına gelmez. Bir kurucudan bir Init yöntemi çağırabilir ve sonra yapılandırma parametreleri değiştiğinde / değiştiğinde daha sonra çağırabilirsiniz.

Özetlemek gerekirse: çoğu ikilemde olduğu gibi, bunun bir kod kokusu olup olmadığı, duruma ve koşullara bağlıdır.


Yapılandırma güncellemeleri ve bu sıfırlamak için nesneyi gerektiriyorsa / siz bunun bir şekilde nesne hareket olması daha iyi olurdu sanmıyorum yapılandırmasına göre 's durumunu değiştirme gözlemci doğru Config?
Vince Emigh

@Vince Emigh Necesarilly değil. Yapılandırma değiştiğinde kesin anı bilirsem gözlemci çalışırdı. Bununla birlikte, yapılandırma verileri uygulamanın dışında değiştirilebilecek bir dosyada tutulursa, gerçekten zarif bir yaklaşım yoktur. Örneğin, bazı dosyaları ayrıştıran ve verileri dahili bir modele dönüştüren bir programım varsa ve ayrı bir yapılandırma dosyası eksik veriler için varsayılan değerler içeriyorsa, varsayılan değerleri değiştirirsem, bir sonraki çalıştırdığımda bunları tekrar okuyacağım ayrıştırma. Uygulamamda bir Init yöntemi olması bu durumda oldukça kullanışlı olacaktır.
Vladimir Stokic

Yapılandırma dosyası harici olarak çalışma zamanında değiştirilirse, olmayanlar değişkenleri yeniden yolu yoktur bazı onu (init çağırmak gerektiğini başvurunuzu bildiren bildirimin tür update/ reloadaslında bu değişiklikleri kayıt muhtemelen de bu tür davranışlarda için daha açıklayıcı olacaktır) . Bu durumda, bu bildirim, yapılandırma değerlerinin uygulamanızda dahili olarak değişmesine neden olur; bu da, yapılandırmanızın gözlemlenebilir olmasını sağlayarak gözlemlenebileceğine ve yapılandırmanın değerlerinden birini değiştirmesi söylendiğinde gözlemcilere bildirimde bulunacağına inanıyorum. Yoksa örneğinizi yanlış mı anlıyorum?
Vince Emigh

Bir uygulama bazı harici dosyaları (bazı CBS'den veya başka bir sistemden dışa aktarılır) alır, bu dosyaları ayrıştırır ve bunları uygulamanın kullanımlarının bir parçası olduğu bazı dahili modellere dönüştürür. Bu işlem sırasında bazı veri boşlukları bulunabilir ve bu veri boşlukları değerler varsayılan olarak doldurularak doldurulabilir. Bu varsayılan değerler, dönüştürülecek yeni dosyalar olduğunda çağrılan dönüştürme işleminin başlangıcında okunabilen yapılandırma dosyasında saklanabilir. Bu noktada bir Init yöntemi işe yarayabilir.
Vladimir Stokic

1

Bunları nasıl kullandığınıza bağlıdır.

Öbek üzerinde bir nesneyi yeniden tahsis etmek istemediğimde, Java / C # gibi çöp toplanan dillerde bu deseni kullanıyorum (video oyunu yaparken ve performansı yüksek tutmam gerektiğinde, çöp toplayıcıların performansı öldürmesi gibi). Yapıcıyı, ihtiyaç duyduğu diğer yığın ayırmalarını yapmak ve inither yeniden kullanışımdan önce temel kullanışlı durumu oluşturmak için kullanıyorum. Bu, nesne havuzları kavramı ile ilgilidir.

Başlatma talimatlarının ortak bir alt kümesini paylaşan birkaç kurucunuz varsa da yararlıdır, ancak bu durumda initözel olacaktır. Bu şekilde, her yapıcıyı olabildiğince küçültebilirim, böylece her biri sadece benzersiz talimatlar ve initgerisini yapmak için tek bir çağrı içerir .

Genel olarak, bir kod kokusu.


Bir reset()yöntem ilk ifadeniz için daha açıklayıcı olmaz mıydı ? İkincisi (birçok kurucu) gelince, birçok kurucuya sahip olmak bir kod kokusudur. Nesnenin bir SRP ihlali olduğunu gösteren birden fazla amacı / sorumluluğu olduğunu varsayar. Bir nesnenin bir sorumluluğu olmalı ve kurucu o sorumluluk için gerekli bağımlılıkları tanımlamalıdır. İsteğe bağlı değerler nedeniyle birden fazla kurucunuz varsa, bunlar teleskop olmalıdır (bu aynı zamanda bir kod kokusudur, bunun yerine bir kurucu kullanmalıdır).
Vince Emigh

@VinceEmigh init veya reset kullanabilirsiniz, sonuçta sadece bir isim. Init, ilk kullandığımda asla ayarlanmayan bir nesneyi sıfırlamak için pek mantıklı olmadığından, kullanma eğiliminde daha anlamlı. Yapıcı sorununa gelince, çok sayıda kurucuya sahip olmaktan kaçınmaya çalışıyorum, ancak bazen yardımcı oluyor. Herhangi bir dilin stringyapıcı listesine, tonlarca seçeneğe bakın. Benim için genellikle en fazla 3 kurucu olabilir, ancak başlatma olarak talimatların ortak alt kümesi, herhangi bir kodu paylaştıklarında ancak herhangi bir şekilde farklı olduklarında mantıklıdır.
Cody

İsimler son derece önemlidir. Müşteriyi sözleşmeyi okumaya zorlamadan gerçekleştirilen davranışı tanımlarlar. Kötü adlandırma yanlış varsayımlara yol açabilir. Birden fazla kurucuya gelince String, bu, dizelerin yaratılmasıyla çözülebilir. Sonunda, a Stringa'dır Stringve yapıcısı yalnızca gerektiğinde çalışması için gerekenleri kabul etmelidir. Bu kurucuların çoğu, kurucuların yanlış kullanımı olan dönüşüm amaçları için maruz kalmaktadır. Kurucular gerektiğini değil mantığını gerçekleştirmek, ya da bir işe yaramaz nesne ile ayrılırken, başarısız başlatma risk.
Vince Emigh

JDK korkunç tasarımlarla dolu, başımın üstünden yaklaşık 10 tane listeleyebilirim. Yazılım tasarımı, birçok dilin temel yönleri kamuya açık olduğu için gelişmiştir ve modern zamanlar için yeniden tasarlanacaksa kod kırılma olasılığı nedeniyle oyalanmaktadır.
Vince Emigh

1

init()diğer kaynaklar tarafından eşzamanlı olarak kullanılan harici kaynaklara (örneğin bir ağ bağlantısı gibi) ihtiyaç duyduğunuz nesneler olduğunda yöntemler oldukça anlamlı olabilir. Nesnenin ömrü boyunca kaynağı barındırmak istemeyebilirsiniz / ihtiyacınız olmayabilir. Bu gibi durumlarda, kaynak ayırma işleminin başarısız olması muhtemel olduğunda kaynağı yapıcıda ayırmak istemeyebilirsiniz.

Özellikle gömülü programlamada, belirleyici bir bellek ayak izine sahip olmak istersiniz, bu nedenle kurucularınızı erken, belki de statik olarak çağırmak ve yalnızca belirli koşullar karşılandığında daha sonra başlatmak yaygın bir uygulamadır (iyi?).

Bu gibi durumlar dışında her şeyin bir kurucuya girmesi gerektiğini düşünüyorum.


Tür bir bağlantı gerektiriyorsa, DI'yi kullanmalısınız. Bağlantı oluşturulurken bir sorun varsa, bunu gerektiren nesneyi oluşturmamalısınız. Sınıf içinde bir bağlantı oluşturmayı iterseniz, bir nesne oluşturacaksınız, bu nesne bağımlılıklarını (bağlantı) somutlaştıracaktır. Bağımlılıkların somutlaştırılması başarısız olursa, kullanamayacağınız bir nesne ile sarılırsınız, böylece kaynakları boşa harcarsınız.
Vince Emigh

Şart değil. Sonunda geçici olarak tüm amaçlar için kullanamayacağınız bir nesne ile sonuçlanırsınız . Bu gibi durumlarda, nesne, kaynak kullanılabilir hale gelene kadar bir kuyruk veya proxy olarak işlev görebilir. Tamamen kınama inityöntemleri çok kısıtlayıcı, IMHO.
tofro

0

Genellikle işlevsel bir örnek için gerekli olan tüm argümanları alan bir kurucuyu tercih ederim. Bu, o nesnenin tüm bağımlılıklarını netleştirir.

Öte yandan, parametresiz bir genel kurucu ve bağımlılıkları ve yapılandırma değerlerini enjekte etmek için arabirimler gerektiren basit bir yapılandırma çerçevesi kullanıyorum. Bu yapıldıktan sonra, yapılandırma çerçevesi initnesnenin yöntemini çağırır : şimdi sizin için sahip olduğum her şeyi aldınız, çalışmaya hazırlanmak için son adımları izleyin. Ancak not: init yöntemini otomatik olarak çağıran yapılandırma çerçevesi, bu şekilde çağırmayı unutmayacaksınız.


0

İnit () - yöntemi nesnenin durum yaşam döngüsüne anlamsal olarak katıştırılmışsa kod kokusu yoktur.

Nesneyi tutarlı bir duruma getirmek için init () öğesini çağırmanız gerekiyorsa, bu bir kod kokusudur.

Böyle bir yapının var olmasının birkaç teknik nedeni vardır:

  1. çerçeve kancası
  2. nesneyi başlangıç ​​durumuna sıfırlama (fazlalıktan kaçınma)
  3. testler sırasında geçersiz kılma olasılığı

1
Ama neden bir yazılım mühendisi onu yaşam döngüsüne yerleştirdi? Haklı (kısmen inşa edilmiş nesneleri teşvik ettiğini düşünerek) ve daha verimli bir alternatif tarafından düşürülemeyen herhangi bir amaç var mı? Yaşam döngüsünde gömme bir kod kokusu olurdu ve nesne oluşturma daha iyi zamanlaması ile değiştirilmesi gerektiğini hissediyorum (neden o zaman kullanmayı planlamıyorsanız bir nesne için bellek ayırın? nesneyi oluşturmadan önce gerçekten ihtiyacınız olana kadar bekleyebildiğiniz zaman kısmen inşa edilmiş bir nesne mi?)
Vince Emigh

Mesele, init yöntemini çağırmadan ÖNCE nesnenin kullanılabilir olması gerektiğidir. Belki çağrıldıktan sonra başka bir şekilde. Durum düzenine bakın. Kısmi nesne yapımı anlamında bir kod kokusudur.
oopexpert

-4

İnit ismi bazen opak olabilir. Bir araba ve bir motor alalım. Aracı çalıştırmak için (sadece güç verin, radyo dinlemek için) tüm sistemlerin çalışmaya hazır olduğunu doğrulamak istersiniz.

Böylece bir motor, kapı, tekerlek vb. İnşa edersiniz. Ekranınız motor = kapalı gösterir.

Hepsi pahalı olduğundan motoru vb. İzlemeye başlamanıza gerek yoktur. Daha sonra ateşlemek için anahtarı çevirdiğinizde motor-> start'ı arayın. Tüm pahalı süreçleri çalıştırmaya başlar.

Şimdi motor = açık. Ve ateşleme süreci başlar.

Otomobil, motor mevcut olmadan çalışmayacaktır.

Motoru karmaşık hesaplamanızla değiştirebilirsiniz. Excel hücresi gibi. Tüm hücrelerin her zaman tüm olay işleyicilerinde etkin olması gerekmez. Bir hücreye odaklandığınızda, onu başlatabilirsiniz. Performansı bu şekilde arttırmak.

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.