Üye değişkenini bir metod parametresi olarak geçirmek


33

Bir projede şöyle bir kod buldum:

class SomeClass
{
    private SomeType _someField;

    public SomeType SomeField
    {
        get { return _someField; }
        set { _someField = value; }
    }

    protected virtual void SomeMethod(/*...., */SomeType someVar)
    {
    }

    private void SomeAnotherMethod()
    {
        //.............
        SomeMethod(_someField);
        //.............
    }

};

Takım arkadaşlarımı bunun kötü kod olduğuna nasıl ikna edebilirim?

Bunun gereksiz bir komplikasyon olduğuna inanıyorum. Zaten erişiminiz varsa neden bir üye değişkenini bir yöntem parametresi olarak iletin? Bu aynı zamanda kapsülleme ihlalidir.

Bu kodla ilgili başka herhangi bir sorun görüyor musunuz?


21
Bunun kötü olduğunu düşündüren nedir?
yannis

@Yannis Rizos, sence iyi mu? En azından bu gereksiz bir komplikasyondur. Zaten erişiminiz varsa, neden değişkeni yöntem parametresi olarak iletin? Bu da kapsülleme ihlalidir.
tika

2
Geçerli noktalar, lütfen bunları eklemek için soruyu düzenleyin. Takım arkadaşlarınızı ikna etmenize yardımcı olamayız, ancak kodu değerlendirmenize yardımcı olabiliriz, sorunuz budur.
yannis

8
2 + 2 sabitini kullanan bir metottan farklı değişkenleri toplayabilen bir metoda sahip olmayı tercih ederim.
Dante

Burada önemli olduğunu düşündüğüm bir nokta, bu parametrenin türü. Eğer bir referans tipi ise avantaj göremiyorum ama eğer bir değer tipi ise bence bu değişken tipini değiştirirseniz derleyici sizi kodu kırdığınız yer hakkında uyaracaktır.
Rémi

Yanıtlar:


3

Bunun geçerli bir konu olduğunu düşünüyorum, ancak karışık tepkiler almanızın nedeni, sorunun oluşma biçiminden kaynaklanıyor. Şahsen, ben de argüman olarak geçen üyelerin gereksiz olduğu ve kodu yürüttüğü ekibimde aynı deneyimleri yaşadım. Bir grup üyeyle çalışan bir sınıfımız olurdu, ancak bazı işlevler üyelere doğrudan erişir ve diğer işlevler aynı üyeleri parametrelerle değiştirir (yani tamamen farklı adlar kullanırlar) ve bunu yapmak için kesinlikle teknik bir neden yoktu. Teknik olarak, Kate'in sağladığı bir örneği kastediyorum.

Geri adım atmanızı ve tam olarak parametre olarak geçen üyelere odaklanmak yerine, ekibinizle açıklık ve okunabilirlik üzerine tartışmalar başlatmanızı tavsiye ederim. Ya resmi olarak ya da sadece koridorlarda, bazı kod bölümlerinin okunmasını kolaylaştıran ve diğer kod bölümlerini daha zor yapan şeyleri tartışın. Ardından, ekip olarak çalışmak istediğiniz kalite ölçütlerini veya temiz kod özniteliklerini belirleyin. Sonuçta, yeşil alan projeleri üzerinde çalışırken bile, zamanın% 90'ından fazlasını okuyarak harcıyoruz ve kod yazılır yazılmaz (diyelim ki 10-15 dakika sonra) okunabilirliğin daha da önemli olduğu durumlarda bakıma giriyor.

Buradaki özel örneğiniz için kullanacağım argüman, daha az kodun okunması her zaman daha fazla koddan daha kolay olduğudur. Beyin işlemesi için 3 parametresi olan bir fonksiyon, hiç veya 1 parametresi olmayan bir fonksiyondan zordur. Başka bir değişken adı varsa, kodu okurken beynin başka bir şeyi takip etmesi gerekir. Yani "int m_value" ve sonra "int localValue" u hatırlamak ve birinin gerçekten diğerinin beyniniz için her zaman daha pahalı olduğunu, daha sonra sadece "m_value" ile çalışmak olduğunu hatırlayın.

Daha fazla mühimmat ve fikir almak için Bob Amca'nın Temiz Kodunun bir kopyasını almanızı tavsiye ederim .


Başvurulan kitabın etkisini gördükten sonra indirildi.
Frank Hileman

Her ne kadar küçük bir kısmı çok üzücü olsa da, cevabı yazdıktan 5 yıl sonra, benden sadece 2 internet puanı aldınız, merak uyandıran (belki de kötü) bazı referanslar sunabilecek olursanız, merak ettiğim başka bir küçük kısım daha var. atıfta bulunduğum kitabın sahip olduğu etkisi. Bu adil ve neredeyse bu noktalara değecek gibi görünüyor
DXM

referans kendimim, kişisel olarak diğer geliştiriciler üzerindeki etkiyi görüyoruz. Bu tür kitapların arkasındaki tüm motivasyonlar iyidir, ancak tüm kodların standart dışı yönergelere uyması gerektiğini belirterek (yani, aslında bir amaca hizmet eden kılavuzlar), bu kitapların bazılarının kültüre benzeyen bir eleştirel düşünme kaybına neden olduğu görülmektedir. .
Frank Hileman

beyin işleme zorlukları ile ilgili olarak, bilişsel yük
jxramos

30

Bir üye alanını bir (özel) yöntemde parametre olarak geçirmenin bir gerekçesini düşünebilirim: yönteminizin neye bağlı olduğunu açıkça belirtir.

Sizin de dediğiniz gibi, tüm üye alanları, tüm nesnenin olduğundan, yönteminizin örtük parametreleridir. Ancak, sonucu hesaplamak için tam nesneye gerçekten ihtiyaç var mı? SomeMethodYalnızca bağımlı olan bir iç yöntem varsa , _someFieldbu bağımlılığı açık hale getirmek daha temiz değil mi? Aslında, bu bağımlılığı açık hale getirmek, bu kod parçasını sınıfınızdan gerçekten yeniden aktarabilmenizi de ima edebilir! (Not: Buradaki alıcılardan veya ayarlayıcılardan bahsetmediğimizi, aslında bir şeyleri hesaplayan kodları kabul ettiğimi unutmayın)

Genel yöntemler için aynı argümanı yapmazdım, çünkü arayan kişi sonucu hesaplamak için nesnenin hangi kısmının alakalı olduğunu bilmez veya umursamaz.


2
Kalan örtük bağımlılık ne olurdu? _someFieldÖrneğinizden, sonucu hesaplamak için gereken tek parametre olduğunu varsaymıştım ve biz bunu açıkça belirttik. (Not: bu önemlidir. Bir bağımlılık eklemedik , sadece açık yaptık!)
Andres F.

11
-1 Örnek üyelere dolaylı bağımlılık yoksa, değeri parametre olarak alan statik bir üye olmalıdır. Bu durumda, bir neden bulmuş olsanız da, bunun geçerli bir gerekçe olduğunu sanmıyorum. Bu kötü kod.
Steven Evers

2
@SnOrfus Ben aslında tamamen sınıftan yöntemi yeniden düzenlemeyi önerdi
Andres F.

5
+1. "... ... bu bağımlılığı açık hale getirmek daha temiz değil mi?" Abso-freaking-loutely. Burada kim “küresel değişkenler genel bir kural olarak iyidir” diyebilir? Bu çok COBOL-68. Şimdi beni duy ve sonra bana inan. Önemsiz olmayan kodumuzda, bazen sınıfsal global değişkenlerin kullanıldığı yerlerde açıkça iletmek üzere yeniden düşünürüm. Pooch'u birçok durumda, a) özel bir alanın keyfi bir şekilde kullanılması ve kamusal mallarının kullanılmasıyla berbat ettik. B) alanların dönüşümünü engelleyerek, "bağımlılıkları gizleyerek". Şimdi bunu 3-5 derin miras zinciri ile çarpın.
radarbob

2
@Tarion Bu konuda Bob Amca'ya katılmıyorum. Mümkün olduğunda, yöntemler işlevsellik göstermeli ve sadece açık bağımlılıklara bağlı olmalıdır. (OOP’de genel metotları çağırırken, bu bağımlılıklardan biri this(veya self) dır , fakat bu çağrının kendisi tarafından açık bir şekilde yapılır obj.method(x)). Diğer örtülü bağımlılıklar, nesnenin durumudur; bu genellikle kodun anlaşılmasını zorlaştırır. Mümkün olduğunda - ve gerekçeyle - bağımlılıkları açık ve fonksiyonel bir stilde yapın. Özel yöntemlerde, mümkünse, ihtiyaç duydukları her parametreyi açıkça iletin. Ve evet, onları yenilemek için yardımcı olur.
Andres F.

28

Üye değişkenlerini fonksiyon argümanları olarak özel metotlara - fonksiyon saflığına geçirmek için güçlü bir neden görüyorum . Üye değişkenler etkili bir bakış açısına göre küresel bir durumdur, dahası, yöntemin yürütülmesi sırasında söz konusu üye değişirse değişken bir küresel durumdur. Üye değişken referanslarını yöntem parametreleriyle değiştirerek, bir işlevi etkin bir şekilde saf hale getirebiliriz. Saf fonksiyonlar harici bir duruma bağlı değildir ve hiçbir yan etkiye sahip değildir, her zaman aynı giriş parametreleriyle aynı sonuçları verir - böylece test ve gelecekteki bakımı kolaylaştırır.

Tüm yöntemlerinizi bir OOP dilinde saf yöntemler olarak kullanmanın ne kolay ne de pratik olduğundan emin olun. Ancak, saf olmayan "global" durum yönetimini özel yöntemlerle ayrılmış halde tutarken, karmaşık mantığı saf işleyen yöntemlerle ve değişmez değişkenler kullanarak kod netliği açısından çok kazandığınıza inanıyorum.

Bununla birlikte, fonksiyonun harici olarak çağrılması durumunda, üye değişkenlerin aynı nesnenin ortak bir fonksiyonuna geçirilmesi bence büyük bir kod kokusu teşkil eder.


5
Süpermarkete cevap ver. Hwats ideal olmasına rağmen, bugünün yazılımlarındaki birçok sınıf, "Globals Evil" evresinin kullanıldığı zaman tüm programlardan daha büyük ve çirkindir. Sınıf değişkenleri, tüm pratik amaçlar için sınıf örneği kapsamındaki geneldir. Saf fonksiyonlarda büyük miktarda işin yapılması çok daha test edilebilir ve sağlam bir kod yapar.
mattnz

@mattnz, Hwat'ın bibliyografisine veya ideal programlamaya ilişkin kitaplarından birine link verebileceğiniz bir yol var mı? İnterneti araştırdım ve üzerinde hiçbir şey bulamadım. Google, "neye" otomatik olarak düzeltmeye çalışıyor.
Buttle Butkus

8

İşleve çeşitli zamanlar denirse, bazen bu üye değişkenini iletmek ve bazen başka bir şeyi iletmek, tamamdır. Örneğin, bunu hiç fena saymazdım:

if ( CalculateCharges(newStartDate) > CalculateCharges(m_StartDate) )
{
     //handle increase in charges
}

burada newStartDatebazı yerel değişken ve m_StartDateüye değişkendir.

Bununla birlikte, işlev yalnızca kendisine gönderilen üye değişkeni ile çağrılırsa, bu gariptir. Üye işlevleri, her zaman üye değişkenleri üzerinde çalışır. Üye değişkeninin bir kopyasını almak için (çalıştığınız dile bağlı olarak) bunu yapıyor olabilirler - eğer durum buysa ve bunu göremiyorsanız, tüm süreci açık hale getirmişse kod daha iyi olabilir.


3
Yöntem önemi yok edilir üye değişkeni dışındaki parametrelerle denir. Önemli olan, bu şekilde adlandırılabilmesi. Bir yöntemi oluştururken nihayetinde nasıl bir yöntem çağrılacağını her zaman bilmiyorsunuz.
Caleb

Belki de "if" koşulunu "needHandleIncreaseChages (newStartDate)" ile değiştirmelisiniz ve sonra argümanınız artık geçerli olmaz.
Tarion

2

Kimsenin dokunmadığı şey, SomeMethod'un sanal olarak korunması. Türetilmiş bir sınıfın onu kullanabilmesi ve işlevselliğini yeniden uygulayabilmesi. Türetilmiş bir sınıf, özel değişkene erişemez ve bu nedenle özel değişkene bağlı olan bazı SomeMethod özel bir uygulamasını sağlayamaz. Özel değişkene bağımlılığı almak yerine, beyanda arayan kişinin bunu iletmesini gerektirir.


Kaçırdığınız şey, bu özel üye değişkenin ortak erişimcilere sahip olmasıdır.
tika

Yani, korunan sanal yöntemin özel bir değişkenin kamu erişimine bağımlı olması gerektiğini mi söylüyorsunuz? Ve şu anki kodla ilgili bir sorunun mu var?
Michael Brown

Genel erişimciler kullanılmak üzere yaratılmıştır. Dönemi.
tika

1

GUI çerçeveleri tipik olarak ekranda çizilen şeyleri temsil eden bir tür 'View' sınıfına sahiptir ve bu sınıf genellikle invalidateRect(Rect r)çizim alanının bir kısmını yeniden çizilmesi gereken olarak işaretlemek gibi bir yöntem sağlar . İstemciler bu yöntemi, görünümün bir kısmında güncelleme talep etmek için çağırabilir. Ancak, bir görünüm de şöyle bir yöntem olabilir:

invalidateRect(m_frame);

tüm alanın yeniden çizilmesine neden olmak için. Örneğin, görünüm hiyerarşisine ilk eklendiğinde bunu yapabilir.

Bunu yaparken yanlış bir şey yok - görünümün çerçevesi geçerli bir dikdörtgen ve görünümün kendisi yeniden çizmek istediğini biliyor. View sınıfı, parametre almayan ve bunun yerine görünümün çerçevesini kullanan ayrı bir yöntem sağlayabilir:

invalidateFrame();

Fakat neden daha genel olanı kullanabiliyorsanız bunun için özel bir yöntem ekleyin invalidateRect()? Ya da eğer vermeyi seçseydiniz invalidateFrame(), daha genel olarak bunu muhtemelen uygulardınız invalidateRect():

View::invalidateFrame(void)
{
    invalidateRect(m_frame)
}

Zaten erişiminiz varsa, neden değişkeni yöntem parametresi olarak iletin?

Sen gerektiğini Kendi yönteme parametre olarak örnek değişkenleri geçmesi durumunda yöntem örnek değişkeni üzerinde özel olarak çalışmaz. Yukarıdaki örnekte, görünümün çerçevesi, invalidateRect()yöntem söz konusu olduğunda sadece başka bir dikdörtgendir .


1

Bu yöntem bir yardımcı yöntem ise bu mantıklı. Örneğin, birkaç ücretsiz metin dizesinden benzersiz bir kısa ad türetmeniz gerektiğini söyleyin.

Her dize için ayrı bir uygulama kodlamak istemezsiniz, bunun yerine dizeyi ortak bir yönteme geçirmek mantıklıdır.

Bununla birlikte, yöntem her zaman tek bir üye üzerinde çalışıyorsa, onu parametre olarak iletmek biraz aptalca görünür.


1

Bir sınıfta üye değişkene sahip olmanın temel nedeni, onu bir yere koymanıza izin vermek ve değerinin sınıftaki diğer her yöntem için kullanılabilir olmasını sağlamaktır. Bu nedenle genel olarak, üye değişkenini sınıfın bir metoduna aktarmaya gerek kalmamasını beklersiniz.

Bununla birlikte, üye değişkenini sınıfın başka bir yöntemine aktarmak isteyebileceğiniz birkaç neden düşünebilirim. Birincisi, üye değişkenin değerinin, çağrılan yöntemle kullanıldığında değişmeden kullanılması gerektiğini garanti etmeniz gerekiyorsa, bu yöntemin, işlemdeki bir noktada asıl üye değişken değerini değiştirmesi gerekse bile. İkinci sebep, örneğin bir akıcı sözdizimi uygularken, bir yöntem zinciri kapsamında bir değerin değişmezliğini garanti etmek isteyebilirsiniz.

Bütün bunlar göz önüne alındığında, üye değişkenini sınıfının yöntemlerinden birine iletirseniz, kodun "kötü" olduğunu söyleyecek kadar ileri gitmezdim. Bununla birlikte, örneğin parametrenin yerel bir değişkene atandığı birçok kod çoğaltmasını teşvik edebileceğinden ve ekstra parametreler gerekmediği durumlarda kodda "gürültü" eklediğinden, genellikle ideal olmadığını öneririm. Temiz Kod kitabının hayranıysanız, yöntem parametrelerinin sayısını en düşük seviyede tutmanız gerektiğini ve yalnızca yöntemin parametreye erişmesi için daha mantıklı bir yol bulunmadığını belirtirsiniz. .


1

Bunu düşünürken bana gelen bazı şeyler:

  1. Genel olarak, daha az parametre içeren yöntem imzalarını anlamak daha kolaydır; icat edilmelerinin büyük bir nedeni, uzun parametre listelerini, çalıştıkları verilerle birleştirerek ortadan kaldırmaktı.
  2. Yöntemin imzasını yapmak üye değişkenine bağlı olarak ileride bu değişkenleri değiştirmeyi zorlaştıracaktır, çünkü yalnızca yöntemin içinde değişmeniz gerekmez, aynı zamanda yöntemin adı her yerdedir. Örneğinizdeki SomeMethod korumalı olduğundan, alt sınıfların da değişmesi gerekir.
  3. Dahili sınıflara bağlı olmayan (genel veya özel) yöntemlerin o sınıfa girmeleri gerekmez. Faydalı yöntemlere dahil edilebilirler ve aynı derecede mutlu olabilirler. Bu sınıfın bir parçası olan hiçbir işin yanında değiller. Yöntemi / yöntemleri taşıdıktan sonra başka hiçbir yöntem bu değişkene bağlı değilse, bu değişken de gitmelidir! Büyük olasılıkla değişkenin kendi nesnesi üzerinde olması, üzerinde çalışan ve ana sınıf tarafından bestelenen bu yöntem veya yöntemler üzerinde kendi nesnesi üzerinde olması gerekir.
  4. Verileri sınıfınız gibi çeşitli işlevlere aktarmak, OO tasarımı karşısında global değişkenlerin olduğu yordamsal bir programdır. Bu, üye değişkenlerinin ve üye işlevlerinin amacı değildir (yukarıya bakın) ve sınıfınız çok tutarlı değil gibi geliyor. Verilerinizi ve yöntemlerinizi gruplandırmanın daha iyi bir yolunu öneren bir kod kokusu olduğunu düşünüyorum.

0

Parametre ("argüman") referans veya salt okunur ise, eksiksinizdir.

class SomeClass
{
    protected SomeType _someField;
    public SomeType SomeField
    {
        get { return _someField; }

        set {
          if (doSomeValidation(value))
          {
            _someField = value;
          }
        }
    }

    protected virtual void ModifyMethod(/*...., */ ref SomeType someVar)
    { 
      // ...
    }    

    protected virtual void ReadMethod(/*...., */ SomeType someVar)
    { 
      // ...
    }

    private void SomeAnotherMethod()
    {
        //.............

        // not recommended, but, may be required in some cases
        ModifyMethod(ref this._someField);

        //.............

        // recommended, but, verbose
        SomeType SomeVar = this.someField;
        ModifyMethod(ref SomeVar);
        this.someField = SomeVar;

        //.............

        ReadMethod(this.someField);
        //.............
    }

};

Çoğu geliştirici, genellikle yapıcı yöntemlerinde doğrudan bir değişkenin iç alanlarını atar. Bazı istisnalar var.

"Ayarlayıcıların" yalnızca atanmış değil bazen de sanal yöntemler olabileceğini veya sanal yöntemleri çağırdığını ek yöntemlere sahip olabileceğini unutmayın.

Not: Özelliklerin dahili alanlarını "korumalı" olarak tutmanızı öneririm.

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.