OOP Kodlama stili: yapıcıdaki her şey başlatılsın mı?


14

Kendimi hala bir çırak programcısı olarak görüyorum, bu yüzden her zaman tipik programlama için "daha iyi" bir yol öğrenmek istiyorum. Bugün, iş arkadaşım kodlama tarzımın bazı gereksiz çalışmalar yaptığını ve başkalarından fikir duymak istediğini savundu. Tipik olarak, bir sınıfı OOP dilinde (Genellikle C ++ veya Python) tasarladığımda, başlatmayı iki farklı bölüme ayırırdım:

class MyClass1 {
public:
    Myclass1(type1 arg1, type2 arg2, type3 arg3);
    initMyClass1();
private:
    type1 param1;
    type2 param2;
    type3 param3;
    type4 anotherParam1;
};

// Only the direct assignments from the input arguments are done in the constructor
MyClass1::myClass1(type1 arg1, type2 arg2, type3 arg3)
    : param1(arg1)
    , param2(arg2)
    , param3(arg3)
    {}

// Any other procedure is done in a separate initialization function 
MyClass1::initMyClass1() {
    // Validate input arguments before calculations
    if (checkInputs()) {
    // Do some calculations here to figure out the value of anotherParam1
        anotherParam1 = someCalculation();
    } else {
        printf("Something went wrong!\n");
        ASSERT(FALSE)
    }
}

(veya python eşdeğeri)

class MyClass1:

    def __init__(self, arg1, arg2, arg3):
        self.arg1 = arg1
        self.arg2 = arg2
        self.arg3 = arg3
        #optional
        self.anotherParam1 = None

    def initMyClass1():
        if checkInputs():
            anotherParam1 = someCalculation()
        else:
            raise "Something went wrong!"

Bu yaklaşım hakkında ne düşünüyorsunuz? Başlatma sürecini ayırmaktan kaçınmalı mıyım? Soru sadece C ++ ve Python ile sınırlı değildir ve diğer diller için cevaplar da takdir edilmektedir.





Neden tipik olarak yapıyorsun? Alışkanlık? Bunu yapmak için hiç sebep verdiniz mi?
JeffO

@JeffO Bu alışkanlığı MFC kütüphanesi ile GUI yapmak için çalışırken aldım. CApp, CWindow, CDlg ve benzeri UI ile ilişkili sınıfların çoğunun üzerine yazabileceğiniz ve coorponding iletilerine yanıt veren bir OnInit () işlevi vardır.
Caladbolgll

Yanıtlar:


28

Bazen sorunlu olsa da, yapıcıdaki her şeyi başlatmanın birçok avantajı vardır:

  1. Bir hata olacaksa, mümkün olduğunca çabuk gerçekleşir ve teşhis edilmesi en kolaydır. Örneğin, null geçersiz bir bağımsız değişken değeriyse, yapıcıda test edin ve başarısız olun.
  2. Nesne her zaman geçerli bir durumdadır. Bir iş arkadaşı hata yapamaz ve aramayı unutamaz initMyClass1()çünkü orada değildir . "En ucuz, en hızlı ve en güvenilir bileşenler, orada olmayan bileşenlerdir."
  3. Mantıklı olursa, nesne değişmez hale getirilebilir, bu da birçok avantajı vardır.

2

Kullanıcılarınıza sağladığınız soyutlamayı düşünün.

Neden tek seferde yapılabilecek bir şeyi ikiye bölelım?

Ekstra başlatma, hatırlamak için API'nizi kullanan programcılar için ekstra bir şeydir ve doğru yapmazlarsa yanlış gitmeleri için daha fazlasını sağlar, ancak bu ekstra yük için hangi değer için?

Ölü basit, kullanımı kolay, yanlış soyutlamalar zor gitmek istiyorum. Programlama hatırlanması gereken zahmetsiz şeyler / zıplamak için yeterince zor. API kullanıcılarınızın (sadece kendi API'nizi kullanıyor olsanız bile) başarı çukuruna girmesini istiyorsunuz .


1

Büyük veri alanı hariç her şeyi başlatın. Statik analiz araçları, yapıcıda başlatılmayan alanları işaretler. Bununla birlikte, en üretken / güvenli yol, varsayılan kurucuları olan tüm üye değişkenlere sahip olmak ve yalnızca varsayılan olmayan başlatma gerektiren açık bir şekilde başlatmaktır.


0

Nesnenin iki kategoriye ayrılabilen çok sayıda başlatma işlemine sahip olduğu durumlar vardır:

  1. Değişmez veya sıfırlanması gerekmeyen öznitelikler.

  2. İşlerini yerine getirdikten sonra bazı koşullara bağlı olarak orijinal değerlere (veya geçici değerlere) dönmesi gerekebilecek özellikler, bir tür yumuşak sıfırlama. örneğin bir bağlantı havuzundaki bağlantılar.

Burada başlatmanın ikinci kısmı ayrı bir işlevde tutulur, örneğin InitialiseObject (), ctor'da çağrılabilir.

Aynı işlev, nesneyi atmak ve yeniden oluşturmak zorunda kalmadan yumuşak bir sıfırlama gerekirse daha sonra çağrılabilir.


0

Diğerlerinin söylediği gibi, genellikle yapıcıda başlangıç ​​yapmak iyi bir fikirdir.

Bununla birlikte, bunun özel durumlarda geçerli olabileceği veya olmayabileceği gerekçeleri vardır.

Hata yönetimi

Pek çok dilde, bir kurucuda hata bildirmenin tek yolu bir istisna oluşturmaktır.

Başlatma işleminizin makul bir hata yaratma şansı varsa, örneğin IO içerir veya parametreleri kullanıcı girişi olabilir, o zaman size açık olan tek mekanizma bir istisna oluşturmaktır. Bazı durumlarda, bu istediğiniz şey olmayabilir ve hataya açık kodu ayrı bir başlatma işlevine ayırmak daha mantıklı olabilir.

Proje / organizasyon standardı istisnaları kapatmaksa, bunun en yaygın örneği C ++ 'dadır.

Durum makinesi

Bu, açık durum geçişleri olan bir nesneyi modellediğiniz durumdur. Örneğin, açılabilen ve kapatılabilen bir dosya veya soket.

Bu durumda, nesne yapısının (ve silinmenin) yalnızca belleğe yönelik özniteliklerle (dosya adı, bağlantı noktası vb.) İlgilenmesi yaygındır. Daha sonra, etkin bir şekilde başlatma ve yırtma işlevleri olan açık, kapalı durum geçişlerini özel olarak yönetecek işlevler olacaktır.

Avantajları yukarıdaki gibi hata işlemede, ancak yapıyı başlatmadan ayırmak için bir durum olabilir (bir dosya vektörü oluşturduğunuzu ve senkronize olmayan bir şekilde açtığınızı varsayalım).

Dezavantajı, diğerlerinin söylediği gibi, şimdi sınıf yönetimi kullanıcısına devlet yönetimi yükünü koymanızdır. Sadece inşaat ile başa çıkabiliyorsanız, diyelim ki bunu otomatik olarak yapmak için RAII kullanabilirsiniz.

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.