Genellikle nesneleri veya üye değişkenlerini işlevlere gönderiyor musunuz?


31

Bu iki vaka arasında genel olarak kabul edilen uygulama hangisidir:

function insertIntoDatabase(Account account, Otherthing thing) {
    database.insertMethod(account.getId(), thing.getId(), thing.getSomeValue());
}

veya

function insertIntoDatabase(long accountId, long thingId, double someValue) {
    database.insertMethod(accountId, thingId, someValue);
}

Başka bir deyişle, nesnelerin tamamını etrafa veya sadece ihtiyacınız olan alanlara aktarmak genellikle daha iyi olur mu?


5
Tamamen, işlevin ne için olduğuna ve söz konusu nesneyle ne kadar ilişkili (veya ne alakası olmadığına) bağlı olacaktır.
MetaFight

İşte sorun bu. Birini veya diğerini ne zaman kullanacağımı söyleyemem. Her iki yaklaşıma da uyum sağlamak için her zaman kodu değiştirebileceğimi hissediyorum.
AJJ

1
API terimlerinde (ve uygulamalara hiç bakılmıyorsa), eski soyut ve etki alanı yönelimlidir (iyi olan), ikincisi değildir (kötü olan).
Erik Eidt

1
İlk yaklaşım daha fazla 3 katmanlı OO olacaktır. Ancak, kelime veritabanını yöntemden kaldırarak daha da fazla olması gerekir. "Mağaza" veya "Kalıcı" olmalı ve Hesap veya Şey yapmalı (ikisini birden değil). Bu katmanın bir müşterisi olarak, depolama ortamının farkında olmamalısınız. Bir Hesap alırken, istenen nesneyi tanımlamak için kimliğe veya özellik değerlerinin bir kombinasyonuna (alan değerleri değil) geçmeniz gerekir. Veya / ve tüm hesapları geçen bir numaralandırma yöntemi uygular.
Martin Maat

1
Tipik olarak, her ikisi de yanlış olurdu (ya da en iyisi yerine, daha az). Nasıl genellikle doğrudan nesnenin üyesi değişkene bağlıdır çünkü bir nesne veritabanına dizgeleştirilecek, nesnesinin bir özelliği (üye fonksiyonu) olmalıdır. Nesnenin üyelerini değiştirirseniz, serileştirme yöntemini de değiştirmeniz gerekecektir. Bu, nesnenin bir parçasıysa daha iyi çalışır
17'ya kadar

Yanıtlar:


24

Hiçbiri diğerinden daha iyi değildir. Durum bazında yapmanız gereken bir karar verme çağrısı.

Fakat pratikte, bu kararı gerçekten verebileceğiniz bir pozisyondayken, program programındaki hangi katmanın nesneyi ilkellere ayırması gerektiğine karar verdiğiniz için, tüm çağrı hakkında düşünmeniz gerekir. yığın , şu an içinde bulunduğunuz sadece bir yöntem değil. Muhtemelen, ayrılmanın bir yerde yapılması gerekiyor ve bunu birden fazla yapmak mantıklı olmaz (ya da gereksiz yere hataya açık olurdu). Soru, o yerin nerede olması gerektiğidir.

Bu kararı vermenin en kolay yolu , nesnenin değişmesi durumunda hangi kodun değiştirilmesi gerekip gerekmediğini düşünmektir . Örneğinizi biraz genişletelim:

function addWidgetButtonClicked(clickEvent) {
    // get form data
    // get user's account
    insertIntoDatabase(account, data);
}
function insertIntoDatabase(Account account, Otherthing data) {
    // open database connection
    // check data doesn't already exist
    database.insertMethod(account.getId(), data.getId(), data.getSomeValue());
}

vs

function addWidgetButtonClicked(clickEvent) {
    // get form data
    // get user's account
    insertIntoDatabase(account.getId(), data.getId(), data.getSomeValue());
}
function insertIntoDatabase(long accountId, long dataId, double someValue) {
    // open database connection
    // check data doesn't already exist
    database.insertMethod(accountId, dataId, someValue);
}

İlk versiyonda, UI kodu kör bir şekilde datanesneyi geçiyor ve faydalı alanları çıkartmak için veritabanı koduna bağlı. İkinci versiyonda, UI kodu datanesneyi kullanışlı alanlarına bölüyor ve veritabanı kodu nereden geldiklerini bilmeden doğrudan alıyor. Temel ima, nesnenin yapısının bir dataşekilde değişmesi durumunda, ilk sürümün yalnızca veritabanı kodunun değişmesi, ikinci sürümün ise yalnızca UI kodunun değişmesini gerektirmesidir . Bu ikisinden hangisinin doğru olduğu, büyük ölçüde datanesnenin ne tür veriler içerdiğine bağlıdır, ancak genellikle çok açıktır. Örneğin, eğerdata"20/05/1999" gibi, kullanıcı tarafından sağlanan bir dizedir, iletmeden Dateönce uygun bir türe dönüştürmek UI koduna kadar olmalıdır .


8

Bu ayrıntılı bir liste değildir, ancak bir nesnenin bir yönteme argüman olarak geçip geçmemesi gerektiğine karar verirken aşağıdaki faktörlerden bazılarını göz önünde bulundurun:

Nesne değişmez mi? İşlev 'saf' mı?

Yan etkiler , kodunuzun korunmasında önemli bir husustur. Her yerde etrafta çok fazla değişken durumsal nesne geçirilen bir kod gördüğünüzde, bu kod genellikle daha az sezgiseldir (aynı şekilde, küresel hal değişkenlerinin de çoğu zaman daha az sezgisel olabileceği şekilde) ve hata ayıklama genellikle daha zor ve zaman alır; tüketen.

Genel bir kural olarak, bir metoda ilettiğiniz tüm nesnelerin açıkça değiştirilemez olmasını mümkün olduğunca makul bir şekilde sağlamayı amaçlayın.

Bir fonksiyon çağrısının sonucu olarak bir argüman durumunun değiştirilmesi beklenen herhangi bir tasarımdan kaçının (yine mümkün olduğu kadar) kaçının - bu yaklaşım için en güçlü argümanlardan biri En Az Şaşkınlık İlkesidir ; Örneğin, kodunuzu okuyan ve bir işleve geçirilen bir argüman gören biri, işlev döndükten sonra durumunun değişmesini beklemenin daha az olası olduğunu gösterir.

Yöntemin zaten kaç argümanı var?

Aşırı uzun argüman listeleri olan yöntemler (bu argümanların çoğunun 'varsayılan' değerleri olsa bile) kod kokusu gibi görünmeye başlar. Ancak bazen bu tür fonksiyonlar gerekli olabilir ve tek amacı Parametre Nesnesi gibi davranmak olan bir sınıf oluşturmayı düşünebilirsiniz .

Bu yaklaşım, 'kaynak' nesnenizden parametre nesnesine az miktarda ek eşleştirme kod eşlemesi içerebilir, ancak bu hem performans hem de karmaşıklık açısından oldukça düşük bir maliyettir, ancak ayrıştırma açısından bir takım avantajlar vardır. ve nesne değişmezliği.

İletilen nesne yalnızca uygulamanızdaki bir "katmana" mı ait (örneğin, bir ViewModel veya bir ORM Varlığı?)

Endişelerin Ayrılmasını (SoC) düşünün . Bazen, nesnenin, yönteminizin bulunduğu katman veya modüle "ait olup olmadığını" sormak (örneğin, el ile sarılmış bir API sarmalayıcı kitaplığı veya çekirdek Business Logic Layer, vb.), Bu nesnenin gerçekten kendisine geçirilmesi gerekip gerekmediğini bildirebilir. yöntem.

SoC, temiz, gevşek bağlanmış, modüler bir kod yazmak için iyi bir temeldir. örneğin, bir ORM varlık nesnesi (kodunuzla Veritabanı şemanız arasında eşleme) ideal olarak iş katmanınızda dolaşılmamalı veya sunum / kullanıcı arayüzünüzde daha kötü olmamalıdır.

'Katmanlar' arasında veri iletilmesi durumunda, bir yönteme düz veri parametrelerinin geçirilmesi genellikle bir nesnede 'yanlış' katmandan geçilmesi yerine tercih edilir. Her ne kadar muhtemelen 'doğru' katmanında yer alan ayrı modellere sahip olmanız iyi bir fikir olabilir.

Fonksiyonun kendisi çok mu büyük ve / veya karmaşık mı?

Bir işlev çok fazla veri öğesine ihtiyaç duyduğunda, bu işlevin çok fazla sorumluluk üstlenip üstlenmediğini dikkate almaya değer olabilir; daha küçük nesneler ve daha kısa, daha basit fonksiyonlar kullanarak yeniden refactor için potansiyel fırsatları araştırın.

İşlev bir komut / sorgu nesnesi mi olmalı?

Bazı durumlarda, verilerle işlev arasındaki ilişki yakın olabilir; Bu durumlarda, bir Komut Nesnesi veya bir Sorgu Nesnesi'nin uygun olup olmadığını göz önünde bulundurun .

Bir yönteme bir nesne parametresi eklemek, içeren sınıfı yeni bağımlılıkları benimsemeye zorlar mı?

Bazen "Eski verileri düzleştir" argümanları için en güçlü argüman, alıcı sınıfın zaten tamamen kendi kendine yeten olmasıdır ve yöntemlerinden birine bir nesne parametresi eklemek, sınıfı kirletir (veya sınıf zaten kirliyse, o zaman var olan entropiyi daha da kötüleştirir)

Gerçekten tam bir nesneyi mi geçmeniz gerekiyor yoksa o nesnenin arabiriminin sadece küçük bir bölümüne mi ihtiyacınız var?

İşlevlerinize ilişkin Arabirim Ayrıştırma Prensibi'ni göz önünde bulundurun - yani bir nesneye geçerken, yalnızca bu argümanın arayüzünün gerçekte ihtiyaç duyduğu kısımlarına bağlı olmalıdır.


5

Dolayısıyla, bir işlev oluşturduğunuzda, dolaylı olarak çağıran kodla bir sözleşme imzaladığınızı beyan ediyorsunuz. "Bu işlev bu bilgiyi alır ve onu başka bir şeye dönüştürür (muhtemelen yan etkileri vardır)".

Yani, sözleşme mantıklı veya alanlar ile nesnelerin (ancak bunlar hayata konum) ile olmalıdır sadece bu yüzden gerçekleşmesi bu diğer nesnelerin parçası olmak. Her iki şekilde de kuplaj ekliyorsunuz, ancak programcı olarak, nereye ait olduğuna karar vermek size kalmış.

Genel olarak , eğer net değilse, fonksiyonun çalışması için gerekli en küçük verileri tercih edin. Bu, genellikle sadece alanlara geçmek anlamına gelir, çünkü işlev nesnelerde bulunan diğer öğelere ihtiyaç duymaz. Ancak bazen tüm nesneyi almak daha doğrudur çünkü gelecekte işler kaçınılmaz olarak değiştiğinde daha az etki yaratır.


Neden yöntem adını insertAccountIntoDatabase olarak değiştirmiyorsunuz veya başka herhangi bir tür geçecek misiniz? Obj kullanmak için belirli sayıda argüman kodu okumak için kolay. Sizin durumunuzda, eğer yöntem adı nasıl yapacağım yerine ne ekleyeceğimi netleştirecekse, düşünmeyi tercih ederim.
Laiv

3

Değişir.

Ayrıntılı olarak, yönteminizin kabul ettiği parametrelerin, yapmaya çalıştığınız şeyle anlamsal olarak eşleşmesi gerekir. EmailInviterBir inviteyöntemin bu ve üç olası uygulamasını düşünün :

void invite(String emailAddressString) {
  invite(EmailAddress.parse(emailAddressString));
}
void invite(EmailAddress emailAddress) {
  ...
}
void invite(User user) {
  invite(user.getEmailAddress());
}

Girmeniz Stringgereken bir yere geçmek EmailAddresshatalı, çünkü tüm dizeler e-posta adresleri değil. EmailAddressSınıf daha iyi semantik yöntemin davranışını eşleşir. Bununla birlikte a'yı geçmek Userde ayrıca kusurludur, çünkü neden dünyadakiler EmailInviterdavetkar kullanıcılarla sınırlandırılmalıdır? Peki ya işletmeler? Ya bir dosyadan veya komut satırından e-posta adreslerini okuyorsanız ve bunlar kullanıcılarla ilişkili değilse? Posta listeleri? Liste devam ediyor.

Burada rehberlik etmek için kullanabileceğiniz birkaç uyarı işareti vardır. Tüm dizeler veya inçler gibi basit bir değer türü kullanıyorsanız Stringveya intbunlar geçerli değilse veya bunlar hakkında "özel" bir şey varsa, daha anlamlı bir tür kullanmalısınız. Bir nesneyi kullanıyorsanız ve yaptığınız tek şey bir alıcı aramak, bunun yerine nesneyi doğrudan alıcıya iletmeniz gerekir. Bu kurallar ne zor ne de hızlı olmakla birlikte, birkaç kural da yoktur.


0

Temiz Kod, olabildiğince az argüman almanızı önerir; bu, Nesnenin genellikle daha iyi bir yaklaşım olacağı anlamına gelir ve bence biraz mantıklı olur. Çünkü

insertIntoDatabase(new Account(id) , new Otherthing(id, "Value"));

daha okunabilir bir arama

insertIntoDatabase(myAccount.getId(), myOtherthing.getId(), myOtherthing.getValue() );

orada anlaşamam. İkisi eşanlamlı değil. Yalnızca bir yönteme aktarmak için 2 yeni nesne örneği oluşturmak iyi değildir. Seçenekleriniz yerine insertIntoDatabase (myAccount, myOtherthing) kullanırdım.
17'de

0

Nesnenin etrafında, kurucu devletinde değil. Bu, nesneye yönelik kapsülleme ve veri gizleme ilkelerini destekler. Bir nesnenin iç yapılarını, gerekmediği çeşitli yöntem arayüzlerinde açığa çıkarmak, temel OOP ilkelerini ihlal eder.

Alanları değiştirirseniz ne olur Otherthing? Belki bir tür değiştirir, bir alan ekler veya bir alanı kaldırırsınız. Şimdi sorunuzda bahsettiğiniz gibi tüm yöntemlerin güncellenmesi gerekiyor. Nesnenin etrafından geçirirseniz, arabirim değişikliği olmaz.

Bir nesneyi alan kabul eden bir yöntem yazmanız gereken tek şey nesneyi almak için bir yöntem yazarken:

public User getUser(String primaryKey) {
  return ...;
}

Bu aramayı yapma noktasında, arama kodunun henüz nesneye referansı yoktur, çünkü bu yöntemi çağırmanın amacı nesneyi almaktır.


1
"Alanları değiştirirseniz ne olur Otherthing?" (1) Bu, açık / kapalı ilkenin ihlali olur. (2) tüm nesneyi geçseniz bile, içindeki kod o nesnenin üyelerine erişir (ve değilse neden nesneyi
David Arno

@DavidArno cevabımın amacı hiçbir şeyin kırılmayacağı değil, daha azının kırılacağıdır. İlk paragrafı da unutma: ne olursa olsun, bir nesnenin iç durumu nesnenin arayüzü kullanılarak soyutlanmalıdır. İç durumunun etrafından geçmek, OOP ilkelerinin ihlalidir.

0

Sürdürülebilirlik perspektifinden bakıldığında, argümanlar birbirinden açıkça tercihen derleyici düzeyinde ayırt edilmelidir.

// this has exactly one way to call it
insertIntoDatabase(Account ..., Otherthing ...)

// the parameter order can be confused in practice
insertIntoDatabase(long ..., long ...)

İlk tasarım böceklerin erken tespit edilmesine yol açar. İkinci tasarım, sınamada görünmeyen ince çalışma zamanı sorunlarına yol açabilir. Bu nedenle ilk tasarım tercih edilmelidir.


0

İkisinden tercihim ilk yöntem:

function insertIntoDatabase(Account account, Otherthing thing) { database.insertMethod(account.getId(), thing.getId(), thing.getSomeValue()); }

Bunun nedeni, değişikliklerin bu alıcıları koruduğu ve böylece nesnenin dışına saydam olduğu sürece değişikliklerin yolun dışına doğru yapılması, uygulamanın bozulması ve test edilmesi için daha az kodunuz ve uygulamanın aksatılması olasılığının düşük olması.

Bu sadece benim düşünce sürecimdir. Çoğunlukla bu doğadaki işleri nasıl yapmayı ve yapılandırmayı sevdiğime ve uzun vadede oldukça yönetilebilir ve sürdürülebilir olduğunun kanıtlanmasına dayanır.

Sözleşmelerin isimlendirilmesine girmeyeceğim ancak bu yöntemin içinde “veritabanı” kelimesi olmasına rağmen, depolama mekanizmasının yolda değişebileceğini belirtmek isterim. Gösterilen koddan, işlevi kullanılan veritabanı depolama platformuna bağlayan hiçbir şey yoktur - veya bir veritabanı olsa bile. Az önce varsaydık çünkü bu isimde. Yine, bu alıcıların her zaman korunduğunu varsaymak, bu nesnelerin nerede / nerede depolandığını değiştirmek kolay olacaktır.

İşlevi ve iki nesneyi yeniden düşünürdüm, çünkü iki nesne yapısına ve özellikle kullanılan alıcılara bağımlı olan bir işleve sahipsiniz. Aynı zamanda bu fonksiyon, bu iki nesneyi ısrar eden tek bir kümülatif şeye bağlıyor gibi görünüyor. Bağırsaklarım bana üçüncü bir nesnenin anlamlı olabileceğini söylüyor. Bu nesneler hakkında ve onların gerçeklerle ve beklenen yol haritasındaki ilişkileriyle ilgili daha fazla bilgiye ihtiyacım var. Ama bağırsaklarım bu yöne yaslanmış.

Kod şu anda olduğu gibi, soru şu şekilde beliriyor: "Bu fonksiyon nerede olmalı veya olmalı?" Hesabın bir parçası mı, yoksa OtherThing mi? Nereye gidiyor?

Sanırım zaten üçüncü bir "veritabanı" nesnesi var ve bu işlevi bu nesnenin içine koyma eğilimindeyim ve sonra bir Hesap ve bir OtherThing işleyebilmek, dönüştürmek ve sonuçlarına devam edebilmek için nesneler işi olur. .

Eğer 3. nesnenin nesne-ilişkisel haritalama (ORM) modeline uymasını sağlamak için gitmiş olsaydınız en iyisidir. Bu, “Ah, burası Account ve OtherThing'in bir araya gelip ısrar ettiği yer” i anlamak için kodla çalışan herkes için çok açık bir fikir olacaktır.

Ancak, bir Hesabı ve bir OtherThing'i birleştirme ve dönüştürme işini yürüten, ancak ısrar etme mekaniğiyle ilgilenmeyen ileri bir nesneyi ortaya koymak da mantıklı olabilir. Bunu, bu iki nesne ile veya arasında çok daha fazla etkileşim olacağını düşünüyorsanız, çünkü bu büyüdükçe, ısrar bitlerinin sadece ısrarı yöneten bir nesneye çarpan olmasını istiyorum.

Tasarımı, Hesabın, OtherThing'in veya üçüncü ORM nesnesinin herhangi birinin diğer üçünü de değiştirmek zorunda kalmadan değiştirilebileceği şekilde tutmak için çekerdim. Olmamak için iyi bir neden olmadığı sürece, Hesap ve Diğer İşlemlerin bağımsız olmasını ve birbirlerinin içsel çalışmalarını ve yapılarını bilmek zorunda kalmamasını isterdim.

Elbette, bunun olacağı bağlamı bilseydim, fikirlerimi tamamen değiştirebilirdim. Yine, böyle şeyleri gördüğümde düşündüğüm şey budur ve ne kadar yalın.


0

Her iki yaklaşımın da kendi avantajları ve dezavantajları vardır. Bir senaryoda daha iyi olan, eldeki kullanım durumuna çok bağlıdır.


Pro Çoklu params, Con Nesne referansı:

  • Arayan belirli bir sınıfa bağlı değil , tamamen farklı kaynaklardan gelen değerleri tamamen geçebilir
  • Nesne durumu, yöntem yürütme içinde beklenmedik biçimde değiştirilmeden güvenlidir .

Pro Nesne başvurusu:

  • Yöntemin Nesne referans türüne bağlı olduğunu net bir şekilde netleştirerek, ilgisiz / geçersiz değerleri yanlışlıkla geçmeyi zorlaştırır
  • Bir alanın / alıcının yeniden adlandırılması, yalnızca uygulanmasında değil, yöntemin tüm çağrılarında değişiklik yapılmasını gerektirir.
  • Bir Eğer yeni özellik eklenmiş ve ihtiyaçları geçirilecek, hiçbir değişiklik yöntem imzası gerekli
  • Yöntem nesne durumunu değiştirebilir
  • Benzer ilkel türlerin çok fazla değişkenini geçmek , arayanı sırayla ilgili olarak kafa karıştırıcı hale getirir (Builder pattern problemi)

Öyleyse, neyin kullanılması gerektiği ve ne zaman kullanım durumlarına bağlı olduğu

  1. Bireysel parametreleri geç: Genel olarak, yöntemin nesne türüyle bir ilgisi yoksa, bireysel parametre listesinin daha geniş bir kitleye uygulanabilmesi için geçirilmesi daha iyidir .
  2. Yeni model nesnesini tanıtın: Parametre listesi büyükse (3'ten fazla) büyürse, API denilen yeni bir model nesnesini tanıtmak daha iyidir (bu oluşturucu deseni tercih edilir)
  3. Nesne Referansını İletme: Yöntem, etki alanı nesneleriyle ilişkiliyse, nesne referanslarını iletmek için bakım ve okunabilirlik açısından daha iyidir.

0

Bir tarafta bir Hesabınız ve bir Başka Şey nesneniz var. Diğer taraftan, bir hesap kimliği ve bir Başka Şey kimliği göz önüne alındığında veritabanına bir değer girme olanağına sahipsiniz. Bu verilen iki şey.

Hesap ve Diğer şeyleri argüman olarak alarak bir yöntem yazabilirsiniz. Profesyonel tarafta, arayan kişinin Hesap ve Diğer şeyler hakkında herhangi bir ayrıntı bilmesi gerekmez. Olumsuz tarafı, callee Hesap ve Diğer şeylerin yöntemleri hakkında bilmek gerekiyor. Ayrıca, bir veritabanına, bir Anotherthing nesnesinin değerinden daha başka bir şey eklemenin bir yolu yoktur ve bir hesap nesnesinin kimliğine sahipseniz, ancak bu yöntemi kullanmanın hiçbir yolu yoktur, ancak nesnenin kendisi değildir.

Veya iki id alarak bir yöntem ve argümanlar olarak bir değer yazabilirsiniz. Olumsuz tarafta, arayan kişinin Hesap ve Başka Şeylerin detaylarını bilmesi gerekir. Üstelik, bir Hesap veya Başka Bir Şey hakkında daha fazla ayrıntıya ihtiyaç duyduğunuzda, veritabanına eklemek için sadece bir kimlik gösterilmeyebilir, bu durumda bu çözüm tamamen yararsızdır. Öte yandan, umarım callee'de Hesap ve Diğer Her Şey bilgisine ihtiyaç duyulmaz ve daha fazla esneklik vardır.

Karar çağrınız: Daha fazla esneklik gerekli mi? Bu genellikle bir arama meselesi değildir, ancak tüm yazılımınız boyunca tutarlı olacaktır: Ya Hesap kimliklerini çoğu zaman kullanırsınız ya da nesneleri kullanırsınız. Karıştırmak sizi iki dünyanın da en kötüsüne götürür.

C ++ 'da iki kimliği artı değeri alan bir yönteminiz ve Hesap ve Diğer şeyleri alan bir satır içi yönteminiz olabilir, böylece sıfır ek yükü olan her iki yolunuz da olur.

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.