Örnekleri birkaç katmandan geçirmek kötü bir uygulama mıdır?


60

Program tasarımımda, çoğu zaman nesne sınıflarını birkaç sınıftan geçirmem gereken noktaya gelirim. Örneğin, bir ses dosyasını yükleyen ve sonra onu bir oynatıcıya ileten bir denetleyiciye sahipsem ve oynatıcı onu playerRunnable'a geçirir, bu da başka bir yere geçirir vs. bunun nasıl önleneceğini biliyorum. Yoksa bunu yapmak iyi mi?

EDIT: Belki oyuncu örneği en iyisi değil çünkü dosyayı daha sonra yükleyebilirim, fakat diğer durumlarda çalışmadı.

Yanıtlar:


54

Diğerlerinin de belirttiği gibi, bu mutlaka kötü bir uygulama değildir, ancak katmanların kaygılarını ayırmadığına ve katmanlara katmana özgü örnekleri aktarmadığınıza dikkat etmelisiniz. Örneğin:

  • Veritabanı nesneleri asla daha yüksek katmanlara geçirilmemelidir. .NET'in DataAdapter sınıfını, bir DB erişim sınıfını kullanan ve DAL'deki DataAdapter'ı kullanmak yerine, bir DTO veya veri seti oluşturmak yerine UI katmanına geçiren ve bunu geçen programları gördüm . DB erişimi, DAL'nin etki alanıdır.
  • UI nesneleri elbette UI katmanıyla sınırlı olmalıdır. Yine, her ikisinin de bir dizi / DTO yerine BL katmanına iletilen kullanıcı verileriyle doldurulmuş ListBox'lerle ve (hiyerarşik verileri alan bir DAL sınıfı alan (benim favorim) DB, hiyerarşik bir veri yapısı döndürmek yerine, bir TreeView nesnesini yeni oluşturup doldurdu ve bir forma dinamik olarak eklenecek şekilde UI'ya geri gönderdi.

Ancak, gönderdiğiniz örnekler DTO'lar veya varlıkların kendisi ise, muhtemelen tamamdır.


1
Bu şok edici gelebilir, ancak .NET'in karanlık günlerinde, genel olarak önerilen pratikti ve muhtemelen çoğu diğer yığınların yaptıklarından daha iyiydi.
Wyatt Barnett

1
Katılmıyorum. Microsoft'un, Winforms istemcisinin de DB'ye eriştiği tek katmanlı uygulamaların uygulanmasını onayladığı ve DataAdapter'ın görünmez bir kontrol olarak doğrudan forma eklendiği doğru, ancak bu OP'in N-katmanlı kurulumundan farklı, özel bir mimari. . Fakat çok katmanlı bir mimaride ve bu, .NET'ten önce bile VB6 / DNA için doğruydu, DB nesneleri DB katmanında kaldı.
Avner Shahar-Kashtan

Açıklığa kavuşturmak için: Kullanıcılara doğrudan (örnekte, liste kutularında) UI’ya "Veri Katmanı" ndan eriştiğini gördünüz mü? Üretim kodunda böyle bir ihlalle karşı karşıya olduğumu sanmıyorum .. vay ..
Simon Whitehead

7
@SimonWhitehead Tam olarak. Bir ListBox ve bir dizi arasındaki ayrım konusunda belirsiz olan kişiler ve ListBoxes'ı DTO olarak kullandılar. Bu, başkaları için sezgisel olmayan kaç tane görünmez varsayımı yaptığımı anlamama yardımcı olan bir andı.
Avner Shahar-Kashtan

1
@SimonWhitehead - Evet, kesinlikle, VB6 ve VB.NET Framework 1.1 ve 2.0 programlarında ve bu canavarları koruma görevinde bulunduğumu gördüm. Çok çirkinleşiyor, çok çabuk.
jfrankcarr

15

Henüz kimsenin değiştirilemez nesneler hakkında konuşmadığı ilginç . Değişmez bir nesneyi tüm çeşitli katmanlardan geçirmenin, her katman için çok sayıda kısa ömürlü nesne yaratmaktan çok , aslında iyi bir şey olduğunu savunuyorum .

Eric Lippert'in blogunda değişmezliğe dair bazı tartışmalar var

Öte yandan, değişken nesnelerin katmanlar arasında geçirilmesinin kötü bir tasarım olduğunu iddia ediyorum . Esasen, çevresindeki katmanların kodunuzu kıracak şekilde değiştirmeyeceği sözüyle bir katman oluşturuyorsunuz.


13

Nesne örneklerinin etrafından geçirilmesi, yapılması normal bir şeydir. Durumu koruma ihtiyacını azaltır (örn. Örnek değişkenler) ve kodu yürütme bağlamından ayırır.

Karşılaşabileceğiniz sorunlardan biri, bu zincirin altındaki bir yöntemin değişen parametre gereksinimlerine karşılık olarak, çağırma zinciri boyunca birden fazla yöntemin imzalarını değiştirmeniz gerektiğinde yeniden yapılanmadır. Ancak, yeniden yapılanmaya yardımcı olan modern yazılım geliştirme araçlarının kullanımıyla hafifletilebilir.


6
Karşılaştığınız başka bir sorunun değişmezliği içerdiğini söyleyebilirim. Bir geliştiricinin DTO'yu değiştirdiği, üzerinde belirli bir sınıfın o nesneye atıfta bulunan tek kişi olmadığı gerçeğini düşünmeden önce üzerinde çalıştığım bir projede çok şaşırtıcı bir hatayı hatırlayabilirim .
Phil

8

Belki küçük olabilir, ancak bu referansı, potansiyel olarak sarkan bir referans veya bellek sızıntısına neden olabilecek katmanlardan birinde bir yere atama riski vardır.


Amacınız doğru, ancak OP'nin terminolojisinden ("nesne nesnelerini geçmek") ya değerleri geçtiğini (işaretçiler değil) ya da çöp toplanan bir ortamdan (Java, C #, Python, Go,) bahsettiğini hissediyorum. ..).
Mohammad Dehghan

7

Nesneleri basitçe kodunuzun uzak bir bölgesinde olması gerektiği için dolaştırıyorsanız , isteğe bağlı olarak uygun bir IoC kabı ile birlikte kontrol ve bağımlılık enjeksiyon tasarım desenlerinin ters çevrilmesi, nesnenin etrafındaki nesnelerin taşınmasına ilişkin sorunları güzel bir şekilde çözebilir. Orta ölçekli bir projede kullandım ve bir daha kullanmadan büyük miktarda sunucu kodu yazmayı bir daha asla düşünmemeliydim.


Kulağa ilginç geliyor, zaten yapıcı enjeksiyon kullanıyorum ve üst seviye bileşenlerimin düşük seviye bileşenleri kontrol ettiğini düşünüyorum. Bir IOC kabını, örnekleri taşımaktan kaçınmak için nasıl kullanırsınız?
Puckl

Sanırım cevabı burada buldum: martinfowler.com/articles/injection.html
Puckl

1
Yan not, eğer Java'da çalışıyorsanız, Guice gerçekten çok hoş ve bağlamalarınızı istekler gibi şeylerle ilişkilendirebilir, böylece üst düzey bileşen kapsamı yaratır ve örnekleri o sırada doğru sınıflara bağlar.
Dave,

4

Verileri bir demet katmandan geçirmek kötü bir şey değildir, gerçekten katmanlı bir sistemin katmanlı yapıyı ihlal etmeden çalışabilmesinin tek yolu budur. Buradaki işaretler, hedefinize ulaşmak için verilerinizi aynı katmandaki birkaç nesneye ilettiğiniz zamandır.


3

Hızlı Yanıt: Orada yanlış bir şey nesnelerin örneklerini geçerken. Ayrıca belirtildiği gibi, bu referans potansiyel olarak sarkan bir referans veya bellek sızıntısına neden olan tüm katmanlara atlamayı atlamaktır.

Projelerimizde bu uygulamayı DTO'ları (Veri aktarım nesnesi) katmanlar arasında geçirmek için kullanıyoruz ve bu çok faydalı bir uygulama. Ayrıca, dto nesnelerimizi, özet bilgiler gibi, bir kez daha karmaşık hale getirmek için yeniden kullanıyoruz.


3

Öncelikle bir web UI dev'iyim ama sezgisel rahatsızlığınız, örnek geçişinden daha az ve bu kontrol cihazıyla biraz yordam uyguladığınız gerçeğinden daha fazla olabilir gibi geliyor. Kontrol cihazınız bütün bu detayları terletmeli mi? Sesin çalınması için neden birden fazla nesnenin adına atıfta bulunuyor?

OOP tasarımında, neyin yeşil olduğunu ve neyin değişime maruz kalma ihtimalini daha fazla düşündüğünü düşünüyorum. Eşyaları değiştirmeye konu olan şey, daha büyük nesne kutularınıza koymak isteyeceğiniz şeydir, böylece oyuncular değiştiğinde veya yeni seçenekler eklenirken bile tutarlı arayüzleri koruyabilirsiniz. Veya kendinizi ses nesnelerini veya bileşenlerini toptan olarak değiştirmek istediğinizde bulabilirsiniz.

Bu durumda, denetleyicinizin bir ses dosyası oynatmanın bir gerekliliği olduğunu ve ardından oynatmanın elde edilmesinin tutarlı / her zaman yeşil bir yolunun olduğunu tespit etmesi gerekir. Diğer taraftan, müzik çalar eşyaları teknoloji ve platformlar değiştikçe veya yeni seçenekler eklendikçe kolayca değişebilir. Bu ayrıntıların tümü, daha büyük bir bileşik nesne olan IMO'nun bir arayüzünün altına oturmalıdır ve sesin nasıl çalındığının ayrıntıları değiştiğinde denetleyicinizi yeniden yazmak zorunda kalmamalısınız. Daha sonra, dosya konumu gibi ayrıntılara sahip olan bir nesneyi daha büyük nesneye aktardığınızda, etrafta takas yapan her şey, birinin kendisiyle aptalca bir şey yapma ihtimalinin daha az olduğu uygun bir bağlamın iç kısmında yapılır.

Dolayısıyla bu durumda, bunun etrafından fırlatılmasının, nesnenin sizi rahatsız edebileceğini düşünmüyorum. Bu, Kaptan Picard'ın çözgü çekirdeğini açmak için motor odasına koşuyor, koordinatları çizmek için köprüye geri koşuyor ve ardından kalkanları açtıktan sonra "punch-it" düğmesine basıyor, sadece "Al" Warp 9'da X gezegenine geldik. Öyle yapın. " ve mürettebatının detayları çözmesine izin vermek. Çünkü bu şekilde ele aldığında, her geminin düzenini ve her şeyin nasıl çalıştığını bilmeden filodaki herhangi bir gemiyi kaptan edebilir. Ve sonuçta bu, çekildiği en büyük OOP tasarımı olan IMO'dur.


2

Uygulamanız bu tür bir şeye duyarlıysa, gecikme problemleriniz olsa da, ortaya çıkan oldukça yaygın bir tasarımdır.


2

Diliniz varsa ya da iş parçacığı yerel depolama varsa, bu sorun dinamik olarak kapsamlı değişkenlerle çözülebilir. Bu mekanizmalar bazı özel değişkenleri bir aktivasyon zinciri veya kontrol ipliği ile ilişkilendirmemize izin verir, böylece bu değerleri sadece başka bir koda iletilebilmeleri için onlarla ilgisi olmayan koda geçirmemize gerek kalmaz Bu onlara ihtiyacı var.


2

Diğer cevapların da belirttiği gibi, bu doğal olarak zayıf bir tasarım değildir. Yuvalanmış sınıflar ve onları yuvalayanlar arasında sıkı bağlantı oluşturabilir, ancak başvuruları yuvalamak tasarıma bir değer sağlarsa bağlantıyı gevşetmek geçerli bir seçenek olmayabilir.

Olası bir çözüm, denetleyici sınıfındaki iç içe referansları "düzleştirmek" dir.

Parametreyi iç içe geçmiş nesnelerden birkaç kez geçirmek yerine, denetleyici sınıfında iç içe geçmiş nesnelerin tümüne başvuruda bulunabilirsiniz.

Bunun tam olarak nasıl uygulandığı (veya geçerli bir çözüm olsa bile) sistemin şu anki tasarımına bağlıdır:

  • Çok fazla karmaşıklaşmadan denetleyicideki yuvalanmış nesnelerin bir tür haritasını tutabiliyor musunuz?
  • Parametreyi uygun iç içe geçmiş nesneye geçirdiğinizde, iç içe geçmiş nesne derhal parametreyi tanıyabilir mi veya iç içe geçmiş nesnelerden geçirilirken ortaya çıkan ek işlevler var mıydı?
  • vb.

Bu bir GXT istemcisi için MVC tasarım modelinde karşılaştığım bir konudur. GUI bileşenlerimiz, birkaç katman için yuvalanmış GUI bileşenleri içeriyordu. Model verileri güncellendiğinde, uygun bileşen (ler) e ulaşana kadar birkaç katmandan geçirmeyi bıraktık. GUI bileşenleri arasında istenmeyen bir bağlanma yarattı, çünkü yeni bir GUI bileşen sınıfının model verilerini kabul etmesini istiyorsak, model verilerini, yeni sınıfı içeren tüm GUI bileşenlerinde güncellemek için yöntemler oluşturmak zorunda kaldık.

Bunu düzeltmek için, View sınıfında iç içe geçmiş tüm GUI bileşenlerine bir referanslar haritası oluşturduk, böylece model verileri her güncellendiğinde, View güncellenmiş model verilerini doğrudan ihtiyaç duyduğu GUI bileşenlerine gönderebilir, öykünün sonuna . Bu iyi çalıştı çünkü her bir GUI bileşeninin yalnızca tek örnekleri vardı. Bazı GUI bileşenlerinin birden fazla örneği varsa, hangi kopyanın güncellenmesi gerektiğini tanımlamayı zorlaştırıyorsa, o kadar iyi çalışmadığını görebiliyordum.


0

Tarif ettiğiniz şey, bir Sorumluluk Zinciri tasarım deseni olarak adlandırılır. Apple bu kalıbı olay işleme sistemi için değerinde kullanır.

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.